Skip to main content

codeBox.js

Prototype of a simple mobile robot


The first thing I made was a preliminary sketch of the most important parts with an overview of how I would like to look at the robot, it's a simple design but I need the robot to be modular, to make improvements and updates later. The general idea is to use it in different projects, by now, the first challenge is to build the structure and the second challenge is to write code to control the robot through Bluetooth and an Android app.


To put the circuit PCB or just a protoboard over the top face of the robot I designed 4 sliders that rotate on their own axes. At the moment of designing the robot, I didn't know exactly the size of the PCB, so this simple mechanism will help me to adapt different sizes of circuits over the chassis of the robot. For more details, the interactive 3D view below allows you to isolate single or multiple components designed, so feel free to check the parts for a better understanding of how the robot works.

To develop the structure of the robot I used the CAD software  Fusion360, taking into account mechanical parts (wheels, bolts, MDF-3mm, etc) and electronic parts (motor driver, ESP32, lipo-battery, etc) that I already had

Interactive 3D view
NOTE: To isolate components in the interactive 3D view, left-click + shift to select a part or multiple parts, right-click to open menu options, click on the "isolate" option to show only selected components, on the keypad press ESC to return to normal view.

For a complete list of materials, you can check the CAD drawing, available as PDF : 

Circuit Diagram

To reduce the input voltage from 11.7V (lipo battery) to 5V, I'm using the MP1584 DC-DC adjustable step-down converter. The 5V is used to supply the ESP32-DEVKITV1 in the VIN Pin.

The motor driver TB6612FNG can control up to 2 DC motors at a constant current of 1.2A. The Pins AIN and BIN can control the rotation of the motors (CW or CCW). The speed of each motor is controlled via the PWM. The STBY can take the motors out of standby mode. The logic supply (VCC) recommended is 3.3V, and the motor supply (VM) is limited to a maximum of 15V.

Code explanation

To program the robot I used an ESP32-DEVKITV1 configured to use with the Arduino-IDE, some minor changes in the code compared with an Arduino board but a lot of information can be found in forums, web pages and tutorials. One web page I like to use and have a lot of information about ESP32 boards is : 


You can find information from the basics to complex projects in the link above.

Motor configuration

To move the motors in the same direction (forward/backward), try to manually swap the Pins AIN1 and ANI2 for one motor or BIN1 and BIN2 for the other motor. The PWM configuration is a little different than is made in an Arduino board; the ESP32-DEVKITV1 uses frequency, channel, and resolution values to control the PWM channel. To keep things simple I used standard values for an Arduino Uno board, making it possible to control the PWM in range values from 0-255.

Main.ino

const int pwm_left_motor = 13;     
const int pwm_right_motor = 33;
const int standby = 27;
const int motor_left_AIN1 = 14;
const int motor_left_AIN2 = 12;
const int motor_right_BIN1 = 26;
const int motor_right_BIN2 = 25;
const int pwm_frecuency = 980;           // PWM frecuency for arduino uno 
const int pwm_left_channel = 0;          // 0-15 available channels (left motor channel) 
const int pwm_right_channel = 1;         // 0-15 available channels (right motor channel)
const int resolution = 8;                // 8-bit resolution means control values from 0 to 255

int max_velocity;                        // the maximum linear velocity of the robot read as PWM
int max_pwm = 120;                       // max PWM output for motors
int ss_motor = 40;                       // speed increased in a single motor
          
To make code reusable I defined the function Setup_motors(). The code inside the function is the common Pins declaration used in the void setup() of the Arduino code structure.

SetupConfiguration.ino

void Setup_motors(){
  pinMode(pwm_left_motor,OUTPUT);                                 // declared GPIOS as output to write values
  pinMode(pwm_right_motor,OUTPUT);
  pinMode(standby,OUTPUT);
  pinMode(motor_left_AIN1,OUTPUT);
  pinMode(motor_left_AIN2,OUTPUT);
  pinMode(motor_right_BIN1,OUTPUT);
  pinMode(motor_right_BIN2,OUTPUT);
  
  digitalWrite(standby,HIGH);                                     // turn on motor driver
  ledcSetup(pwm_left_channel, pwm_frecuency, resolution);         // configure pwm for left motor  
  ledcSetup(pwm_right_channel, pwm_frecuency, resolution);        // configure pwm for right motor
  ledcAttachPin(pwm_left_motor, pwm_left_channel);                // attach the channel to the gpio used for pwm left motor 
  ledcAttachPin(pwm_right_motor, pwm_right_channel);              // attach the channel to the gpio used for pwm right motor 
}
          
The functions speed_right_motor(int), speed_left_motor(int), and speed_motors(int) read values in the range (-120,120), where 120 is the maximum PWM value sent to the motors; 


UserFunctions.ino

void speed_right_motor(int right){
  /*
  * Right motor output speed configuration
  */                         
  if(right >= 0)                                    
  {
    digitalWrite(motor_right_BIN1,LOW);                  
    digitalWrite(motor_right_BIN2,HIGH);
    ledcWrite(pwm_right_channel, right);
  }
  
  if(right < 0)                                     
  {                            
    digitalWrite(motor_right_BIN1,HIGH);                  
    digitalWrite(motor_right_BIN2,LOW);
    ledcWrite(pwm_right_channel, abs(right));
  }
}


void speed_left_motor(int left){
  /*
  * Left motor output speed configuration
  */ 
  if(left >= 0)                                    
  {
    digitalWrite(motor_left_AIN1,LOW);                  
    digitalWrite(motor_left_AIN2,HIGH);
    ledcWrite(pwm_left_channel, left);
  }
  
  if(left < 0)                                     
  {                            
    digitalWrite(motor_left_AIN1,HIGH);                  
    digitalWrite(motor_left_AIN2,LOW);
    ledcWrite(pwm_left_channel, abs(left));
  }
}


void speed_motors(int robot_speed){
  /*
  * Right and Left motor output speed configuration.
  * One output speed on both motors
  */                      

  if(robot_speed >= 0)                                    
  {
    digitalWrite(motor_left_AIN1,LOW);                  
    digitalWrite(motor_left_AIN2,HIGH);
    digitalWrite(motor_right_BIN1,LOW);                  
    digitalWrite(motor_right_BIN2,HIGH);
    ledcWrite(pwm_right_channel, robot_speed);
    ledcWrite(pwm_left_channel, robot_speed);
  }
  
  if(robot_speed < 0)                                     
  {                            
    digitalWrite(motor_left_AIN1,HIGH);                  
    digitalWrite(motor_left_AIN2,LOW);
    digitalWrite(motor_right_BIN1,HIGH);                  
    digitalWrite(motor_right_BIN2,LOW);
    ledcWrite(pwm_right_channel, abs(robot_speed));
    ledcWrite(pwm_left_channel, abs(robot_speed));
  }
}
          

Bluetooth Configuration

To control the robot I'm using a Bluetooth Android App, available at the Playstore as:


The Android App is easy to use and has a section with documentation, examples, and value configuration. For this specific case, I will use the default App configuration.

To use the embedded Bluetooth into the ESP32 we need to add the library BluetoothSerial.h into the project and create a new serial communication (SerialBT).

The app sends 3 values as a unique string:
  • Angle of the analog stick [0-359]
  • Strength of the analog stick [0-100] 
  • Button pressed [1,2,3 or 4]
Also, I defined a char variable move_selector to save 5 different letters that will help me to select between the different movements of the robot (forward('f'), backward ('b'), left ('l'),  right ('r') and stop ('s'))

Main.ino

#include "BluetoothSerial.h"
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
  #error Bluetooth is not enabled!
#endif

BluetoothSerial SerialBT;                 // bluetooth serial communication name: SerialBT 

String angle = "0";                       // joystick angle after button preseed (0-359 degress)
String strength = "0";                    // joystick strength after button pressed (0-100)
String button = "0";                      // pressed buttons (1-4)
String value = "0";                       // total string send from bluetooth app to esp32
char move_selector;                       // to select between different robot movements
          
To make code reusable I defined the function bluetooth_app(). The code inside the function extracts substrings from the original value sent from the Bluetooth App. Later this function will run inside the void loop() of the Arduino code structure because we will constantly send data to control the robot

UserFunctions.ino

void bluetooth_app(){
  /*
  * Received one string from the bluettoth app and splited into
  * usefull values 
  */
  if (SerialBT.available()>0) {
    value = SerialBT.readStringUntil('#');
    if (value.length()==7){
      angle = value.substring(0,3);
      strength = value.substring(3,6);
      button = value.substring(6,8);
      SerialBT.flush();
      value = "";
    }
      
  }
}	
          

Void Setup

In the void setup() I configured a baud rate communication of 115200, to show data in the monitor of the IDE, the previously explained Setup_motors() function and we give a name to the Bluetooth communication ("ROBOT"), this name it will be shown when we will try to pair the phone with the robot.

Main.ino

void setup() {
  Serial.begin(115200);                   // baud rate communication 
  Setup_motors();                         // motor driver/ESP32 gpios configuration
  SerialBT.begin("ROBOT");                // bluetooth device name
}
          

Void Loop

The image below shows the different sections of the Android App. I divided the joystick into 6 sections to control the different robot movements



Main.ino
void loop() {
  bluetooth_app();
  
  if (strength.toInt()==0){move_selector='s';}                            //stop the robot
  else if (button.toInt()==4 && strength.toInt()>0){move_selector = 'l';} //rotate to the left
  else if (button.toInt()==3 && strength.toInt()>0){move_selector = 'f';} //move forward
  else if (button.toInt()==2 && strength.toInt()>0){move_selector = 'r';} //rotate to the right
  else if (button.toInt()==1 && strength.toInt()>0){move_selector = 'b';} //move backward
  //maximun linear velocity of the motors in PWM
  max_velocity = map(strength.toInt(),0,100,0,max_pwm);
  
  switch(move_selector){ 
    /*****************************************************/
    case 's':
    //stop the robot
    speed_motors(0); 
    break;
    /*****************************************************/
      
    /*****************************************************/
    case 'l':
    //rotate the robot to the left
    speed_right_motor(max_velocity);
    speed_left_motor(-max_velocity); 
    break;
    /*****************************************************/
    
    /*****************************************************/
    case 'f':
    //move the robot forward and slow rotation
    if (angle.toInt()>=75 && angle.toInt() <=105){
      //forward
      speed_motors(max_velocity);  
    }
    else if (angle.toInt()>105 && angle.toInt() <=180){
      //slow rotation to the left
      speed_right_motor(max_velocity + ss_motor);
      speed_left_motor(max_velocity);
    }
    else if (angle.toInt()>0 && angle.toInt() <75){
      //slow rotation to the right
      speed_right_motor(max_velocity);
      speed_left_motor(max_velocity + ss_motor);
    }
    break;
    /*****************************************************/
    
    /*****************************************************/
    case 'r':
    //rotate the robot to the right
    speed_right_motor(-max_velocity);
    speed_left_motor(max_velocity);
    break;
    /*****************************************************/
    
    /*****************************************************/
    case 'b':
    //move the robot backward and slow rotation
    if (angle.toInt()>=255 && angle.toInt() <=285){
      //backward
      speed_motors(-max_velocity);  
    }
    else if (angle.toInt()>180 && angle.toInt() <255){
      //slow rotation to the left
      speed_right_motor(-max_velocity - ss_motor);
      speed_left_motor(-max_velocity);
    }
    else if (angle.toInt()>285 && angle.toInt() <=359){
      //slow rotation to the right
      speed_right_motor(-max_velocity);
      speed_left_motor(-max_velocity - ss_motor);
    }
    break;
    /*****************************************************/
  }
}
          
The code, files, and other details can be found and downloaded from my GitHub page. Feel free to use the Comments section in this blog or visit my social media to learn about my job.

Comments

Popular Post

ESP32 SPI Master - Slave

The SPI (Serial Peripheral Interface) acts as a synchronous data bus used to send data between microcontrollers and small peripherals that use separate lines for data and a clock that keeps both sides in perfect sync. In SPI only one side generates the clock signal ( SCK for serial clock). The side that generates the clock is called the Controller or the Master, and the other side is called the Peripheral or the Slave. There is always only one Controller , but there can be multiple Peripherals . Data sharing from the  Controller to the  Peripheral is sent on a data line called COPI (Controller Output Peripheral Input). If the Peripheral  needs to send a response back to the Controller data is sent on the line called CIPO (Controller Input Peripheral Output). The last line is called CS (Chip select) or SS (Slave select). This tells the peripheral that it should wake up and receive/send data and is also used when multip...

ESP32-CAM: Stream Video with an Asynchronous Web Server

  Introduction This project was developed under two main characteristics: efficiency and scalability. The original example available on the Arduino IDE, "ESP32/Camera/CameraWebServer.ino" is a great example for beginners and runs well. However, a problem arises when I tried to escalate the project. I decided to rebuild the entire application to adjust to my necessities, rewrite the code, delete unnecessary parts, create a directory, and keep single files (.cpp, .h, .html, .css, .js, etc.) I used  PlarformIO with VSCode to create the project structure, for directory management. I adapted the server code to run with an asynchronous web server, this gives me the ability to handle multiple connections from different clients simultaneously without blocking the main program flow. The frontend was developed using responsive design to adapt to different screens, additional for mobile devices was implemented a full-screen mod...

Analizing IMDB data (movie review) for sentiment analysis

This is the first neural networks project I made with preprocessed data from IMDB to identify sentiments (positive or negative) from a dataset of 50.000 (25.000 for training and 25.000 for testing). I tried to detail every step and decision I made while creating the model. In the end, the neural network model was able to classify with an accuracy of 81.1% or misclassify 11.9% of the data (around 3000 movie reviews). This is a high error margin considering that an acceptable error must be between 3% and 5%, but the model, in general, helped me and gave me clues to develop a new version. At the same time, I learned a slight introduction to Natural Language Processing, a topic new to me. 1. The dataset : IMDB (Internet Movie Database) ¶ References: ¶ Maas, A., Daly, R., Pham, P., Huang, D., Ng, A., & Potts, C. (2011). Learning Word Vectors for Sentiment Analysis. IMDB movie review sentiment classification dataset ...