From the article Controlling a Servo 2-Wheel Robot in the ESP8266+RoboServo and the DC electric motor in VisionRobo Car: Drive Motor, we have taken the 2nd built-in robot car from the Raspberry Pi to the ESP8266 to operate via WiFi using the guidelines from the ESP-01s+Relay article. Let’s rewrite Arduino’s C/C++ with the WebServer class from the ESP8266 article to MicroPython. Thus, by the end of this article, the robot can be operated in the first example by connecting a phone or communication device. Go to 192.168.4.1 and order it to go forward, backward, turn left, turn right, or stop.
Equipment
The list of equipment for the experiment as shown in Figure 1 is as follows.
- NodeMCU and extension board
- dc driver MX1508
- Rechargeable power supply
- Robot car powered by 2 electric motors
- DC electric motor for left wheel
- DC electric motor for right wheel
- Wheel set for electric motor, left wheel
- Wheel set for electric motor, right wheel
- Body mounting bracket
Code
From the list of equipment when assembling a robot as shown in Figure 1 or read the details from the article ESP8266+RoboServo, the next step is to design the software architecture.
โคThe structure of the software to be developed consists of two main parts.
- The functions under the Arduino framework are setup(), loop() and additional functions.
- Actor class that is a robot to support the properties of the existing Actuator, that is, the wheels on both sides make it able to respond to the environment in 5 cases:
- Stop moving, what is activated at this stage is a low transmission to stop the supply of pressure, causing the movement to follow the force generated by the last command. Then stop by sending high to all 4 poles of both motors to stop and then relax the pause by sending low before the end of the operation. This was caused by a simulation experiment like stepping on the clutch then stepping on the brake. After that, release the brake and hold the clutch to wait for the accelerator pedal (Readers can try other designs.).
- Running forward
- Moving backward
- Turning left by turning the motor in the opposite direction
- Turning right by turning opposite to turning left
The code for RobotAgent is as follows.
#define MOTOR_L1 D4
#define MOTOR_L2 D5
#define MOTOR_R1 D6
#define MOTOR_R2 D7
class RobotAgent {
private:
public:
RobotAgent() {
// Actuator
pinMode(MOTOR_L1, OUTPUT);
pinMode(MOTOR_L2, OUTPUT);
pinMode(MOTOR_R1, OUTPUT);
pinMode(MOTOR_R2, OUTPUT);
}
~RobotAgent() {
}
void stop() {
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, LOW );
delay(5);
digitalWrite( MOTOR_L1, HIGH );
digitalWrite( MOTOR_L2, HIGH );
digitalWrite( MOTOR_R1, HIGH );
digitalWrite( MOTOR_R2, HIGH );
delay(100);
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, LOW );
}
void forward() {
digitalWrite( MOTOR_L1, HIGH );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, HIGH );
}
void left() {
digitalWrite( MOTOR_L1, HIGH );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, HIGH );
digitalWrite( MOTOR_R2, LOW );
}
void right() {
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, HIGH );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, HIGH );
}
void backward() {
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, HIGH );
digitalWrite( MOTOR_R1, HIGH );
digitalWrite( MOTOR_R2, LOW );
}
};
Control movement
Example program for testing the functionality of the robot (Readers need to adjust the rotation to match the robot on their car too) to go forward, backward, turn left and turn right for 3 seconds each, can be written as follows:
#define MOTOR_L1 D4
#define MOTOR_L2 D5
#define MOTOR_R1 D6
#define MOTOR_R2 D7
class RobotAgent {
private:
public:
RobotAgent() {
// Actuator
pinMode(MOTOR_L1, OUTPUT);
pinMode(MOTOR_L2, OUTPUT);
pinMode(MOTOR_R1, OUTPUT);
pinMode(MOTOR_R2, OUTPUT);
}
~RobotAgent() {
}
void stop() {
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, LOW );
delay(5);
digitalWrite( MOTOR_L1, HIGH );
digitalWrite( MOTOR_L2, HIGH );
digitalWrite( MOTOR_R1, HIGH );
digitalWrite( MOTOR_R2, HIGH );
delay(100);
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, LOW );
}
void forward() {
digitalWrite( MOTOR_L1, HIGH );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, HIGH );
}
void left() {
digitalWrite( MOTOR_L1, HIGH );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, HIGH );
digitalWrite( MOTOR_R2, LOW );
}
void right() {
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, HIGH );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, HIGH );
}
void backward() {
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, HIGH );
digitalWrite( MOTOR_R1, HIGH );
digitalWrite( MOTOR_R2, LOW );
}
};
RobotAgent car;
void setup() {
}
void loop() {
car.stop();
car.forward();
delay(3000);
car.stop();
car.backward();
delay(3000);
car.stop();
car.left();
delay(3000);
car.stop();
car.right();
delay(3000);
}
Control movement through the browser
After successfully testing the robot to move. The next step is to add the code part of the web order as shown in Figure 2.
The added program code is to open port 80 and define itself as an AP named “JarutEx” with a password of “123456789”.
- / To show the visual motion state of the button in Figure 2, the active state is a button with a green background color and a red background for the other buttons to distinguish them.
- /stop call a robotStop to stop moving.
- /forward call a robotForward to move forward.
- /backward for backwards with a call robotBackward
- /left to rotate to the left in response from the robotLeft command
- /right to rotate right by calling robotRight
Code
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#define AP_NAME "JarutEx"
#define AP_PASSWD "123456789"
#define MOTOR_L1 D4
#define MOTOR_L2 D5
#define MOTOR_R1 D6
#define MOTOR_R2 D7
class RobotAgent {
private:
public:
RobotAgent() {
// Actuator
pinMode(MOTOR_L1, OUTPUT);
pinMode(MOTOR_L2, OUTPUT);
pinMode(MOTOR_R1, OUTPUT);
pinMode(MOTOR_R2, OUTPUT);
}
~RobotAgent() {
}
void stop() {
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, LOW );
delay(5);
digitalWrite( MOTOR_L1, HIGH );
digitalWrite( MOTOR_L2, HIGH );
digitalWrite( MOTOR_R1, HIGH );
digitalWrite( MOTOR_R2, HIGH );
delay(100);
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, LOW );
}
void forward() {
digitalWrite( MOTOR_L1, HIGH );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, HIGH );
}
void left() {
digitalWrite( MOTOR_L1, HIGH );
digitalWrite( MOTOR_L2, LOW );
digitalWrite( MOTOR_R1, HIGH );
digitalWrite( MOTOR_R2, LOW );
}
void right() {
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, HIGH );
digitalWrite( MOTOR_R1, LOW );
digitalWrite( MOTOR_R2, HIGH );
}
void backward() {
digitalWrite( MOTOR_L1, LOW );
digitalWrite( MOTOR_L2, HIGH );
digitalWrite( MOTOR_R1, HIGH );
digitalWrite( MOTOR_R2, LOW );
}
};
RobotAgent car;
IPAddress myIP(192, 168, 4, 1);
IPAddress gwIP(192, 168, 4, 10);
IPAddress subnet(255, 255, 255, 0);
int motionState = 0;
ESP8266WebServer server(80);
void setup() {
motionState = 0;
car.stop();
if (WiFi.softAPConfig( myIP, gwIP, subnet )) {
if (WiFi.softAP(AP_NAME, AP_PASSWD, 8, false, 5)) {
} else {
while (true);
}
} else {
while (true) {
}
}
server.on("/", htmlPage);
server.on("/stop", robotStop);
server.on("/forward", robotForward);
server.on("/backward", robotBackward);
server.on("/left", robotLeft);
server.on("/right", robotRight);
server.begin();
}
void robotStop() {
motionState = 0;
car.stop();
htmlPage();
}
void robotForward() {
motionState = 1;
car.forward();
htmlPage();
}
void robotBackward() {
motionState = 2;
car.backward();
htmlPage();
}
void robotLeft(){
motionState = 3;
car.left();
htmlPage();
}
void robotRight(){
motionState = 4;
car.right();
htmlPage();
}
void htmlPage() {
String html;
html.reserve(2048); // prevent ram fragmentation
html = F(
"<!DOCTYPE HTML>"
"<html><head>"
"<meta name='viewport' content='width=device-width, initial-scale=1'>"
"<style>"
".button { border: none; color: white; padding: 20px; text-align: center; text-decoration: none;"
" display: inline-block; font-size: 14"
" px; margin: 4px 2px; cursor: pointer; border-radius: 4%;"
" width: 100%; height: 100%;"
"}"
".button1 { background-color: #3ABC40; }"
".button2 { background-color: #BC4040; }"
"</style></head><body><table>"
);
if (motionState == 0) {
html += F(
"<tr>"
"<td></td>"
"<td><a href='/forward'><button class='button button2'>Forward</button></a></td>"
"<td></td>"
"</tr>"
"<tr>"
"<td><a href='/left'><button class='button button2'>Turn Left</button></a></td>"
"<td><a href='/stop'><button class='button button1'>Stop</button></a></td>"
"<td><a href='/right'><button class='button button2'>Turn Right</button></a></td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><a href='/backward'><button class='button button2'>Backward</button></a></td>"
"<td></td>"
"</tr>"
);
}
else if (motionState == 1) {
html += F(
"<tr>"
"<td></td>"
"<td><a href='/forward'><button class='button button1'>Forward</button></a></td>"
"<td></td>"
"</tr>"
"<tr>"
"<td><a href='/left'><button class='button button2'>Turn Left</button></a></td>"
"<td><a href='/stop'><button class='button button2'>Stop</button></a></td>"
"<td><a href='/right'><button class='button button2'>Turn Right</button></a></td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><a href='/backward'><button class='button button2'>Backward</button></a></td>"
"<td></td>"
"</tr>"
);
}
else if (motionState == 2) {
html += F(
"<tr>"
"<td></td>"
"<td><a href='/forward'><button class='button button2'>Forward</button></a></td>"
"<td></td>"
"</tr>"
"<tr>"
"<td><a href='/left'><button class='button button2'>Turn Left</button></a></td>"
"<td><a href='/stop'><button class='button button2'>Stop</button></a></td>"
"<td><a href='/right'><button class='button button2'>Turn Right</button></a></td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><a href='/backward'><button class='button button1'>Backward</button></a></td>"
"<td></td>"
"</tr>"
);
}
else if (motionState == 3) {
html += F(
"<tr>"
"<td></td>"
"<td><a href='/forward'><button class='button button2'>Forward</button></a></td>"
"<td></td>"
"</tr>"
"<tr>"
"<td><a href='/left'><button class='button button1'>Turn Left</button></a></td>"
"<td><a href='/stop'><button class='button button2'>Stop</button></a></td>"
"<td><a href='/right'><button class='button button2'>Turn Right</button></a></td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><a href='/backward'><button class='button button2'>Backward</button></a></td>"
"<td></td>"
"</tr>"
);
}
else if (motionState == 4) {
html += F(
"<tr>"
"<td></td>"
"<td><a href='/forward'><button class='button button2'>Forward</button></a></td>"
"<td></td>"
"</tr>"
"<tr>"
"<td><a href='/left'><button class='button button2'>Turn Left</button></a></td>"
"<td><a href='/stop'><button class='button button2'>Stop</button></a></td>"
"<td><a href='/right'><button class='button button1'>Turn Right</button></a></td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><a href='/backward'><button class='button button2'>Backward</button></a></td>"
"<td></td>"
"</tr>"
);
}
html += F("</table></body></html>\r\n");
server.send(200, "text/html", html);
}
void loop() {
server.handleClient();
}
Conclusion
It will be found that the use of language is part of preferences or convenience in development. But the language for displaying or interacting with users through the web is still HTML5. As a result, if readers understand and have web writing skills with HTML, CSS3 and JavaScript, they will be able to create beautiful web results. However, with microcontrollers ESP8266 or ESP32 as a microprocessor, the limited amount of memory may make it impossible to perform as a service provider in a real system. Therefore, readers who have undergone various programming experiments will find the facts and gain further experience that any kind of action can be done and what kind of use is a hindrance? Or maybe it won’t work. Finally, have fun with programming.
(C) 2022, By Jarut Burasathid and Danai Jedsadathitikul
Updated 2022-02-18Thanks to Assoc. Prof. Dr. Thiang Meadthaisong and Assoc. Prof. Siwaporn Meadthaisong for the equipment