WiDC - Wi-Fi Controlled FPV Robot (with Arduino, ESP8266 and DC Motors) - LEKULE

Breaking

22 May 2017

WiDC - Wi-Fi Controlled FPV Robot (with Arduino, ESP8266 and DC Motors)

What about using a remote controlled robot for exploring your surroundings, reaching inaccessible places, spying, take pictures, making films or broadcasting a video? This tutorial will show you how to make an inexpensible for all those purposes!

This instructable is an improvement of my previous tutorial (https://www.instructables.com/id/Wi-fi-Controlled-FPV-Rover-Robot-with-Arduino-ESP8/), in which I present a faster home-made robot. It shows how to design a remotely controlled two-wheeled robot over a Wi-Fi network, using two DC motors, an Arduino Uno and an ESP8266 Wi-fi module.
The robot can be controlled from an ordinary internet browser, using a HTML designed interface. An Android smartphone might de used to broadcast video and audio from the robot to the control interface.

As in my previous tutorial, I show how to desing and build a low-cost frame for a robotic project, using just ordinary tools (no 3D printer, laser cutter or CNC router needed).
One might notice that the components used here might not be optimised for its purpose. A NodeMCU might be used instead of the Arduino + ESP8266 combination, for instance. A Rapsberry Pi with a camera would replace the smartphone and controll the motors as well. It's even possible to use an Android smartphone as the "brain" for your robot. That's true...

This Instructable was derived from another project I'm part of: Joy Robot (https://hackaday.io/project/12873-rob-da-alegria-joy-robot or https://www.hackster.io/igorF2/robo-da-alegria-joy-robot-85e178), and the same hardware was used here for simplicity. An Arduino Uno was choosed because it's very accessible and easy to use for everyone, and we wanted to design a simple shield for it. In our original project, whe also control some servos and LED matrices, and interfaces with the ESP. The smartphone is actually replaced by a tablet, that runs other applications as well. You might find other instructables related to that robot in the links bellow:

This guide might be adapted and have its shape or control interface changed to fit your needs.

Step 1: Tools

Tools
The following tools were used for the construction of this prototype:
Tools:
    • Hand saw (for cutting acrylic sheets)
    • Screwdriver (for placing bolts and nuts)
    • Ruler (for measuring the model dimensions)
    • Utility knife (for cutting the structure and making holes)
    • Drilling machine (used for drilling the acrylic for installation of the screws)
    • Sandpaper (to smooth rought edges)
    • Soldering iron (for soldering motor terminals)
    • A computer (for compiling and uploading Arduino code)
    Those tools were used for the production of robot's mechanical structure, assembly of the robot and connection of electronic components. Some of the tools aren't needed if you choose to buy a structure instead of building your own.
    You might even use other pieces of equipment (a 3D printer or laser cutter, for instance), depending on the tools available at your makerspace.

    Step 2: Mechanical Structure and Materials

    Mechanical Structure and Materials
    The following materials were used on the mechanical structure:

    Mechanical materials:
    • 2mm acrylic sheet
    • DC gear motor with wheel (x2)
    • 30mm caster wheel (x1)
    • M2 x 10mm screws (x5)
    • M2 x 1,5mm nuts (x5)
    • M3 x 10mm screws (x9)
    • M3 x 10mm screws (x9)
    • M3 x 40 mm screws (x4)
    • M3 x 1,5mm nuts (x12)
    • 5/32" x 1" screws (x4)
    • 5/32" nuts (x12)
    • Universal phone holder
    • Steel angle "L" shaped bracket (30 x 30 mm) (x4)

    Step 3: Electronic Components

    Electronic Components
    The following electronic components where used in this robot:

    Arduino Uno
    ESP8266
    Protoshield (for a more compact version) or an ordinary breadboard
    • 1 kohm resistor (x2)
    • 10 kohm resistor (x1)
    • DC gear motor with wheel (x2)
    • H-bridge module
    • Some jumper wires
    • 5V USB power bank
    You can find all the components online easily on your favourite e-commerce store.
    As it was highlighted in the introduction of this Instructable, the choice of the components was derived from other project I designed, and may be optimised using a different microcontroller, for instance.

    Step 4: Designing the Structure

    Designing the Structure
    First l had to design the mechanical structure of my robot. You might also buy the complete structure online if you doesn't want to build your own custom structure (there is a lot of robotic kits available online). In this case, you might jump to Step 6.

    For this instructable, a low-cost acrylic frame was designed for the attachment of the motors and other components. The structure presented in this tutorial was 3D designed using Fusion 360 CAD software and constructed with ordinary tools (no 3D printer, laser cutter or CNC router needed). You might use the same principles described bellow to design a custom structure that fit your needs.
    First I had to make a 3D model of the main components, in order to decide the arrangement of the structure. Some of them were downloaded from GrabCAD Comunity Library (https://grabcad.com/library):
    The electronic components were arranged in a plane, so that I could figure out the footprint need for my robot. After that, I had to choose the position for the motors and wheels to create a stable structure.
    Based on that positions, the main structure was designed, which is composed of three plates (a base plate, and two side plates) which are connected using some brackets.

  • base.svg base.svg

  • Step 5: Building and Assembling the Structure

    The construction of the structure of the base is divided in the following steps:

    1. Cut the acrylic base according to the dimensions in the 2D drawing:
    This might be done with a laser cutter (if you have access to one) or, as in my case, with ordinary tools.
    First you'll need to transfer the dimensions of your model to the acrylic sheet. Print your 2D drawing using an ordinary printer on an adhesive paper, then cut the paper in suitable dimensions and apply that mask on the surface of the acrylic.

    With an utility knife and with the help of a ruler or a scale, cut the acrylic in straight lines. You won't need to cut all the way thru the sheet, just score it to create some tracks where the piece will be later cutted. Place the acrylic on a flat surface, hold it in place with some clamps and apply some pressure until the sheet breaks into two. Repeat this process untill all the cuts are done. After that, you might use a sandpaper to smooth rough edges. You might also use a hand saw to cut the acrylic.

    2. Drill the holes in the positions in shown in the 2D drawing:
    Drill the holes in the positions in shown in the 2D drawing (indicated in the mask) with a drilling machine. Acrylic is relativelly easy to drill. So if you don't dispose of a drilling machine, you can drill the holes manually with a sharp tool, like an utility knife. You might also use it to enlarge small holes to fit screw sizes. Remove the mask and your base will be ready.

    3. Mount the components with screws and nuts according to the 3D drawing:
    Mount the components with screws and nuts according to the video, and your structure will be ready to go. M3 screws are used for the instalation of the brackets and DC motors. M2 screws were used for instalation of electronic components, while the 5/32" ones are used for the instalation of the front wheel and the smartphone clip.

    Now take a break and start to assemble the circuit in the following step...

    Step 6: Assembling the Electronics

    Assembling the Electronics
    The circuit proposed here uses a Arduino Uno as the main controller, which interfaces with an ESP8266 for Wi-Fi communication.The Arduino is controls the DC motors using a H-brige driving circuit, which is able to control up to two motors, rotating them independently in both directions.
    A power bank was used to powered the electronics, connected directly to Arduino's USB port. This is an easy way to power your Arduino: easy to charge, can be easily replaced , and provides a safe 5V voltage.

    Connect all the componets according to the schematic. You'll need some jumper wires to connect ESP8266 module, which is not protoboard-friendly, h-bridge and DC motors. You might use a protoshield (for a more compact circuit), an ordinary breadboard, or design your own Arduino shield.
    You'll notice that the ESP8266-1 is not breadboard friendly. It will be connected using some jumper wires, but it won't be attached to the structure.

    Plug an USB cable to Arduino's board and proceed to the next step.

     Plug an USB cable to Arduino's board and proceed to the next step.

  • wiDC.fzz wiDC.fzz
  • wiDC_bb.pdf wiDC_bb.pdf

  • Step 7: Arduino Code

    Install the latest Arduino IDE. No library was needed for communication with ESP-8266 module or control of the motors.
    Please check the baudrate of you ESP8266 and set it properly in the code.
    Download Arduino code (WiDC.ino) and replace the XXXXX by your wifi router SSID and YYYYY by router password. Connect the Arduino board to your computer USB port and upload the code.
    Plug an USB cable to Arduino's board and proceed to the next step.
    //include libraries
    #include 
    
    SoftwareSerial esp8266(3, 2); //RX pin = 3, TX pin = 2
    
    //definition of variables
    #define DEBUG true //show messages between ESP8266 and Arduino in serial port
    int state = 5; //define initial state of the robot (5 = stand-by)
    //define motor pins
    const int motor1Pin1 = 5;
    const int motor1Pin2 = 6;
    const int motor2Pin1 = 9;
    const int motor2Pin2 = 10;
    //define motor speed
    int motorSpeed = 150; //motor speed (PWM)
    
    //*****
    //SETUP
    //*****
    void setup()
    {
      //set pin modes
      pinMode(motor1Pin1, OUTPUT);
      pinMode(motor1Pin2, OUTPUT);
      pinMode(motor2Pin1, OUTPUT);
      pinMode(motor2Pin2, OUTPUT);
      
      //start communication
      Serial.begin(9600);
      esp8266.begin(9600);
      
      sendData("AT+RST\r\n", 2000, DEBUG); //reset module
      sendData("AT+CWMODE=1\r\n", 1000, DEBUG); //set station mode
      sendData("AT+CWJAP=\"XXXXX\",\"YYYYY\"\r\n", 2000, DEBUG); //connect wi-fi network (replace XXXXX by your Wi-Fi router SSID and YYYYY by its password
      delay(5000); //wait for connection
    
      sendData("AT+CIFSR\r\n", 1000, DEBUG); //show IP address
      sendData("AT+CIPMUX=1\r\n", 1000, DEBUG); //allow multiple connections
      sendData("AT+CIPSERVER=1,80\r\n", 1000, DEBUG); // start web server on port 80
    }
    
    //*********
    //MAIN LOOP
    //*********
    void loop()
    {
      
      if (esp8266.available())  //verify incoming data
      {
        if (esp8266.find("+IPD,")) //if there is a message
        {
          String msg;
          esp8266.find("?"); //look for the message
          msg = esp8266.readStringUntil(' '); //read whole message
          String command = msg.substring(0, 3); //first 3 characters = command
          Serial.println(command);
               
          //move forward
          if(command == "cm1") {
              state = 1;
          }
    
          //move backward
          if(command == "cm2") {
              state = 2;
          }
    
          //turn right
          if(command == "cm3") {
              state = 3;
           }
           
           //turn left
           if(command == "cm4") {
              state = 4;
           }
    
           //do nothing       
           if(command == "cm5") {
              state = 5;
           }
    
        }
      }
      
      //STATE 1: move forward
      if (state == 1) {
        analogWrite(motor1Pin1, motorSpeed);
        digitalWrite(motor1Pin2, LOW);
        analogWrite(motor2Pin1, motorSpeed);
        digitalWrite(motor2Pin2, LOW);
      }
      //STATE 2: move backward
      if (state == 2) {
        digitalWrite(motor1Pin1, LOW);
        analogWrite(motor1Pin2, motorSpeed);
        digitalWrite(motor2Pin1, LOW);
        analogWrite(motor2Pin2, motorSpeed);  }
      //STATE 3: move right
      if (state == 3) {
        analogWrite(motor1Pin1, motorSpeed);
        digitalWrite(motor1Pin2, LOW);
        digitalWrite(motor2Pin1, LOW);
        analogWrite(motor2Pin2, motorSpeed);
      }
      //STATE 4: move left
      if (state == 4) {
        digitalWrite(motor1Pin1, LOW);
        analogWrite(motor1Pin2, motorSpeed);
        analogWrite(motor2Pin1, motorSpeed);
        digitalWrite(motor2Pin2, LOW);
      }
      //STATE 5: do nothing
      if (state == 5) {
        digitalWrite(motor1Pin1, LOW);
        digitalWrite(motor1Pin2, LOW);
        digitalWrite(motor2Pin1, LOW);
        digitalWrite(motor2Pin2, LOW);
      }
      
    }
    
    //*******************
    //Auxiliary functions
    //*******************
    String sendData(String command, const int timeout, boolean debug)
    {
      String response = "";
      esp8266.print(command);
      long int time = millis();
      while ( (time + timeout) > millis())
      {
        while (esp8266.available())
        {
          char c = esp8266.read();
          response += c;
        }
      }
      if (debug)
      {
        Serial.print(response);
      }
      return response;
    }
    
    

    Code explained:
    The code uses one serial port for the communication between the Arduino and the ESP8266, and another for communication between the Arduino and a computer. Once the Arduino Uno only have one serial port, SoftwareSeial library was used to create a secondary port, using digital pins 2 and 3.
    //include libraries
    #include 
    
    SoftwareSerial esp8266(3, 2); //RX pin = 3, TX pin = 2
    
    During the setup, both serial communications have to be started, and their baudrate defined (one between the Arduino and your Serial monitor, and other to match ESP8266 speed). Notice that my ESP8266 was set to 9600 kbps. By default most of these modules come at 115200kbps, but SoftwareSerial library can't work at that speed and you'll have to change its baudrate. For me 9600 kbps worked fine.
    In this project I didn't use a specific library for the comunication with the Wi-Fi module. Instead, only the following ordinary AT commands (a set of instructions define on ESP8266 default firmware) were used:
    • AT+RST: reset ESP8266 module
    • AT+CWMODE: set module to station mode or access point
    • AT+CWJAP: connect a Wi-Fi network given by its SSID and password
    • AT+CIPMUX: set module for multiple connections or single connection
    • AT+CIPSERVER: starts web server on a given port //start communication
      Serial.begin(9600);
      esp8266.begin(9600);
      
      sendData("AT+RST\r\n", 2000, DEBUG); //reset module
      sendData("AT+CWMODE=1\r\n", 1000, DEBUG); //set station mode
      sendData("AT+CWJAP=\"XXXXX\",\"YYYYY\"\r\n", 2000, DEBUG); //connect wi-fi network (replace XXXXX by your Wi-Fi router SSID and YYYYY by its password
      delay(5000); //wait for connection
      sendData("AT+CIFSR\r\n", 1000, DEBUG); //show IP address
      sendData("AT+CIPMUX=1\r\n", 1000, DEBUG); //allow multiple connections
      sendData("AT+CIPSERVER=1,80\r\n", 1000, DEBUG); // start web server on port 80
    An auxiliar function (sendData) is used for sending data (from Arduino to ESP8266), reading and displaying the response on Serial Monitor.
    String sendData(String command, const int timeout, boolean debug)
    {
      String response = "";
      esp8266.print(command);
      long int time = millis();
      while ( (time + timeout) > millis())
      {
        while (esp8266.available())
        {
          char c = esp8266.read();
          response += c;
        }
      }
      if (debug)
      {
        Serial.print(response);
      }
      return response;
    }
    
    Using the code above makes the Arduino reset the module, join a network, wait for sometime for the connection, then show its IP address and start a webserver. After that, main loop will be started, and the microcontroller will wait for commands.
    void loop()
    {
      
      if (esp8266.available())  //verify incoming data
      {
        if (esp8266.find("+IPD,")) //if there is a message
        {
          String msg;
          esp8266.find("?"); //look for the message
          msg = esp8266.readStringUntil(' '); //read whole message
          String command = msg.substring(0, 3); //first 3 characters = command
          Serial.println(command);
               
          //move forward
          if(command == "cm1") {
              state = 1;
          }
    
          //move backward
          if(command == "cm2") {
              state = 2;
          }
    
          //turn right
          if(command == "cm3") {
              state = 3;
           }
           
           //turn left
           if(command == "cm4") {
              state = 4;
           }
    
           //do nothing       
           if(command == "cm5") {
              state = 5;
           }
    
        }
      }
    
    Five possible commands were defined (cm1 to cm5). Whenever the Arduino receives one of those commands, it enters in one of five possible states (moving forward, moving backward, moving right, moving left and stand by), and continue in that state until it receives a different command.
    Each state defines the signals for motors pins. I used digitalWrite(pin, LOW) when I wanted to set a pin to 0V and analogWrite(pin, motoSpeed) when I wanted to turn on a pin. Using analogWrite allowed me to change the speed of the motor, and make the robot move slower.
    //STATE 1: move forward
      if (state == 1) {
        analogWrite(motor1Pin1, motorSpeed);
        digitalWrite(motor1Pin2, LOW);
        analogWrite(motor2Pin1, motorSpeed);
        digitalWrite(motor2Pin2, LOW);
      }
      //STATE 2: move backward
      if (state == 2) {
        digitalWrite(motor1Pin1, LOW);
        analogWrite(motor1Pin2, motorSpeed);
        digitalWrite(motor2Pin1, LOW);
        analogWrite(motor2Pin2, motorSpeed);  }
      //STATE 3: move right
      if (state == 3) {
        analogWrite(motor1Pin1, motorSpeed);
        digitalWrite(motor1Pin2, LOW);
        digitalWrite(motor2Pin1, LOW);
        analogWrite(motor2Pin2, motorSpeed);
      }
      //STATE 4: move left
      if (state == 4) {
        digitalWrite(motor1Pin1, LOW);
        analogWrite(motor1Pin2, motorSpeed);
        analogWrite(motor2Pin1, motorSpeed);
        digitalWrite(motor2Pin2, LOW);
      }
      //STATE 5: do nothing
      if (state == 5) {
        digitalWrite(motor1Pin1, LOW);
        digitalWrite(motor1Pin2, LOW);
        digitalWrite(motor2Pin1, LOW);
        digitalWrite(motor2Pin2, LOW);
      }
    Notice that the motors work between 3 and 6V. Once a 5V power source is used, you might modulate motors' average voltage (using PWM) between 3 and 5V, thus changing its speed. It won't allow you to fine control robot's .speed

    Step 8: Web-based Control Interface

    Web-based Control Interface
    A html interface was designed for the control of the robot.

    Download interface.rar and extract all the files to a given folder. Then open it on Firefox. A textbox form is used in that interface to enter IP addresses of the ESP module and of the video/audio server (from Android IP Webcam app). There is a test but, which will make the robot spin until anoter command is received. Keyboard arrow keys are used for moving the robot forward or backward, and to rotate left or right.

    An Android smartphone was used to broadcast the video and audio from the robot to the control interface. You may find the app on Google Play store (https://play.google.com/store/apps/details?id=com.pas.webcam).

    Install it and move to next step.

    HTML code explained:

    The html interface has two division: one for audio and video (from Android IP Webcam server) and one for the commands.

    Audio and video division has a form with a textbox a buttom. This is used as an input for specifying Webcam server IP address and loading it. It comes with a standart IP address (192.168.0.5), but the use can enter a different IP. Video and audio are loaded in objects bellow the textbox.

    IP Webcam (IP):
    The other division has another form wit a text box, so that the user can inform it's ESP

    Press and hold keyboard arrow keys to move the robot

    JavaScript is used for dealing with the interface and sending data to the Arduino. Those scripts are coded in different files, and added on html header.
    
     
     
     Control interface
     
     
    
    Javascript explained:
    A function (readUrlAV) is used for for reading the IP address from the html form and loading them in "video" and "audio" objects.
    function readUrlAV (form) {
     TextVar = form.inputbox.value;
     VideoVar = "http://"+TextVar+":8080/video";
     AudioVar = "http://"+TextVar+":8080/audio.opus";
     document.getElementById("video").setAttribute('data', VideoVar);
     document.getElementById("audio").setAttribute('data', AudioVar);
    }
    
    The script reads the key board periodically, wainting for the user to press down any key. If the user press any arrow key (left = '37', up = '38', right = '39' or 'down' = 40), it sends a command ("cm1" to "cm4") for a given IP address. Notice that there is a latch function, which avoids the same command to be repeated over and over again. Data will be transferred only when the key is pressed.
    var latch = false;
    document.onkeydown = checkKeyDown;
    function checkKeyDown(e) {
        e = e || window.event;
        if (e.keyCode == '38') {
            // up arrow
     if (latch == false) {
      TextVar = myform2.inputbox.value;
      ArduinoVar = "http://" + TextVar + ":80";
      $.get( ArduinoVar, { "cm1": 1000 }) ;
      {Connection: close};
      latch = true;
     }
        }
        else if (e.keyCode == '40') {
            // down arrow
     if (latch == false) {
      TextVar = myform2.inputbox.value;
      ArduinoVar = "http://" + TextVar + ":80";
      $.get( ArduinoVar, { "cm2": 1000 }) ;
      {Connection: close};
      latch = true;
     }
        }
        else if (e.keyCode == '37') {
     // left arrow
     if (latch == false) {
      TextVar = myform2.inputbox.value;
      ArduinoVar = "http://" + TextVar + ":80";
      $.get( ArduinoVar, { "cm3": 1000 }) ;
      {Connection: close};
      latch = true;
     }
        }
        else if (e.keyCode == '39') {
            // right arrow
     if (latch == false) {
      TextVar = myform2.inputbox.value;
      ArduinoVar = "http://" + TextVar + ":80";
      $.get( ArduinoVar, { "cm4": 1000 }) ;
      {Connection: close};
      latch = true;
     }
        }
    }
    
    When any arrow key is released, doNothing function is executed, which sends command "cm5" (stop the motors), and resets the latch, allowing interface to send differente commands.
    document.onkeyup = checkKeyUp;
    function checkKeyUp(e) {
        e = e || window.event;
    
        if ((e.keyCode == '38')||(e.keyCode == '40')||(e.keyCode == '37')||(e.keyCode == '39')) {
     setTimeout(doNothing, 200);
        }
    }
    
    function doNothing(){
     TextVar = myform2.inputbox.value;
      ArduinoVar = "http://" + TextVar + ":80";
      $.get( ArduinoVar, { "cm5": 1000 }) ;
      {Connection: close};
      latch = false;
    }
    
    
    

    Step 9: Usage

    When the Arduino is restarted, it will try to connect your wi-fi network automatically. Use the Serial Monitor to check if the connection was successfull, and to obtain which IP was assigned to your ESP-8266 by your router. Open the html file in an internet browser (Firefox) and inform this IP address in the textbox.

    You might also user other means to find out which IP address you router assigned to your device. Disconnect the the Arduino Uno from your computer and connect it to the power bank. Wait for it to connect again. Launch IP Webcam app in the smartphone attached to the robot. Type the video/audio IP on your control interface and connect to the server and you'll be ready to go. You might need to reduce the resolution of the video in the app to reduce the delay between during the transmission. Click and hold the arrow buttons of your keyboar to rotate the robot or move it forward/backward and have fun exploring your environment.

    Notice that robot runs on open loop. This way, it's quite difficult to make it move straight forward. Small difference between the motors, aligment, etc. will cause cumulative deviations.

    The robot start moving when it receives a given command ("cm1" to "cm4"), and keep that state until a different command is received ("cm1" to "cm5"). Sometimes the ESP8266 loses some messages, and that might cause some trouble. If, for instance, a "cm5" command is lost, the robot will keed moving even after any arrow key was released. I'm still dealing with this problem. Feel free to change the way the commands are interpreted to avoid this kind of problem.

    No comments: