Nikko Jeep car - controlled with esp-now

I had changed the robotic car AD174 from Aceboot, China with ESP-now, last year.

As I had owned after my grandchildren Nikko Jeep wrangeled with 40 MHz controller, but withou bateries and a function, I have started to change it similar to my previous change of AD174 car.

At first I have removed the 40 MHz receiver and I have tested both motors , the one for driving and second for turning. And these both motor have worked on 7V (2x LiOn 18650).

So I have created the receiver with ESP8266 NodeMcu type with CP2102 USB adapter

and for the sender – controller ESP32 Wemos D1 R32 CH340 , so very similar as I had used them on AD174 car.

So the sketches for these ESP-now systém are very similar to schetches for AD174 car.

I have tryed to use for the turning of jeep at first the auto ruller from potentiometer, but as the answer of the turning motor is to complicate, I have used two PS2 joystics, one for driving and second fo turning.

I have used in this case Arduino IDE 2.3.8 version on W11 and I have had at first

a problem to convert sketch for receiver to ino code as I have used Esp 3.3.6 library version .. but on the end all went through to the receiver with ESP8266-now and the sender with ESP32-now inos .

There is my sketch for Wemos D1 R32 as ESP-NOW sender :

//ESP32-now-sender-joystick-en-6_Nikko
/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp-now-esp32-arduino-ide/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <esp_now.h>
#include <WiFi.h>

String sketchName = "ESP32-now-sender-joystick-en-6_Nikko";

const int ANALOG_G_PIN = 35; 
const int ANALOG_T_PIN = 34;
const int PIN_BUTTON_START = 16;
const int PIN_BUTTON_STOP = 27;
const int PIN_ESPNOWOFF = 17;
const int PIN_ESPNOWON = 25;

int axeG = 100;     // axe X
int axeGzero = 1800;
int axeT = 200;     // axe Y
int axeTzero = 1800;
int buttonStop = 1;
int buttonStart = 1;
int analogDelay = 20;

int carOn = 0;
bool dataCalibration = false;
bool espNow = false;

bool serialOn = false;

// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0xEC, 0xFA, 0xBC, 0xC9, 0x65, 0x17};   //NodeMCU V0.9 CP Nikko receiver
//Wemos D1 R32 + kontaktni pole - F8:B3:B7:50:BC:5C

// Structure example to send data
// Must match the receiver structure
typedef struct struct_message 
 {
  int dataG;
  int dataT;
  int dataSW;
 } struct_message;

// Create a struct_message called myData
struct_message myData;

esp_now_peer_info_t peerInfo;

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) 
 {
   status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail";
   if (serialOn)
    {
     Serial.print("\r\nLast Packet Send Status:\t");
     Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
    }
   if (status == ESP_NOW_SEND_SUCCESS)
    {
     digitalWrite(PIN_ESPNOWON, HIGH);
     digitalWrite(PIN_ESPNOWOFF, LOW);
    }
   else
    {
     digitalWrite(PIN_ESPNOWON, LOW);
     digitalWrite(PIN_ESPNOWOFF, HIGH);  
    }  
 }
 
void setup() {
  // Init Serial Monitor
  Serial.begin(9600);
  delay(1000);
  Serial.println();
  Serial.println(sketchName);

  pinMode(PIN_BUTTON_START,INPUT_PULLUP);  
  Serial.println();  
  Serial.println("Test of indication: ");
  pinMode(PIN_BUTTON_STOP,INPUT_PULLUP);  
  pinMode(PIN_BUTTON_START,INPUT_PULLUP);  
  pinMode(PIN_ESPNOWOFF,OUTPUT);
  pinMode(PIN_ESPNOWON,OUTPUT);
  digitalWrite(PIN_ESPNOWOFF, HIGH);
  delay(2000);
  digitalWrite(PIN_ESPNOWOFF, LOW);
  
  axeT = analogRead(ANALOG_T_PIN);
  delay(analogDelay);
  
  axeG = analogRead(ANALOG_G_PIN);
  delay(analogDelay);
  
  if (!dataCalibration)
   {
      axeGzero = axeG;
      axeTzero = axeT;
      dataCalibration = true;
   }
  Serial.print("axe G calibration = ");
  Serial.println(axeGzero);
  Serial.print("axe T calibration = ");
  Serial.println(axeTzero);
  digitalWrite(PIN_ESPNOWON, HIGH);
  delay(2000);
  digitalWrite(PIN_ESPNOWON, LOW);
  
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);


  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) 
   {
    Serial.println("Error initializing ESP-NOW");
    return;
   }
  else
   {
    Serial.println("Initializing ESP-NOW OK");
   } 

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  //esp_now_register_send_cb(OnDataSent);
  esp_now_register_send_cb((esp_now_send_cb_t)OnDataSent); // change for Esp32 lib. 3.3.6
  
  // Register peer
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;
  
  // Add peer        
  if (esp_now_add_peer(&peerInfo) != ESP_OK)
   {
    Serial.println("Failed to add peer");
    return;
   }
  else
   {
    Serial.println("ESP-NOW of joystick is ready");
   }
  carOn = 0;
  dataCalibration = false;
  Serial.println("Next serial communication is limited for better work in real time");
  digitalWrite(PIN_ESPNOWOFF, HIGH);
  delay(2000);
  serialOn = false; 
}
 
void loop() 
 {
  // Set values to send
  axeT = analogRead(ANALOG_T_PIN);
  delay(analogDelay);
  
  axeG = analogRead(ANALOG_G_PIN);
  delay(analogDelay);
  
  axeG = 4095 - axeG ;   // reversed value - zero is near of the contacts
  
  buttonStop = digitalRead(PIN_BUTTON_STOP);
  if (buttonStop == 0) 
     {
       carOn = 0;
     }

  buttonStart = digitalRead(PIN_BUTTON_START);
  if (buttonStart == 0) 
     {
       carOn = 1;
     }

  if (serialOn)
   {
      Serial.print("axe G = ");
      Serial.println(axeG);
      Serial.print("axe G - axeG zero = ");
      Serial.println(axeG - axeGzero);
      Serial.print("axe T = ");
      Serial.println(axeT);
      Serial.print("axe T - axeT zero = ");
      Serial.println(axeT - axeTzero);
      Serial.print("car On = ");
      Serial.println(carOn);
   }
  
  myData.dataG = axeG; 
  myData.dataT = axeT;
  myData.dataSW = carOn;
  
  // Send message via ESP-NOW
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
   
  if (serialOn)
   {
    if (result == ESP_OK) 
      {
       Serial.println("Sent with success");
      }
    else 
     {
      Serial.println("Error sending the data");
     }
   }
  else 
   { 
    if (result == ESP_OK) 
     {
       digitalWrite(PIN_ESPNOWON, HIGH);
       digitalWrite(PIN_ESPNOWOFF, LOW);
     }
    else
     {
       digitalWrite(PIN_ESPNOWON, LOW);
       digitalWrite(PIN_ESPNOWOFF, HIGH);  
     }  
  } 
 if (serialOn) 
  {
   delay(5000);
  }
}


There is my sketch for ESP-NOW receiver :

//ESP8266_now_receiver_Nikko_en_8
//New signals for motors :
//Turn Left = TL, Turn Right = TR
//Go Forward = GF, Go Backward = GB
//Signals pro L298N dual
//Chanel A for speed of motor Forward/Backward 
//Inputs IN1, IN2 for move Forward or Backward
//Chanel B for speed of motor Right/Left
//Inputs IN3, IN4 for Right or Left
//Changed from ESP8266_now_receiver_AD174_po_17_tank_en.ino
//Nikko Jeep car controlled with two joysticks
// used at first GPIO for ESP8266 NodeMCU V0.9 Esp-12
// ESP Board MAC Address (for a AD174 tank):  50:02:91:EF:E6:06 NodeMCU V1.0 po
// ESP Board MAC Address:  EC:FA:BC:C9:65:17 of NodeMcu V0.9 CP2021 Nikko Jeep
/*
  Thanks to Rui Santos & Sara Santos and their Random Nerd Tutorials
  Complete ESP-NOW details at https://RandomNerdTutorials.com/esp-now-esp32-arduino-ide/  
  Permission is hereby granted, free of charge, to any person obtaining 
  a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included 
  in all copies or substantial portions of the Software.
*/
// reprogrammed for my Nikko Jeep Wrangered with ESP8266 NodeMCU V0.9 by PavelOu

#include <ESP8266WiFi.h>
#include <espnow.h>

//bool serialOn = false;
String sketchName = "ESP8266_now_receiver_Nikko_en_8.ino";

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message 
 {
  int dataG;
  int dataT;
  int dataSW;
 } struct_message;

 // Create a struct_message called myData
struct_message myData;

long int timeLast = 0;
int timeOut = 1000;

bool dataOn = false;

bool joystickCalibration = false;
bool carOn = false;
bool blinkOn = false;

//definition I/O for Nikko and NodeMcu V0.9 CP2102

int enableMotorT = 4;   // Enable motor in chanel B  GPIO04 = D2  // PWM from 0 to 255
int enableMotorG = 5;   // Enable motor in chanel A  GPIO05 = D1  // PWM from 0 to 255

// Turn Left or Turn Right
int gpTL = 14; // Turn Left  IN3  GPIO13 = D7  // Turn car left
int gpTR = 13; // Turn Right IN4  GPIO14 = D5  // Turn car right

//Go Forward or Go Backward
int gpGF = 15; // Go forward  IN1 GPIO15 = D8  // Two wheels right forward 
int gpGB = 12; // Go backwrad IN2 GPIO12 = D6  // Two wheels right backward

int carDataOnLed = 2;   // GPIO2 = D4 data off LED_BUILT_IN reversed level
int carOnLed = 16;      // D0  indikace car on

const byte carStop = 0;
byte speedGoMax = 220;
byte speedTurnMax = 220;
int carDirectTLR = 0;
int carDirectGFB = 0;

int carSW = 0;  // = 0 = carOff , = 1 carOn
int axeG = 0;   // axe G = speed of motor G
int axeGzero = 2200;
int axeGtolerance = 80;
int axeT = 0; // axe T = speed of motor T
int axeTzero = 2200;
int axeTtolerance = 80;
int axeTLR = 0;  // recounted on position value speedOfmotorT
int axeGFB = 0;  // - " -

// rychlosti pro motory
byte speedOfmotorG = 0;
byte speedOfmotorT = 0;

void InitMotors() //changed from AD174
{
  Serial.println("Motors Initialisation");
  pinMode(gpTL, OUTPUT); //Left Wheels Forward
  pinMode(gpTR, OUTPUT); //Left Wheels Backward
  pinMode(gpGB, OUTPUT); //Right Wheels Forward
  pinMode(gpGF, OUTPUT); //Right WheelsBackward

  digitalWrite(gpTL, LOW);
  digitalWrite(gpTR, LOW);
  digitalWrite(gpGF, LOW);
  digitalWrite(gpGB, LOW);

  pinMode(enableMotorG, OUTPUT);
  pinMode(enableMotorT, OUTPUT);

  digitalWrite(enableMotorG, 0);
  digitalWrite(enableMotorT, 0);
}

void MoveCar(byte newDirect)  //for variant of receiver
 {
  if (newDirect == 0)   //stop
   {
    //Serial.println("Car will stop");
    digitalWrite(gpTR,LOW); //set gpTR Low level
    digitalWrite(gpTL,LOW);  //set gpTL high level
    digitalWrite(gpGB,LOW);  //set gpGB Low level
    digitalWrite(gpGF,LOW); //set gpGF high level   
    analogWrite(enableMotorT,0);
    analogWrite(enableMotorG,0);
       
   }
  if (newDirect == 1)   //forward
   {
    //Serial.println("Car will goes forward");
    digitalWrite(gpGB,LOW);  //set gpGB Low level
    digitalWrite(gpGF,HIGH); //set gpGF high level  
    //digitalWrite(carBackLed,LOW);    
   }
  if (newDirect == 2)  //bacward
   {
    //Serial.println("Car will goes backward");
    digitalWrite(gpGB,HIGH);  //set gpGB High level
    digitalWrite(gpGF,LOW); //set gpGF LOW level         
    //digitalWrite(carBackLed,HIGH);    
   }
  if (newDirect == 3)  //turn left
   {
    //Serial.println("Car will turn left");
    digitalWrite(gpTR,HIGH); //set gpTR Higy level
    digitalWrite(gpTL,LOW);  //set gpTL Low level
   }
  if (newDirect == 4)  //turn right
   {
    //Serial.println("Car will turn right");
    digitalWrite(gpTR,LOW); //set gpTR Higy level
    digitalWrite(gpTL,HIGH);  //set gpTL Low level
   }
  }


// callback function that will be executed when data is received
void OndataGecv(const uint8_t * mac, const uint8_t *incomingData, int len) 
 {
  memcpy(&myData, incomingData, sizeof(myData));
  axeG = myData.dataG; // speedOfmotorG 
  axeT = myData.dataT; // speedOfmotorT
  carSW = myData.dataSW;
  dataOn = true;
  digitalWrite(carDataOnLed,HIGH); 
  if (carSW == 0)
   { 
       carOn = false;
       joystickCalibration = false;           
       digitalWrite(carOnLed,LOW); 
    }
  if (carSW == 1) 
   {
     if (!carOn)
        {
         if (!joystickCalibration)
          {
           axeTzero = axeT;
           axeGzero = axeG;
           joystickCalibration = true;
          }
         carOn = true;
         digitalWrite(carOnLed,HIGH); 
       }
   }  

  analogWriteResolution(8);
        
  carDirectTLR = 0;
  axeTLR = axeT - axeTzero;
  if (axeTLR<0)  // Y < 0 = turn to the left 
      {
       axeTLR = axeTzero - axeT;
       carDirectTLR = 3;
      }  
     else
      {
       carDirectTLR = 4;
      }  
  MoveCar(carDirectTLR);          

  int speedintT = map(axeTLR,0,axeTzero,0,speedTurnMax);
  speedOfmotorT = constrain(speedintT,0,speedTurnMax);
     
  carDirectGFB = 0;
  axeGFB = axeG - axeGzero;
  if (axeGFB<0)  // X < 0 = motor G  / car goes  backward
      {
       axeGFB = axeGzero - axeG;
       carDirectGFB = 2;
      }  
     else
      {
       carDirectGFB = 1;
      }  
  MoveCar(carDirectGFB);          

  int speedintG = map(axeGFB,0,axeGzero,0,speedGoMax);
  speedOfmotorG = constrain(speedintG,0,speedGoMax);
  if (carOn)
   {          
    analogWrite(enableMotorG,speedOfmotorG); 
    analogWrite(enableMotorT,speedOfmotorT);
   }     
  else
   {
     MoveCar(0);
     analogWrite(enableMotorG,0);
     analogWrite(enableMotorT,0);
   }
  timeLast = millis(); 
 }
 
void setup() 
 {
  // Initialize Serial Monitor
  Serial.begin(9600);
  delay(1000);
  Serial.println(sketchName);
  //before using io pin, pin mode must be set first 
  InitMotors();
  //pinMode(espnowLED, OUTPUT);
  pinMode(carOnLed, OUTPUT);
  pinMode(carDataOnLed, OUTPUT);
  Serial.println("Test of outputs - indication:");  
  digitalWrite(carDataOnLed,HIGH); 
  delay(1000);
  digitalWrite(carDataOnLed,LOW); 
  delay(1000);
  digitalWrite(carOnLed,HIGH); 
  delay(1000);
  digitalWrite(carOnLed,LOW); 
  delay(1000);
  Serial.println("Set of motors");
  analogWriteResolution(8);
     
  Serial.println("Preliminary calibration of joystick ");
  Serial.print("axeTzero: ");
  Serial.println(axeTzero);  // speed left zero
  Serial.print("axeGzero: ");
  Serial.println(axeGzero);  // speed right zero

  speedOfmotorG = 0;
  speedOfmotorT = 0;
  carOn = false;
  joystickCalibration = false;
  
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != 0) 
   {
    Serial.println("Error initializing ESP-NOW");    
    return;
   }
  else
   {
    Serial.println("ESP-NOW is initialized");    
   }

  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_register_recv_cb(esp_now_recv_cb_t(OndataGecv));
  digitalWrite(carDataOnLed,LOW);
  digitalWrite(carOnLed,LOW);
  Serial.println("There is end of serial communication for work in better real time");
  delay(5000);
 }
 
void loop() 
 {
  long int timeActual = millis() - timeLast;
  if (timeActual > timeOut)
   {
    // watch dog
    if (!dataOn)
     {
      digitalWrite(carDataOnLed,LOW);
     }
    else
     {
      dataOn = false;
     }
    timeLast = millis();
   }
 }  

There are photos of my mechanical solution :

and there is detail of my receiver detailed :

Nice! Any plans to refine it?

Hi , for my purpose so for my grandchildren it is something as school preparat .. :) Some next question?