Jonathan Loong
Published © GPL3+

DIY RC Car

Arduino-powered DIY remote control car

IntermediateWork in progress22
DIY RC Car

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
Arduino UNO
Arduino UNO
×1
SparkFun Snappable Protoboard
SparkFun Snappable Protoboard
×2
Lipo 2S 2200mAh
×1
Toggle Switch, (On)-Off-(On)
Toggle Switch, (On)-Off-(On)
×1
5 mm LED: Red
5 mm LED: Red
×1
5 mm LED: Green
5 mm LED: Green
×1
Resistor 220 ohm
Resistor 220 ohm
×2
915MHz 3DR Telemetry Radio
×2
Jumper wires (generic)
Jumper wires (generic)
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Wire Stripper & Cutter, 32-20 AWG / 0.05-0.5mm² Solid & Stranded Wires
Wire Stripper & Cutter, 32-20 AWG / 0.05-0.5mm² Solid & Stranded Wires

Story

Read more

Schematics

Schematic of RC Transmitter Circuit

Code

RC Transmitter Code

Arduino
This takes in a connected vintage joystick and reads in the ADC for the steer and throttle axes and trigger input (for safety switch). It then sends these over hardware serial to the connected telemetry radio
/*

Driver to send vintage joystick inputs to 915 MHz telemetry link
to send to RC car

References:
http://www.built-to-spec.com/blog/2009/09/10/using-a-pc-joystick-with-the-arduino/
https://old.pinouts.ru/InputCables/GameportPC_pinout.shtml
https://baremetalmicro.com/tutorial_avr_digital_io/04-Outputs.html

*/

//#define DEBUG_MODE //debug_mode shows raw ADC 
#define STEER_ADC A0
#define THROTTLE_ADC A1
#define LED 6
#define TRIGGER 2
#define BLINK_TIME 250

char tx_array[24] = {};
unsigned long blink_ctr = 0;

#define MOVING_AVG_FILTER_NUM 5
int smooth_array_steer[MOVING_AVG_FILTER_NUM];
int smooth_array_throttle[MOVING_AVG_FILTER_NUM];
int index_steer = 0;
int sum_steer = 0;
int index_throttle = 0;
int sum_throttle = 0;
int smooth_steer = 0;
int smooth_throttle = 0;

void setup()
{

  Serial.begin(57600);
  pinMode(STEER_ADC,INPUT);
  pinMode(THROTTLE_ADC,INPUT);
  pinMode(LED,OUTPUT);
  pinMode(TRIGGER,INPUT);
}

int mapVal(int curr_val, int minIn, int maxIn, int minOut, int maxOut)
{
  return (minOut + ((maxOut - minOut) / (maxIn - minIn) * (curr_val - minIn)));
}

// moving average filter for ADC (steer)
int tx_steer()
{
//  sum_steer = sum_steer - smooth_array_steer[index_steer];
//  uint8_t temp_value_steer = analogRead(STEER_ADC);
//  smooth_array_steer[index_steer] = temp_value_steer;
//  sum_steer += temp_value_steer;    
//  index_steer = (index_steer+1) % MOVING_AVG_FILTER_NUM;
//  smooth_steer = sum_steer / MOVING_AVG_FILTER_NUM;
  return mapVal(analogRead(STEER_ADC),960,670,1000,2000);
}

// moving average filter for ADC (throttle)
int tx_throttle()
{
  sum_throttle = sum_throttle - smooth_array_throttle[index_throttle];
  uint8_t temp_value = analogRead(THROTTLE_ADC);
  smooth_array_throttle[index_throttle] = temp_value;
  sum_throttle += temp_value;
  index_throttle = (index_throttle+1) % MOVING_AVG_FILTER_NUM;
  smooth_throttle = sum_throttle / MOVING_AVG_FILTER_NUM;
  int converted_throttle = mapVal(smooth_throttle,27,252,1000,1240);
  if (converted_throttle > 1200) return 1200;
  else return converted_throttle;
}

void loop()
{
  
  if (digitalRead(TRIGGER) == LOW)
  {
    if (millis() - blink_ctr > BLINK_TIME)
    {
      blink_ctr = millis();
      digitalWrite(LED,HIGH);     
    }
    else
    {
      digitalWrite(LED,LOW);
    }
     
    #ifdef DEBUG_MODE
      sprintf(tx_array,"T: %d S: %d\n",analogRead(THROTTLE_ADC),analogRead(STEER_ADC));
    #else
      sprintf(tx_array,"%d,%d#\n",tx_throttle(),tx_steer());
    #endif
  }
  else
  {
    sprintf(tx_array,"%d,%d#\n",990,1500);
  }
  Serial.print(tx_array);    
  delay(50);  
}

RC Car Receiver Code

Arduino
This code takes in the telemetry from the car-side radio and parses it into usable throttle and steer values for actuation.
// Hardware Serial to receive XBOX input from 915 MHz telemetry link and
// actuate steering and throttle servos

// Adapted from https://forum.arduino.cc/t/serial-input-basics-updated/382007/3

#include <Servo.h> 
#include <SoftwareSerial.h>

const int numChars = 32;
char receiveArray[numChars];
char tempArray[numChars];
int char_ctr = 0;
char stopMarker = '#'; //stop byte is # symbol

//parsed input from XBOX
int currSteer = 0;
int currThrottle = 0;

boolean newData = false;

// Defining hardware pins
#define MOTOR_PWM 3   //Motor for propulsion 
#define SERVO_PWM 5   //Servo for steering. (to calibrate) 

#define PWM_TOP 1200
#define PWM_BOT 1000
#define PWM_MID 1500
#define LED 2

Servo driveMotor;
Servo steerServo;

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(57600);
  pinMode(LED,OUTPUT);
  analogWrite(LED,140);

  driveMotor.writeMicroseconds(PWM_BOT);
  steerServo.writeMicroseconds(PWM_MID);

  driveMotor.attach(MOTOR_PWM);
  steerServo.attach(SERVO_PWM);
  delay(1000);
}

void parseData() {      // split the data into its parts in CSV format

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempArray, ",");
    currThrottle = atoi(strtokIndx);   

    strtokIndx = strtok(NULL, ",");
    currSteer = atoi(strtokIndx);     

}

void actuateRC() {
    //Serial.print("Throttle ");
    //Serial.print(currThrottle);
    //Serial.print(",Steering ");
    //Serial.println(currSteer);
    
    // actuate servos from input
    if (currThrottle >= PWM_TOP) currThrottle = PWM_TOP; //set max threshold to prevent motor burnout
    steerServo.writeMicroseconds(currSteer);
    driveMotor.writeMicroseconds(currThrottle);

}

void loop()
{
  
  ///*
  while (Serial.available() > 0 && newData == false)
  {
    char rc = Serial.read();
    //Serial.println(rc);

    if (rc != stopMarker)
    {
      receiveArray[char_ctr] = rc;
      char_ctr++;
      if (char_ctr >= numChars) char_ctr = numChars - 1;
    }
    // at stopMarker
    else 
    {
      receiveArray[char_ctr] = '\0'; //null terminate string
      char_ctr = 0;
      newData = true;
    }        
  }

  if (newData == true) {
        strcpy(tempArray, receiveArray);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        actuateRC();
        newData = false;
    }
  //*/

//  if (rcSerial.available() > 0)
//    Serial.println(rcSerial.read());
    
  delay(50);
}

Credits

Jonathan Loong

Jonathan Loong

0 projects • 0 followers

Comments

Add projectSign up / Login