Remote car Tank drive

Alright, just made a 3 wheeled 2 wheel drive car. My searches didn’t bring up any satisfactory joystick code, so I headed in and tried my own. Maps the y axis so that it starts counting back up below center but leaves a buffer in the middle. Maps the x axis and subtracts that speed from the proper wheel. Some code and observations. I first got all the code running and virtual writing the wheel speeds to the app. I noticed that the app would get way behind with the numbers. I had decided before I started I would run this one on local server because our internet has bad latency. Once I got it running on local server it was better but it still was slow. When I commented out the virtual writes BAMB!! works fine. I would love to hear criticisms on the code.

Setup
-three wheeled car from Banggood or Ebay includes 4- AA battery pack and L298N motor driver (also included an Uno and shield)
-Instead of the Uno I am using the trusty NodeMcu
-virtual Joystick

Additions for future
-Battery voltage off the analog pin
-Ordered a IR bar to try line following

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
 

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
// Use your own WiFi settings
char auth[] = "==";
char ssid[] = "==";
char pass[] = "==";
 
bool reverseFlag = false;
bool drivingFlag = false;
bool leftTurnFlag = false;
bool rightTurnFlag = false;

#define RightSpeedPin   D5
#define RightMotorDir   D6  
#define RightMotorDir1  D7  
#define LeftSpeedPin    D8  
#define LeftMotorDir    D1
#define LeftMotorDir1   D0

int leftMotorSpeed = 0;
int rightMotorSpeed = 0;

void setup()
{
  // initial settings for motors off and direction forward
   pinMode(RightMotorDir1, OUTPUT);
   pinMode(RightMotorDir, OUTPUT);
   pinMode(LeftMotorDir1, OUTPUT);
   pinMode(LeftMotorDir, OUTPUT);
   pinMode(RightSpeedPin, OUTPUT);
   pinMode(LeftSpeedPin,  OUTPUT);
   
   digitalWrite(RightMotorDir1,LOW);
   digitalWrite(RightMotorDir,LOW);
   digitalWrite(LeftMotorDir1, LOW);
   digitalWrite(LeftMotorDir,LOW);
   Serial.begin(9600);
   
  Blynk.begin(auth, ssid, pass, IPAddress(192,168,0,97), 8080);
    Blynk.connect();
    ArduinoOTA.setHostname("car");
    ArduinoOTA.begin();

   Serial.println("setup");
 }
 
 
void loop()
{
  Blynk.run();
  ArduinoOTA.handle(); 
}
 
 
BLYNK_WRITE(V1)
{
  int x = param[0].asInt();
  int y = param[1].asInt();
  int y1;
  int factor;

//maps forward, stop and backward (speed)

  if((y > 123)&&(y < 131))
  {
    drivingFlag = false;
    //Blynk.virtualWrite(V6,"stop1");
    Serial.println("stop");
  }
  else if(y < 129)                         
  {
    y1 = map(y,0,125,1030,500);                     
    reverseFlag = true;
    drivingFlag = true;
    //Blynk.virtualWrite(V6,"reverse");
    Serial.println("reverse");
  }
  else{
    y1 = map(y,131,255,500,1030);
    reverseFlag = false;
    drivingFlag = true;
    //Blynk.virtualWrite(V6,"forward");
    Serial.println("forward");
  }

//maps left and right speed

  if(x < 110)
  {
    factor = map(x,0,110,400,20);
    leftTurnFlag = false;
    rightTurnFlag = true;
  }
  else if (x > 144){
    factor = map(x,144,255,20,400);
    leftTurnFlag = true;
    rightTurnFlag = false;
  }
  else
  {
   factor = 128;
   leftTurnFlag = false;
   rightTurnFlag = false;
  }

//driving functions
  if (drivingFlag == true)
  {
///////////reverse
    if (reverseFlag == true)
    {
      ////////reverse left turn
      if (leftTurnFlag == true)
      {
        rightTurnFlag = false;
        rightMotorSpeed = y1;
        leftMotorSpeed = (y1 - factor);
        if (leftMotorSpeed < 500){
          leftMotorSpeed = 0;
        }
        driveBackward();
        //Blynk.virtualWrite(V4, "ReverseLeft");
        //Blynk.virtualWrite(V3, leftMotorSpeed);
        //Blynk.virtualWrite(V2, rightMotorSpeed);
        Serial.print("reverseleft");
      }
      else if (rightTurnFlag == true){
        leftTurnFlag = false;
        leftMotorSpeed = y1;
        rightMotorSpeed = (y1 - factor);
        if (rightMotorSpeed < 500){
          rightMotorSpeed = 0;
        }
        driveBackward();
        //Blynk.virtualWrite(V4, "ReverseRight");
        //Blynk.virtualWrite(V3, leftMotorSpeed);
        //Blynk.virtualWrite(V2, rightMotorSpeed);
        Serial.print("reverseRight");
      }
      else
      {
        leftMotorSpeed = y1;
        rightMotorSpeed = y1;
        driveBackward();
        //Blynk.virtualWrite(V4, "ReverseStraight");
        //Blynk.virtualWrite(V3, leftMotorSpeed);
        //Blynk.virtualWrite(V2, rightMotorSpeed);
        
      }
     }
    //////////////////////////////forward
    if (reverseFlag == false)
    {
      if (leftTurnFlag == true)
      {
        rightTurnFlag = false;
        rightMotorSpeed = y1;
        leftMotorSpeed = (y1 - factor);
        driveForward();
        //Blynk.virtualWrite(V4, "ForwarLeft");
        //Blynk.virtualWrite(V3, leftMotorSpeed);
        //Blynk.virtualWrite(V2, rightMotorSpeed);
      }
      else if (rightTurnFlag == true){
        leftTurnFlag = false;
        leftMotorSpeed = y1;
        rightMotorSpeed = (y1 - factor);
        driveForward();
        //Blynk.virtualWrite(V4, "ForwardRight");
        //Blynk.virtualWrite(V3, leftMotorSpeed);
        //Blynk.virtualWrite(V2, rightMotorSpeed);
      }
      else
      {
        leftMotorSpeed = y1;
        rightMotorSpeed = y1;
        driveForward();
        //Blynk.virtualWrite(V4, "ForwardStraight");
        //Blynk.virtualWrite(V3, leftMotorSpeed);
        //Blynk.virtualWrite(V2, rightMotorSpeed);
      }
    }
  }
  else
  {
    driveStop();
    //Blynk.virtualWrite(V6, "stopped");
    //Blynk.virtualWrite(V4, "Stopped");
    leftMotorSpeed = 500;
    rightMotorSpeed = 500;
    //Blynk.virtualWrite(V3, leftMotorSpeed);
    //Blynk.virtualWrite(V2, rightMotorSpeed);
  }
}

void driveForward()
{
   analogWrite(RightSpeedPin, rightMotorSpeed);
   analogWrite(LeftSpeedPin,  leftMotorSpeed);
   digitalWrite(RightMotorDir, LOW);
   digitalWrite(RightMotorDir1, HIGH);
   digitalWrite(LeftMotorDir, LOW);
   digitalWrite(LeftMotorDir1, HIGH);
   Serial.print("now driving forward ");
   Serial.print(rightMotorSpeed);
   Serial.println(leftMotorSpeed);
}

void driveBackward()
{
   analogWrite(RightSpeedPin, rightMotorSpeed);
   analogWrite(LeftSpeedPin,  leftMotorSpeed);
   digitalWrite(RightMotorDir1,LOW);
   digitalWrite(RightMotorDir,HIGH);
   digitalWrite(LeftMotorDir1, LOW);
   digitalWrite(LeftMotorDir,HIGH);
   Serial.print("now driving backward");
   Serial.print(rightMotorSpeed);
   Serial.println(leftMotorSpeed);
}

void driveStop()
{
   digitalWrite(RightMotorDir1,LOW);
   digitalWrite(RightMotorDir,LOW);
   digitalWrite(LeftMotorDir1, LOW);
   digitalWrite(LeftMotorDir,LOW);
   Serial.print("now stoppppped ");
   Serial.print(rightMotorSpeed);
   Serial.println(leftMotorSpeed);
}
4 Likes

Hi @daveblynk,
Please, upload video and pictures!!!

Thanks for sharing!!!

1 Like

Will do… in the meantime I tried out some 10k resistors between my 6v battery and A0. It doesn’t read anything, but if I jump the 3v pin to A0 it works. You electrical engineers out there, is the current to low with the resistors?

You need to create a voltage divider using two resistors. Read this for more info…

Note that a bare ESP8266 chip can accept a maximum of 1v on the analogue pin, but the NodeMCU/Wemos D1Mini dev boards have an onboard voltage divider that changes the range from 0-1v to 0-3.3v, so the calculations used in the above link are correct for the board you’re using.

Pete.

Should work for 6v readings as well.

Sorry I didn’t state clearly. I did the resistor voltage divider so my volt meter shows 1.5v. You think it is the internal step up that is causing Blynk display to show a 0 for 1.5 and 3.3 when I jump the 1.1 (software calculation) pin? I was wondering if my 10k resistor step down was not letting enough current through…

Edit. Bang head I’m thinking I did my calculations the wrong way. I’ll check when I get home… thanks for now

If you’re using a 6v battery then you’ll probably want to use a voltage divider that gives an output of 3.3v for an input of around 6.5v (it depends what your multimeter says when it’s fully charged).

When you read the digital pin you’ll get a value between 0 and 1023 (as the NodeMCU has a 10 bit ADC), so you’ll want to map 0,1023 to 0,6.5

You can then do a bit of tweaking of the 6.5 value in the map command to give you a value that matches what your multimeter says when the setup is in its idle mode.

Pete.

1 Like

@Gunner You mention built in resistors and such hindering analog voltage measurements seems the case with my board. It holds 1024 till just before it dies. Is my only option going to some breakout board? If so, you have a suggestion? Isn’t critical on this project but I am working on a battery operated weather station and don’t want to ruin my 16500’s.

Photo attached

1 Like

1024 is the maximum reading you’ll get, so maybe you have your resistor values wrong and the analogue port is always seeing a voltage that’s higher than the maximum it can differentiate?

Pete.

Ok so it all of a sudden hit me… it needs a resistor, A0 to ground so it is not floating. Now it works…

1 Like

I’d assumed that you already had a resistor between the analogue pin and ground, as one half of your voltage divider.

https://esp8266-projects.org/2016/08/esp8266-internal-adc-2-the-easy-way-example/

Pete.

Ya brain fart. Had a separate group wire resisted…

Toggle between auto and self drive.

1- IR light bar guidance
2- Nodemcu handling WiFi and motor directions/speeds
3- UNO handling light bar and sending a number to Nodemcu for guidance.

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <Wire.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
// Use your own WiFi settings
char auth[] = "**";
char ssid[] = "**";
char pass[] = "**";
 
bool reverseFlag = false;
bool drivingFlag = false;
bool leftTurnFlag = false;
bool rightTurnFlag = false;
bool autoMode;
bool autoDrive;

#define RightSpeedPin   D5
#define RightMotorDir   D6  
#define RightMotorDir1  D7  
#define LeftSpeedPin    D8  
#define LeftMotorDir    D1
#define LeftMotorDir1   D0

int leftMotorSpeed = 0;
int rightMotorSpeed = 0;
int trigPossition = 0;            //A number received from Uno light bar

int autoSpeed;                    //Stores autonomous speed

const int analogInPin = A0;       //Analog pin to test battery voltage

BlynkTimer timer;

void setup()
{
  // initial settings for motors off and direction forward
   pinMode(RightMotorDir1, OUTPUT);
   pinMode(RightMotorDir, OUTPUT);
   pinMode(LeftMotorDir1, OUTPUT);
   pinMode(LeftMotorDir, OUTPUT);
   pinMode(RightSpeedPin, OUTPUT);
   pinMode(LeftSpeedPin,  OUTPUT);
   pinMode(analogInPin, INPUT);
   
   digitalWrite(RightMotorDir1,LOW);
   digitalWrite(RightMotorDir,LOW);
   digitalWrite(LeftMotorDir1, LOW);
   digitalWrite(LeftMotorDir,LOW);
   
   Serial.begin(9600);
   Wire.begin(D2, D3);                     /* join i2c bus with SDA=D2 and SCL=D3 of NodeMCU */
   
   timer.setInterval(1000L, voltageRead);
   delay (25);
   timer.setInterval(51L, autoModefunction);
   
   Blynk.begin(auth, ssid, pass, IPAddress(192,168,0,97), 8080);
   Blynk.connect();
    
   ArduinoOTA.setHostname("car_v2_4");
   ArduinoOTA.begin();
   
   Blynk.syncVirtual(V2);                  //Syncs autonomous mode at startup
 }
 
 
void loop()
{
  timer.run();
  Blynk.run();
  ArduinoOTA.handle(); 
}

BLYNK_WRITE(V3)                           //Reads autonomous motor speed slider
{
  int autoSpeed1 = param.asInt();
  
  if ((autoMode == true) && (autoSpeed1 > 131))   //check autonomous mode and speed so doesn't start driving till you increase speed
  {
    autoSpeed = autoSpeed1;                       //changes to a global variable
    autoDrive = true;
  }
  else
  {
    autoDrive = false;
    driveStop();
    leftMotorSpeed = 500;
    rightMotorSpeed = 500;
  }
}

BLYNK_WRITE(V2)                 //Slider for Autonomous mode resets if not fully slid.
{
 if (param.asInt()){
  autoMode = true;
  Blynk.syncVirtual(V3);        //re-syncs the speed
 }else{
  autoMode = false;
  Blynk.virtualWrite(V2,0);     //re sets the slider if not completely slide
  Blynk.syncVirtual(V3);        // re-syncs to fully stop the car
 }
}
 
BLYNK_WRITE(V1)                 //joystick for manual mode
{
  if (autoMode == false)
  {         //doesn't operate on auto mode
    int x = param[0].asInt();     //get values from joystick
    int y = param[1].asInt();
    int y1;                       //creates a new int to map the joystick into
    int factor;                   //initualizes a factor for turning

/////maps forward, stop and backward

      if((y > 79)&&(y < 131))       //tests joystick value (tended to back up a bit before going forward)
      {                             //complete stop function
        drivingFlag = false;
      }
      else if(y < 80)               //calls reverse function                   
      {
        y1 = map(y,0,125,1030,500);    //maps reverse (bottom part of joystick)
        reverseFlag = true;
        drivingFlag = true;
      }
      else
      {                            //else forward function
        y1 = map(y,131,255,500,1030);  //maps forward (top part of joystick)
        reverseFlag = false;
        drivingFlag = true;
      }

/////maps left and right

    if(x < 110)                       //tests left right axis leaves a center gap
    {                                 //creates a right turn
      factor = map(x,0,110,400,20);   //maps left side of joystick to create a factor to subtract off of one wheel speed
      leftTurnFlag = false;
      rightTurnFlag = true;
    }
    else if (x > 144)                  //tests left right axis
    {                                  //creates left turn
      factor = map(x,144,255,20,400);  //maps right side of joystick to create a factor to subtract off of one wheel speed
      leftTurnFlag = true;
      rightTurnFlag = false;
    }
    else                               //else drives straight
    {
      factor = 128;                    //mid point of joystick
      leftTurnFlag = false;
      rightTurnFlag = false;
    }

/////driving functions

    if (drivingFlag == true)
    {
      
/////reverse

      if (reverseFlag == true)
      {
        
/////reverse leftturn

        if (leftTurnFlag == true)     //exicutes the left turn
        {
          rightTurnFlag = false;
          rightMotorSpeed = y1;
          leftMotorSpeed = (y1 - factor);  //removes the amount of factor from the left motor
          if (leftMotorSpeed < 500)        //if this falls below 500 motor will just buzz
          {
            leftMotorSpeed = 0;
          }
          driveBackward();                 //enitiates the function
        }
        else if (rightTurnFlag == true)
        {                                 //just opposite of left turn
          leftTurnFlag = false;
          leftMotorSpeed = y1;
          rightMotorSpeed = (y1 - factor); 
          if (rightMotorSpeed < 500)
          {
            rightMotorSpeed = 0;
          }
          driveBackward();
        }
        else
        {
          leftMotorSpeed = y1;
          rightMotorSpeed = y1;
          driveBackward();       
        }
     }
     
/////forward                                the opposite of the reverse function

     if (reverseFlag == false)
     {
      if (leftTurnFlag == true)
      {
        rightTurnFlag = false;
        rightMotorSpeed = y1;
        leftMotorSpeed = (y1 - factor);
        driveForward();
      }
      else if (rightTurnFlag == true)
      {
        leftTurnFlag = false;
        leftMotorSpeed = y1;
        rightMotorSpeed = (y1 - factor);
        driveForward();
      }
      else
      {
        leftMotorSpeed = y1;
        rightMotorSpeed = y1;
        driveForward();
      }
     }
    }
    
  else
  {
    driveStop();
    leftMotorSpeed = 500;
    rightMotorSpeed = 500;
  }
 }
}

void driveForward()              //takes the motor speeds and applies the direction rotation
{
   analogWrite(RightSpeedPin, rightMotorSpeed);
   analogWrite(LeftSpeedPin,  leftMotorSpeed);
   digitalWrite(RightMotorDir, LOW);
   digitalWrite(RightMotorDir1, HIGH);
   digitalWrite(LeftMotorDir, LOW);
   digitalWrite(LeftMotorDir1, HIGH);
}

void driveBackward()             //takes the motor speeds and applies the direction of rotation
{
   analogWrite(RightSpeedPin, rightMotorSpeed);
   analogWrite(LeftSpeedPin,  leftMotorSpeed);
   digitalWrite(RightMotorDir1,LOW);
   digitalWrite(RightMotorDir,HIGH);
   digitalWrite(LeftMotorDir1, LOW);
   digitalWrite(LeftMotorDir,HIGH);
}

void driveStop()                 //stops the motors
{
   digitalWrite(RightMotorDir1,LOW);
   digitalWrite(RightMotorDir,LOW);
   digitalWrite(LeftMotorDir1, LOW);
   digitalWrite(LeftMotorDir,LOW);
}

void voltageRead()               //read battery voltage function
{
  float sensorValue = analogRead(analogInPin);

  float outputValue = (sensorValue*6.2/1024)*6;

  if(outputValue <= 4.9){
    Blynk.setProperty(V10, "color", "#D3435C");     //turns text red if voltage low
  }else{
    Blynk.setProperty(V10, "color", "#23C48E");     //turns text green
  }
  Blynk.virtualWrite(V10,"\xF0\x9F\x94\x8B"," ", outputValue, "V");      //adds a battery icon in the text
}

void autoModefunction()          //autonoumus function called every 51ms
{                                //most of the code copied from above
  if(autoDrive == true)
  {
    Wire.requestFrom(8, 13);         /* request & read data of size 13 from slave */
    trigPossition = Wire.read();     //creates var with a number 1-10 from the UNO light bar
    
    int xx = trigPossition;          //had started with the read in a differnt function so changes to a local variable
    int y = autoSpeed;               //changes autoSpeed to the y variable used above so I could copy and paste code
    int y1;
    int factor;
      
    if(y < 131)                     //stared my speed slider at 128 so can use same parms as above
    {
      drivingFlag = false;
    }
    else
    { 
      y1 = map(y,130,256,400,1024);   //maps the slider to the motors numbers 500-1024
      reverseFlag = false;
      drivingFlag = true;
    }
  
/////maps left and right to a switch for preset factor amounts

  switch (xx)                    //factor removes speed from on wheel to turn
   {
    case 9:
      factor = 390;
      leftTurnFlag = false;
      rightTurnFlag = true;
      break;
  
    case 8:
      factor = 210;
      leftTurnFlag = false;
      rightTurnFlag = true;
      break;
      
    case 7:
      factor = 185;
      leftTurnFlag = false;
      break;

    case 6:
      factor = 135;
      leftTurnFlag = false;
      rightTurnFlag = true;
      break;

    case 5:
      factor = 128;
      leftTurnFlag = false;
      rightTurnFlag = false;
      break;

    case 4:
      factor = 135;
      leftTurnFlag = true;
      rightTurnFlag = false;
      break;

    case 3:
      factor = 185;
      leftTurnFlag = true;
      rightTurnFlag = false;
      break;
 
    case 2:
      factor = 210;
      leftTurnFlag = true;
      rightTurnFlag = false;
      break;
 
    case 1:
      factor = 390;
      leftTurnFlag = true;
      rightTurnFlag = false;
      break;

    case 10:                            //if looses line car stops
      leftTurnFlag = false;
      rightTurnFlag = false;
      drivingFlag = false;
      driveStop();
      break;
    }
  
/////driving functions

  if (drivingFlag == true)
  {
    
/////reverse

    if (reverseFlag == true)
    {
      
/////reverse leftturn
                                                //see notes above
      if (leftTurnFlag == true)
      {
        rightTurnFlag = false;
        rightMotorSpeed = (y1 - factor);
        leftMotorSpeed = y1;
        
        if (leftMotorSpeed < 500)
        {
          leftMotorSpeed = 0;
        }
        
        driveBackward();
      }
      else if (rightTurnFlag == true)
      {
        leftTurnFlag = false;
        leftMotorSpeed = (y1 - factor);
        rightMotorSpeed = y1;
        
        if (rightMotorSpeed < 500)
        {
          rightMotorSpeed = 0;
        }
        driveBackward();
      }
      else
      {
        leftMotorSpeed = y1;
        rightMotorSpeed = y1;
        driveBackward();        
      }
    }
    
/////forward
    
    if (reverseFlag == false)
    {
      if (leftTurnFlag == true)
      {
        rightTurnFlag = false;
        rightMotorSpeed = y1;
        leftMotorSpeed = (y1 - factor);
        driveForward();
      }
      else if (rightTurnFlag == true)
      {
        leftTurnFlag = false;
        leftMotorSpeed = y1;
        rightMotorSpeed = (y1 - factor);
        driveForward();
      }
      else
      {
        leftMotorSpeed = y1;
        rightMotorSpeed = y1;
        driveForward();
      }
     }
   }
 }
}