[EN] Arduino: Control robot’s movement via browser with esp8266 Part 2

Based on the previous article that uses a single esp8266 for controlling the Agent, the number of pins that the esp8266 microcontroller (as written in the MicroPython article about machine.Pin) is limited. Many of the pins are used at boot up causing unintended errors such as the wheel spinning when the system starts and stops when the system finishes booting, etc. Therefore, in this article, a microcontroller board LGT8F328P is added as shown in Figure 1, or the reader may change to other Arduino family microcontrollers, such as Arduino Nano or Arduino Uno, etc. by giving that LGT8F328P is part of the Actuator that acts as a movement in the environment. It can be commanded to go forward, backward, turn left, turn right and stop, reducing the workload of the esp8266 and making it more responsive to WiFi communication.

Figure 1 LGT8F328P is integrated into a robotic car system to control the movement

Equipment

The list of equipment for the experiment as shown in Figure 1 is as follows.

  1. NodeMCU and extension board
  2. LGT8F328P for being Actuator
  3. MX1508 module
  4. Recharable power supply
  5. Robot car powered by 2 electric motors
    1. DC electric motor for left wheel
    2. DC electric motor for right wheel
    3. Wheel set for electric motor, left wheel
    4. Wheel set for electric motor, right wheel
    5. Mannequin body mounting bracket

Code

The pin connection of LGT8F328P to MX1508 motor driver board is as follows.

  • D2 connected to left motor on pin 1
  • D3 connected to left motor on pin 2
  • D4 onnected to right motor on pin 1
  • D5 onnected to right motor on pin 2

In addition, RX/TX pins have been connected between the 2 microcontroller boards, esp8266 and LGT8F328P as follows:

  • pin Rx of esp8266 connected to Tx of LGT8F328P
  • pin Tx of esp8266 connected to Rx of LGT8F328P

For the convenience of communication with the esp8266 board, serial communication is used. The communication protocol is defined as follows.

  • 0 for stopping
  • 1 for moving forward
  • 2 for moving backward
  • 3 for turning left
  • 4 for turning right

The program code part of LGT8F328P is as follows.

#define MOTOR_L1  D2
#define MOTOR_L2  D3
#define MOTOR_R1  D4
#define MOTOR_R2  D5

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, LOW );
      digitalWrite( MOTOR_L2, HIGH );
      digitalWrite( MOTOR_R1, HIGH );
      digitalWrite( MOTOR_R2, LOW );
    }
    void left() {
      digitalWrite( MOTOR_L1, LOW );
      digitalWrite( MOTOR_L2, HIGH );
      digitalWrite( MOTOR_R1, LOW );
      digitalWrite( MOTOR_R2, HIGH );

    }
    void right() {

      digitalWrite( MOTOR_L1, HIGH );
      digitalWrite( MOTOR_L2, LOW );
      digitalWrite( MOTOR_R1, HIGH );
      digitalWrite( MOTOR_R2, LOW );
    }
    void backward() {

      digitalWrite( MOTOR_L1, HIGH );
      digitalWrite( MOTOR_L2, LOW );
      digitalWrite( MOTOR_R1, LOW );
      digitalWrite( MOTOR_R2, HIGH );
    }
};

RobotAgent car;
int cmd;

void setup() {
  Serial.begin(9600);
}

void loop() {
  if (Serial.available()) {
    cmd = (int)Serial.read() - '0';
    switch (cmd) {
      case 0:
        car.stop();
        break;
      case 1:
        car.forward();
        break;
      case 2:
        car.backward();
        break;
      case 3:
        car.left();
        break;
      case 4:
        car.right();
        break;
    }
  }
}

For esp8266, it has been improved from the previous example to only send commands to the serial communication system connected to the LGT8F328P board as follows.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

#define AP_NAME "JarutEx"
#define AP_PASSWD "123456789"

IPAddress myIP(192, 168, 4, 1);
IPAddress gwIP(192, 168, 4, 10);
IPAddress subnet(255, 255, 255, 0);

int motionState = 0;
String html;

ESP8266WebServer server(80);


void setup() {
  Serial.begin(9600);
  delay(1000);
  html.reserve(2048);               // prevent ram fragmentation
  motionState = 0;
  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;
  Serial.println(motionState);
  htmlPage();
}
void robotForward() {
  motionState = 1;
  Serial.println(motionState);
  htmlPage();
}
void robotBackward() {
  motionState = 2;
  Serial.println(motionState);
  htmlPage();
}
void robotLeft() {
  motionState = 3;
  Serial.println(motionState);
  htmlPage();
}
void robotRight() {
  motionState = 4;
  Serial.println(motionState);
  htmlPage();
}

void htmlPage() {
  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

From this article, you will find that we can resolve the issue where Board esp8266 does not have enough system control pins by using Board LGT8F328P as a command or read function for external devices and design a command through a custom set of instructions for sending and receiving via serial communication. Finally, have fun with programming.

(C) 2022, By Jarut Busarathid and Danai Jedsadathitikul
Updated 2022-02-24

Thanks to Assoc. Prof. Dr. Thiang Miadthaisong and Asst. Prof. Sivaphon Miadthaisong for supporting the experimental equipment.