ESP8266 WiFi RC car

Modify RC Car to control via WiFi using ESP8266

ESP8266 WiFi RC car

This page explains how to modify a Model RC car to control via WiFi using ESP8266. Both lua (NodeMCU) and Arduino IDE code available!

Materials needed:
- Model RC car waiting to be modified
- ESP-01 WiFi module
- 8-pin socket for the ESP-01 module
- 2x 2k resistors
- 2x 47uF capacitors
- 3.3V 250mA LDO voltage regulator MCP1702T3302ECB
- 2-pin jumper + jumper header
- wires
- protoboard
- soldering iron + solder
- Android phone with RoboRemo app installed

Introduction

In this project I used a JAMARA COCOON 1:10 4WD Model RC car. The car has a powerful DC motor contorlled by an Electronic Speed Controller (ESC) and a Servo Motor for steering.
The ESC input signal is servo-type PWM, like the one used for Servo, it just controls the DC motor speed instead of the Servo motor angle.
So it has totally 2 PWM signals that come from a 2-ch. 2.4G receiver. What I did in this project was to replace the 2-ch. receiver with an ESP8266 so that I can control the car via WiFi :)

The final schematic looks like this:

esp8266-wifi-rc-car-schematic.png

Steps:

Using LUA and NodeMCU:

1.
Flash NodeMCU Firmware to ESP8266 (Here is how).

2.
Upload this init.lua script to the ESP:

wifi.setmode(wifi.SOFTAP)

cfg={}
cfg.ssid="mywifi"
cfg.pwd="qwerty123"

cfg.ip="192.168.0.1"
cfg.netmask="255.255.255.0"
cfg.gateway="192.168.0.1"

port = 9876

wifi.ap.setip(cfg)
wifi.ap.config(cfg)

function stringStarts(a,b)
    return string.sub(a,1,string.len(b))==b
end

function stringEnds(a,b)
   return b=='' or string.sub(a,-string.len(b))==b
end

chCount = 2
chPin = {3, 4} -- GPIO 0, 2
chVal = {1500, 1500}

cmd = ""

for i=1, chCount do
    gpio.mode(chPin[i],gpio.OUTPUT)
    gpio.write(chPin[i],gpio.LOW)
end

pwmEn = 0 -- disabled until connect

tmr.alarm(0,20,1,function() -- 50 Hz
    if pwmEn==1 then
        for i=1, chCount do
            if chVal[i] then -- if valid val
                -- gen pulse
                gpio.write(chPin[i], gpio.HIGH)
                tmr.delay(chVal[i]*0.893921) -- with correction
                gpio.write(chPin[i], gpio.LOW)
            end
        end
    end
end)


function exeCmd(st) -- ex: "ch0 1500"

    tmr.stop(1) -- clr contact lost tmr
    pwmEn = 1
    
    tmr.alarm(1,500,0,function() -- if cnt lost for 500ms
        pwmEn = 0
    end)
    
    if stringStarts(st, "c") then
        ch = tonumber( string.sub(st, 3, 3) ) + 1
        val = tonumber( string.sub(st, 5, string.len(st) ) )
    end
    
    if stringStarts(st, "ch") then chVal[ch] = val end  
    if stringStarts(st, "ci") then chVal[ch] = -val end   
    if stringStarts(st, "ca") then chVal[ch] = 1500 + val*51 end  
    if stringStarts(st, "cb") then chVal[ch] = 1500 - val*51 end
end


function receiveData(conn, data)
    cmd = cmd .. data

    local a, b = string.find(cmd, "\n", 1, true)   
    while a do
        exeCmd( string.sub(cmd, 1, a-1) )
        cmd = string.sub(cmd, a+1, string.len(cmd))
        a, b = string.find(cmd, "\n", 1, true)
    end 
end


print("ESP8266 RC receiver 1.0 powered by RoboRemo")
print("SSID: " .. cfg.ssid .. "  PASS: " .. cfg.pwd)
print("RoboRemo app must connect to " .. cfg.ip .. ":" .. port)

srv=net.createServer(net.TCP, 28800) 
srv:listen(port,function(conn)
    print("RoboRemo connected")
     
    conn:on("receive",receiveData)  
    
    conn:on("disconnection",function(c) 
        print("RoboRemo disconnected")
    end)
    
end)

3.
Build the circuit according to the schematic, mount on RC car and turn ON.

4.
From Android WiFi settings, connect to SSID "mywifi" with password "qwerty123".

5.
Open RoboRemo, build the interface.

6.
Connect to 192.168.0.1:9876 and enjoy :)


Using Arduino IDE:

1.
Upload this sketch to your ESP:

// 4-channel RC receiver for controlling
// an RC car / boat / plane / quadcopter / etc.
// using an ESP8266 and an Android phone with RoboRemo app

// Disclaimer: Don't use RoboRemo for life support systems
// or any other situations where system failure may affect
// user or environmental safety.

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <Servo.h>

// config:

const char *ssid = "mywifi";  // You will connect your phone to this Access Point
const char *pw = "qwerty123"; // and this is the password
IPAddress ip(192, 168, 0, 1); // From RoboRemo app, connect to this IP
IPAddress netmask(255, 255, 255, 0);
const int port = 9876; // and this port

const int chCount = 4; // 4 channels, you can add more if you have GPIOs :)
Servo servoCh[chCount]; // will generate 4 servo PWM signals
int chPin[] = {0, 2, 14, 12}; // ESP pins: GPIO 0, 2, 14, 12
int chVal[] = {1500, 1500, 1500, 1500}; // default value (middle)

int usMin = 700; // min pulse width
int usMax = 2300; // max pulse width


WiFiServer server(port);
WiFiClient client;


char cmd[100]; // stores the command chars received from RoboRemo
int cmdIndex;
unsigned long lastCmdTime = 60000;
unsigned long aliveSentTime = 0;


boolean cmdStartsWith(const char *st) { // checks if cmd starts with st
  for(int i=0; ; i++) {
    if(st[i]==0) return true;
    if(cmd[i]==0) return false;
    if(cmd[i]!=st[i]) return false;;
  }
  return false;
}


void exeCmd() { // executes the command from cmd

  lastCmdTime = millis();

  // example: set RoboRemo slider id to "ch0", set min 1000 and set max 2000
  
  if( cmdStartsWith("ch") ) {
    int ch = cmd[2] - '0';
    if(ch>=0 && ch<=9 && cmd[3]==' ') {
      chVal[ch] = (int)atof(cmd+4);
      if(!servoCh[ch].attached()) {
        servoCh[ch].attach(chPin[ch], usMin, usMax);
      }   
      servoCh[ch].writeMicroseconds(chVal[ch]);
    }
  }
  
  // invert channel:
  // example: set RoboRemo slider id to "ci0", set min -2000 and set max -1000
  
  if( cmdStartsWith("ci") ) {
    int ch = cmd[2] - '0';
    if(ch>=0 && ch<=9 && cmd[3]==' ') {
      chVal[ch] = -(int)atof(cmd+4);
      if(!servoCh[ch].attached()) {
        servoCh[ch].attach(chPin[ch], usMin, usMax);
      }   
      servoCh[ch].writeMicroseconds(chVal[ch]);
    }
  }
  
  // use accelerometer:
  // example: set RoboRemo acc y id to "ca1"
  
  if( cmdStartsWith("ca") ) {
    int ch = cmd[2] - '0';
    if(ch>=0 && ch<=9 && cmd[3]==' ') {
      chVal[ch] = (usMax+usMin)/2 + (int)( atof(cmd+4)*51 ); // 9.8*51 = 500 => 1000 .. 2000
      if(!servoCh[ch].attached()) {
        servoCh[ch].attach(chPin[ch], usMin, usMax);
      }   
      servoCh[ch].writeMicroseconds(chVal[ch]);
    }
  }
  
  // invert accelerometer:
  // example: set RoboRemo acc y id to "cb1"
  
  if( cmdStartsWith("cb") ) {
    int ch = cmd[2] - '0';
    if(ch>=0 && ch<=9 && cmd[3]==' ') {
      chVal[ch] = (usMax+usMin)/2 - (int)( atof(cmd+4)*51 ); // 9.8*51 = 500 => 1000 .. 2000
      if(!servoCh[ch].attached()) {
        servoCh[ch].attach(chPin[ch], usMin, usMax);
      }   
      servoCh[ch].writeMicroseconds(chVal[ch]);
    }
  }
  
  
  
}



void setup() {

  delay(1000);

  /*for(int i=0; i<chCount; i++) {
    // attach channels to pins
    servoCh[i].attach(chPin[i], usMin, usMax);
    // initial value = middle
    chVal[i] = (usMin + usMax)/2;
    // update
    servoCh[i].writeMicroseconds( chVal[i] );
  }*/
  
  cmdIndex = 0;

  
  
  Serial.begin(115200);

  WiFi.softAPConfig(ip, ip, netmask); // configure ip address for softAP 
  WiFi.softAP(ssid, pw); // configure ssid and password for softAP

  server.begin(); // start TCP server

  Serial.println("ESP8266 RC receiver 1.1 powered by RoboRemo");
  Serial.println((String)"SSID: " + ssid + "  PASS: " + pw);
  Serial.println((String)"RoboRemo app must connect to " + ip.toString() + ":" + port);
  
}


void loop() {

  // if contact lost for more than half second
  if(millis() - lastCmdTime > 500) {  
    for(int i=0; i<chCount; i++) {
      // set all values to middle
      servoCh[i].writeMicroseconds( (usMin + usMax)/2 );
      servoCh[i].detach(); // stop PWM signals
    }
  }

  

  if(!client.connected()) {
    client = server.available();
    return;
  }

  // here we have a connected client

  if(client.available()) {
    char c = (char)client.read(); // read char from client (RoboRemo app)

    if(c=='\n') { // if it is command ending
      cmd[cmdIndex] = 0;
      exeCmd();  // execute the command
      cmdIndex = 0; // reset the cmdIndex
    } else {      
      cmd[cmdIndex] = c; // add to the cmd buffer
      if(cmdIndex<99) cmdIndex++;
    }
  } 

  if(millis() - aliveSentTime > 500) { // every 500ms
    client.write("alive 1\n");
    // send the alibe signal, so the "connected" LED in RoboRemo will stay ON
    // (the LED must have the id set to "alive")
    
    aliveSentTime = millis();
    // if the connection is lost, the RoboRemo will not receive the alive signal anymore,
    // and the LED will turn off (because it has the "on timeout" set to 700 (ms) )
  }

}

2.
Build the circuit according to the schematic, mount on RC car and turn ON.

3.
From Android WiFi settings, connect to SSID "mywifi" with password "qwerty123".

4.
Open RoboRemo, build the interface.

5.
Connect to 192.168.0.1:9876 and enjoy :)