Introduction: Remote Control

A few months ago I bought a rc car, a used one, very cheap. a maverick stealth xb or 4.6 hpi pulse (after the HPI bought Maverick).

I bought it without engine, but all the mechanics are very good, and also I have the remote (2ch) and receiver. I decide that the methanol engine is quite powerful but has a big disadvantage, has no reverse, so choosing the electric motor and trying to make the remote and the receiver by my self.

I go after the idea of DIY

If you've already seen I apologize for my bad English :))

The remote is made mostly from scratch. Its heart is Arduino, uses two potentiometers for throttle / break and steering, a graphic LCD (one from nokia 1100), five analog key to navigate the menu, other 4 programmable keys with LED indicators for communications I chose serial connection (Rx, Tx) and UTC-4432-B1 Long Distance Wireless Transceiver Module w / Antenna(I don't have it yet, its on the road :P), and housing is an old remote helicopter (doublehorse Something ... chinese)

.

The videos are not so good, are made with the phone :( and also the code was not puted on the receiver board, i just use an arduino for test.The serial data is send through 3 wires and I dont,t have a ESC for the motor but I connect a rgb led, blue light is throttle forward and green is reverse :P

Step 1: List of What You Need...for the Remote and Reciver

I used un arduino bard only to make the soft and to upload it to a board named "Reduino" :). Reduino comes as a DIY kit, and it's nothing but a ATMEGA 328 with arduino uno bootloader, external clock, the reset button, a power LED and pins all Arduino compatible

I had an old nokia lying around, and decided to use it. Normally you can use any display that matches the housing that you have / do. The Lcd may also connect with SPI or I2C (is also very used the lcd from nokia 3310 the LCD is very easy to use, there are some good libraries). if you choose the LCD Nokia 1100, I spent a few nights and made a library for this type of display (PCF8814). You can find the library here at the arduino forum or here at GitHub

Remote:

1. Arduino uno board(or reduino), 1 pcs (normaly any arduino is good)

2. The lcd

3. Potentiometer, 2 pcs (I needed 4-5 because I used only a part of it and when I opened them I broke some of them)

4. Push button, 9 psc

5. 7805 voltage regulator, 1 psc(for arduino power)

6. 7803 voltage regulator, 1 psc(for lcd power)

7. Rezistors, capacitors, some leds

8. Wires, pins, heders, tape, shrink tube...

9. Some small driled PCBs(i guess prototiping pcb)

10. Power jack socket, 1 pcs (use an external power suply)

11. Some screws

Reciver:

1. Arduino uno board(or reduino), 1 pcs (normaly any arduino is good)

2. 7805 voltage regulator, 1 psc(for arduino power)
3. 7806 voltage regulator, 1 psc(for servo power)

4. Power button

5. Rezistors, capacitors, some leds

6. Wires, pins, heders, tape, shrink tube...

Tools:

You don't need some special tools.

Screwdrivers, pliers,

Drill machine and some drill bits

Soldering station

A dremel (i did not have, so is optional)

Step 2: Prepareing the Lcd

I played pretty much until I managed to use display. First time I used one of the nokia 3310 but I got to tear metal pins. with 3310 lcd was easy, there are a lot of libraries and bits of code that can be used. lcd 1100 we found only a simple library not having the graphics, then I tried to combine several libraries and after a few nights I managed to finish. I put the link in intro but go here. The driver for 1100 lcd is PCF8814 you can find the datasheet here.

To protect the display I decided to keep the metal housing and the clear plastic piece instead have used other LEDs.
I read quite well on this lcd and found different opinions about voltage on data pins (SPI), the datasheet says it's also ok 5v, but preventive I used some 10k resistors.

Connections SCLK (pin lcd 5) sdin (lcd pin 4), reset (lcd pin 2), SCE (lcd pin 1) is conected to 13, 11, 12, 10 on the Arduino through 10k resistors, the power is supplied by IC 7803 (3.3v) on lcd pins 6 and 7 + Lcd 3.3v and the pin 3 GND.

I fix the display with his metal cover on a PCB with 4 screws and in the top side(where the conections are made) the cables are fixed with another PCB piece and in the midle of the big PCB i put a sheet of metal that will fix the display on the remote case.

Step 3: Joysticks

The original board of the remote had some simple contacts for stear and about 4 contacts to throttle

I cut where the screw starts to pull the shaft, then I drill the shaft on plastic side so we left with the pcb with carbon and the white plastic with the contacts
I fixed the plastic with a screw on the mobile part from the joystick. If the pcb is not moving easely on the joystick you can put a verry small nut between joystick and the white plastic.

The pcb must be glue on the remote housing.

Step 4: Analog Key

For the meniu navigation i put 5 buttons, the actual switch are glued on the battery houseing and the plastic buttons are taped on top side of the remote.

The action keys(autopilot, headlight, reverse, divider) are puted on a small pcb with the led indicator and the rezistors.

The actual button(the plasticthat you push) are from some toys. You have to take care to the size of the holes you drill so the plastic button won't fall out.

The functional part is verry simple, every click take the +5v through 1, 2 ,3..... rezistors so you just need to read the analog value and compare, for a better and stable way i did something like this: Eg. key ok ....analogRead = 200...if analogRead <250 & >150 key = ok.

Step 5: Power Board

here I have just 2 voltage regulators, one of 5 volts for Atmega and another one for LCD(3.3 volts).

also here i put a voltage divider so i can read the battery voltage. For more info about voltage divider and reading voltages higher then 5 v go here

Step 6: Comunication

I chose a communications wireless module operating at 300-510 MHz. It is a serial transciver uses only two pins connected to the Arduino (rx, tx) and the the specifications shows that would work at 2000 meters, is included antenna, power supply is between 2.1 and 3.6 and is inexpensive. but like I said i don't have yet the module and I used wire for testing. This is the module "UTC-4432-B1 Long Distance Wireless Transceiver Module w/ Antenna", you can buy it from dx.com and they have there the full specification.

Step 7: Reciver

for the reciver a made a board fixed on the reduino but i am thinking to make a shield.

for the board or shield you will need a 5v regulator, a 6v regulator for servos(this depends on what servos you use), I olso put some pins for servo conection and 2 voltage divider(1 divider to read the battery that will power up the "logic part", arduino and servos, and another divider for motor batery).

i will put all the schematics on the next steps i guess.

for the reciver i first thinking for a optical encoder to read the rpm from the motor and calculate the speed and the distance traveled. I am not shure but i do not think that the AtMega 328 is capable to keep up with an intrerupt at high rates and the serial comunication, so may be a AtTiny will do the job to send the rpm and save the distance, then send it to 328 trough I2C.....if you have sugestion I need them :P

Step 8: Hardware Update :)))

The remote control case have a place where should be a quartz crystal for tuning the frequency, I used this to put an ISP socket and make the soft update easyer.

Step 9: The "Code" :)) Remote Code

The code is quite large and it will be pretty difficult to explain and also no pictures helps :P
The first attempts I got 99% microcontroller memory , the flash and the RAM, so I learned to use "programem" to write an int in EPROM, also LCD driver or library ... and much more.

Ok let's start with the Lcd .

First time you have to include the library #include. In the library I made a class so "static PCF8814 lcd;" may be helpful so you can use it like "lcd.print("Car remote") ". I put the lcd up side down in the case so in setup is something like this "lcd.begin(); lcd.rotate(1);" . The begin function starts the lcd and define the pins that arduino use to conect with lcd, so if u need to connect at other pins, consult the PCF8814.h to be sure the order of the pins but i think is like this, from default lcd.begin(13,11,12,10); rotate() with 1 turn the img on lcd up side down and with 0 is normal.

you can print with lcd.print() string; set the cursor with lcd.setCursor(y,x), y being the row (1 row = 8 pixels) on this lcd you have 8 rows, first row is 0 and last 7, and y being the pixel number from letf to right; and you can do more but you will see it later.

Now how to read the analog keys.

int analogValue = analogRead(analogKeyPin);////// will return a value depending on what button you push(just watch the schematics and try to think where the curent flow, you will have diferit rezistance on each button). you can use serial monitor to read them or the lcd.print(analogValue) to print them on lcd, then some if-s or switch/case and you are done eg.

if (analogValue > 25 && analogValue < 40){ key = "meniu"; } 
else if (analogValue > 40 && analogValue < 70){key = "back";  }

I used string to store the key because is easier to program but you can use an int to save more ram.

The bigest problem I had was the strings for menius so i used the programem to block the strings in flash memory. So I made an array like this:

prog_char meniu_0[] PROGMEM = "Obtions";
prog_char meniu_1[] PROGMEM = "Autoset";
prog_char meniu_2[] PROGMEM = "Stearing set";
PROGMEM const char *meniu[] = {  meniu_0,meniu_1, meniu_2 };  

This way you can easyely generate the menius

for (uint8_t x = 0; x < 7 ; x++){     // 7 is the number of option you have in 
					menu, so stop the for at 6 poz
     lcd.setCursor(6, x);

//read from progrmem

strcpy_P(buffer, (char*)pgm_read_word(&(meniu[pozMeniu + x]))); lcd.print(buffer); }

7 is the number of option you have in menu, so make the "for" 7 times, then set cursor to print from first row(0) with x like this will go on next row when the prog make the second time the for, I start printing the options from the 6th pixel because from 0 to 6 i will draw cursor. Now strcpy_P function copy the string from programen in a buffer that i declare it with 50 chars is a bit too much but you have to be sure that is enough, after that just print the buffer.

pgm_read_word(&(meniu[pozMeniu + x]))

this reads the value from programem array menu[0], if the pozMeniu and x is "0". I use pozMeniu because when I will put the cursor and i want to navigate up and down and let's say I have 10 obtions an just 7 rows, when i go with the cursor at the 7th option and i wanna go downer i will just make pozMeniu =1 like this I will print starting with the second option and on the 7th row I will have the 8th option . For that i made a small function that needs a parameter, the number of obtions. So for the last exemple is " upDown_navigate(6); " (also start counting with "0"). if you download the code i have the function explained there.

The upDown_navigate(); also returns a "pozCursor" that it will change when you go up and down so you can print a cursor like this :

lcd.setCursor(0, pozCursor);
lcd.print("\2");

The "/2" is the second char that i define it. is the cursor that a was saying. Yes, you can add custom chars, you will see in the link with the entire code.

If you wanna do a sub menu you can do it like this(is just one idea, I made it like this):

"level" - this i use it in void loop() to go to a specific screen(I have function for every menu)
"countUpDown" - this is generated by upDown_navigate(), saves the position on array(you know what obtion you select)

<p>if (key == "ok"){
       switch (countUpDown) {
            case 0:
                level = 2;
                countUpDown = 0;
                pozCursor = 0;
                pozMeniu = 0;
                lcd.clear();   
                break;
            case 1: 
                level = 3;
                countUpDown = 0;
                pozCursor = 0;
                pozMeniu = 0;
                lcd.clear();    
                break;    
           case 2:    
                level = 4;
                countUpDown = 0;
                pozCursor = 0;
                pozMeniu = 0; 
                lcd.clear();
                break;

Step 10: The Code - Eprom

When you do some menus you will wanna modify some settings an your arduino to save them and remember them after a reset or when it gets out of power so for that you need to use Eprom, that's easy, but if you wanna save a int you will have to split it in 2 addresses. I found that this is easy too if you look in the code i have a funtion EEPROMWriteInt and also a readInt.

So you have your option selected, you need to change some values. Let's say I wanna change the brightness of the backlight on lcd/Here I made a trick, I use an multi dimensonal array where i save the eprom adresss, the step to increase or decreases the value saved in eprom, the maximum value, the minimum value.

prog_char obtions_0[] PROGMEM = "Headlight";
prog_char obtions_1[] PROGMEM = "Backlight";
prog_char obtions_2[] PROGMEM = "Autopilot";


 PROGMEM const char *obtions[] = {obtions_0,
                                  obtions_1,
                                  obtions_2 };


   //define values ==== [value], [step], [min], [max]       
  //value is in fact eeprom adress to read
   int obtions_item_value[][4] = { {72,1,0,1},
                                   {2,10,0,255},
                                   {70,1,0,1} };

And like this you just list all option in a menu and if you select it you will change the value in eprom . the coment are made like you select the second option(Backlight)

void Obtions(){
if (option_open == 0){
        lcd.setCursor(0, pozCursor);
        lcd.print("\2");
        lcd.setCursor(10, 7);
        lcd.print(" Home  Back ");
       //////////////print options/////////////////           
        for (uint8_t x = 0; x < 3 ; x++){                
           lcd.setCursor(6, x);
           strcpy_P(buffer, (char*)pgm_read_word(&(obtions[pozMeniu + x])));                                lcd.print(buffer);
        }
        upDown_navigate(2); 
}
/////////////////////if you select an option/////////////////////
else if (option_open == 1){
        lcd.setCursor(0,1);
        strcpy_P(buffer, (char*)pgm_read_word(&(obtions[countUpDown])));  
//print the option name(remember "countUpDown")                        
        lcd.print(buffer);
        lcd.setCursor(0,2);
        int x = EEPROMReadInt( obtions_item_value[countUpDown][0] ); 
//x = eprom read Int (2)
        lcd.clearLine();
        lcd.print(x);
        if (key == "up"){
            x = x + obtions_item_value[countUpDown][1];   
///if up x = x + step (step here is 10)
            if ( x > obtions_item_value[countUpDown][3]){    
// if x get bigger then max, stop it(max here is 255)
                 x = obtions_item_value[countUpDown][3] ;     
            }                                          
        }
       if (key == "down"){
            x = x - obtions_item_value[countUpDown][1];                                          
            if ( x < obtions_item_value[countUpDown][2]){  
 // if x get smaller then min, stop it(min here is 0)
                x = obtions_item_value[countUpDown][2] ;
            }                                        
        }
        EEPROMWriteInt( (obtions_item_value[countUpDown][0]),x );      
//save back the x in eprom
        delay(70);
    }
//////////////option_open "0" or "1" when push ok to enter in obtion//////////////////
    if (key == "ok" & option_open == 0){
       option_open = 1;
       lcd.clear();
       key = "no key";  
    }
    else if (key == "ok" & option_open == 1){
       option_open = 0;
       lcd.clear(); 
       key = "no key";
    }
  ////////////go back one level if push back/////////////////////
    if (key == "back"){
       lcd.clear();
       level = 1;
    }                         
}

Step 11: The Code - the Rest O "screens"

First of all I have a home screen where a draw a car img, img is too much said :). On the home screen beside the ing I have menu and view "button" view will take you to 4 screens with status data about battery voltage, rpm, throttle, break, steering, lights, and other stuff and also some chart with throttle, i will explain it later.I also have a text that is scrooling on the lcd, and a warning for low battery.

Those are the menus:

#Obtions - Headlights

Backlight

Autopilot

#Autoset-----here you just keep the joystick first at maximum throttle and hit ok, then minimum throttle, and here the program save in eprom the potentiometer values and somewhere in readControler() you will use tis values to map the throttle ( throttle = map(analogRead(A1), maxValReaded, minValReaded, 255, 0) ).

#Stearing------here you have reverse steer, steer center set with servo value to adjust the car to go straight, and steer max, how much to steer also servo values.

#Throtle set------here you have just max and min, if you use a PWM signal to control the motor set them to 255, 0 if you use a ESC you will have to se what values do you need, i think is from 89 to 189, i am not sure and depends on how is the esc programmed.

#Break set--------break min and max, if you have a servo break adjust the servo values for no break and full break else you'll also have to consult your esc manual.

#Obtion button-------for this 4 button I put for function(Autopilot, headlights, throttle divider, reverse), here you can assign to each button what function you want.

#Reset setings----- just write in eprom some default values.

Step 12: The Code - That 4 Button

The throttle joystick have about 70% of movement forward and the rest, 30% I used it for break and I needed something for reverse so i was thinking to a toggle reverse/ forward. And if I put one button, I said let's put more option:

1.Autopilot - this will keep the throttle until you hit the break, so for example, you put the throttle to 40% and you let it back to 0%, the autopilot keep the throttle to 40% and if you go again to 70%, the autopilot sets the throttle to 70%. When you hit break autopilot is disabled and throttle goes to 0%.

2.Headlight - On / Off :P

3.Divider - For this option I have 4 states, and every state will split the throttle so if you wanna go slower and with a high rate o handling you can set the throttle at 25%(you will have throttle at 25% when the joystick is at 100%).Divider options is like this, 100% divider off, 75%, 50% and 25%.

4.Reverse

For every option i have 2 stuff saved in EPROM, first one I have the button number, and the state. Saving the button nr, make it easy to change it, so if you want you can put the reverse option on first button and then change it to 3th button.

Here is how I change the button option

int EpromPozBut = 58;
lcd.clear();
for (int x = 0; x < 4; ){
readkey();
lcd.setCursor(0, 1);
lcd.print("press the key then ok");
lcd.setCursor(0, 3);
strcpy_P(buffer, (char*)pgm_read_word(&(but_set[x]))); //but_set[] stores the options name (autopilot, revrse..)
lcd.print(buffer);
lcd.setCursor(0, 4);
lcd.print("you press key");
lcd.print(but);

if (key == "ok"){ ///if you hit ok
EpromPozBut += 2; //increase with 2 (EEPROMWriteInt() saves int so needs 2 bytes to store)

EEPROMWriteInt(EpromPozBut, but); // save the but nr in eprom
x++; //increase the x (for "for" :p)


lcd.clear();
}
if (x == 4 || key == "back"){ ///exit if you finis with the 4th option or hit back
level = 1;
lcd.clear();
}

}

To activate a option you will have to check in eprom to see where you have stored the button nr.

Example:

in eprom I store it like this: 60 is the autopilot button nr and 70 is the state

62 is the headlight button nr and 72 is the state

64 is the divider button nr and 74 is the state

66 is the reverse button nr and 76 is the state

So...if you push the 3th button you will have to see where in 60, 62,64,66 is stored "3" , when you find it check the state and modify it

void buttonPress(){

if (but != 0){
int addrNrBut = 58;
int addrStateBut = 68;
for(int x = 0; x < 4; x++){ // go on all 4 eprom position
addrNrBut += 2;
addrStateBut += 2;

//////if you find the button nr on eprom 64 (throttle divider) you need to handle this in a diferent way

///////because you have 4 states

if (but == EEPROMReadInt(addrNrBut) && addrNrBut == 64){ //throttle divider
if (EEPROMReadInt(addrStateBut) < 3){
EEPROMWriteInt(addrStateBut, (EEPROMReadInt(addrStateBut) + 1) );
x = 4;
but = 0;
}
else if (EEPROMReadInt(addrStateBut) == 3){
EEPROMWriteInt(addrStateBut, 0);
x = 4;
but = 0;
}
}

/////the rest of buttons are just on / off (1/0)
if (but == EEPROMReadInt(addrNrBut) && addrNrBut != 64){
if (EEPROMReadInt(addrStateBut) == 0){
EEPROMWriteInt(addrStateBut, 1);
x = 4;
but = 0;
}
else if (EEPROMReadInt(addrStateBut) == 1){
EEPROMWriteInt(addrStateBut, 0);
x = 4;
but = 0;
}
}
}
delay(160);
}
}

And one more thing at those 4 buttons, the led indicators. Because i can change the option of one button, is a bit tricky :

void ledUpdate(){
int state = 68;
int adr = 58;
for(int x = 0; x < 4; x++){
state += 2;
adr += 2;
if(EEPROMReadInt(state) == 0){digitalWrite(led[EEPROMReadInt(adr)], LOW); }
else if(EEPROMReadInt(state) != 0){digitalWrite(led[EEPROMReadInt(adr)], HIGH); }
}

}

Step 13: The Code - Joysticks

This is one easy part I just use the map function to modify an int according to potentiometer values. On map function I use the max and min on each potentiometers that I store in Eprom with Autoset option. So for throttle is something like this:

throttle = map (th_pot, EEPROMReadInt(20), EEPROMReadInt(18), EEPROMReadInt(40), EEPROMReadInt(42));

th pot is the analog value read from analog pin.

Eprom adresses: 20 - the max throttle, potentiometer value

18 - the min throttle, potentiometer value

40 - the max throttle, PWM or ESC value

42 - the min throttle, PWM or ESC value

Also here you will have to process the autopilot, divider and reverse steer.

if (EEPROMReadInt(70) == 1) { //if autopilot is on
if (throttle > count_throttle){
count_throttle = throttle;
}
if (throttle < count_throttle){
throttle = count_throttle;
}
if (break_ < (EEPROMReadInt(50) - 5)){ //if hit break set throttle 0, autopilot off
count_throttle = 0;
throttle = 0;
EEPROMWriteInt(70, 0);
}
}

//th divider
switch (EEPROMReadInt(74)) {
case 1:
throttle *= 0.25;
break;
case 2:
throttle *= 0.5;
break;
case 3:
throttle *= 0.75;
break;
}

Step 14: The Code - Sending/Reciveing Serial Data

First time I was thinking to send a big string, something like "start-th110br55st80rev1light0-end" and in the other aduino to decode and check if the string is corect like, if it starts with "start", ends with "end" and if it haves the "th", "st"... inside. But i get lazy and I just use the Serial.parseInt().

I send it normal, with serial.print() and after sending one value I send a ","(coma). The things you need to send from remote are: throttle, break, steer, reverse, light. And you need to recive from the car: rpm, logic battery, motor battery

void reciveData(){
if (Serial.available() > 0) {
int x = Serial.parseInt();
if (x>1){(car_bat_motor = x/100.00)-1.00;}
x = Serial.parseInt();
if (x>1){(car_bat_controler = x/100.00)-1.00;}
x = Serial.parseInt();
if (x>1){RPM = x-1;}

if(x == (RPM + 1)){
for(;Serial.available();){
char y = Serial.read();
}
}
recived = 1;
}
if (recived){
sendData();
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void sendData(){

Serial.print("t");
Serial.print(throttle+1);

Serial.print("b");
Serial.print(break_+1);

Serial.print("s");
Serial.print(stear+1);

Serial.print("r");
Serial.print(EEPROMReadInt(76)+1);

Serial.print("l");
Serial.print(EEPROMReadInt(72)+1);

Serial.println("End");

recived = 0;

}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

I put the sendData() in the remote code and also in the receiver code once in void setup() and then in the loop send data only if it receive first. Like this if you have the car receiver power is off and remote is on, the remote won't send data until you power on the receiver.

Also when sending float I prefer to send it like a int(7.22 v I send it like this Serial.print(voltage *100)) and when I receive it I devide it to 100. And when I send it I add 1 (+1) like this I do not sent "0" so parseInt() won't go crazy, when I receive it I substract 1 back :P (-1).

Step 15: The Code - Reciver

The receiver code just receive the data and put the values to servos, esc or pwm th driver, lights and stuff.

Here I made a noSignal () to protect the car in case if the remote stops or run out of battery, or just lose the signal. The noSignal() put the steer straight and breaks the car and also start flashing the headlights. noSignal() enter when the time from the last value received is bigger then 800 milliseconds.


void loop() {

reciveData();
timeRcv = millis() - lastTimeRcv;
//Serial.println(timeRcv);
if (timeRcv < 1000){

RPM = (throttle/10)*100; /////

digitalWrite(headLight, light);
stearServo.write(stear);
breakServo.write(break_);
if(reverse == 0){
analogWrite(thForward, throttle);
analogWrite(thReverse, 0);
}
else if (reverse ==1){
analogWrite(thReverse, throttle);
analogWrite(thForward, 0);
}

}
else{
noSignal();
Serial.println(timeRcv);
}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void noSignal(){

analogWrite(thForward, 0);
analogWrite(thReverse, 0);
breakServo.write(65); //servo val -- full break
stearServo.write(108); //servo val -- center

Serial.print("NoSignal");

unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (ledState == 0){
ledState = 1;
}
else{
ledState = 0;
}
digitalWrite(headLight, ledState);
}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void reciveData(){

while (Serial.available() > 0){

int x = Serial.parseInt();
if(x > 0){throttle = x-1;}
x = Serial.parseInt();
if (x > 0){break_ = x-1;}
x = Serial.parseInt();
if (x > 0){stear = x-1;}
x = Serial.parseInt();
if (x > 0){reverse = x-1;}
x = Serial.parseInt();
if (x > 0){light = x-1;}
delay(5);
if(x == (light + 1)){
//Serial.println("end if");
for(;Serial.available();){
char y = Serial.read();
}
}
delay(5);
recived = 1;
lastTimeRcv = millis();
}
if (recived){
sendData();
}
}

Step 16: To Do

First time I need the communication module

Then make some cool pcb because what I have now is looking like shit

A paint job.

And at the code I need to break the car if is running and I push reverse, this is not good for the motor.

You can do alot of things with it, make a ABS system by reading the rpm at at least 2 wheels (left front, right rear) or other cool stuff

Here you have the ino files, you can find it on google drive here

Here you have again the link to nokia 1100 lcd library

Step 17: Update :) Serial Comunication Protocol

Because the connection part of my code was not so good and i seen that the car receive just 1 from 5 packet of data that remote send it i tryd to make some sort of protocol so the car and remote send requests and give feedback if the data was complete or stuff like that and also check the connection status and try to reconnect if disconnected.

On the hardware part i added 3 led on the remote, one indicate the connection status and the other 2 when sending or receive data, the leds are connected to 3 digital pins

Remote side connect function:

void connect(){
if(Serial.available() > 0){ byte first = Serial.read(); if(first == request_connect){ Serial.flush(); Serial.write(ok); connect_accepted = 1; } if (first == request_data && connect_accepted == 1){ connected = 1; connect_accepted = 0; Send_data(); } } }

Receiver(car) side connect function:

void connect(){
if(Serial.available() > 0){ if(Serial.read() == ok){ connected = 1; delay(10); Serial.write(request_data); Serial.flush(); } } else{ delay(100); Serial.write(request_connect); } }

As you can see the car send the request_connect until the remote sends an "ok" then the car sets the connection and send data request. The remote just wait the request_connect and delete the rest of Serial data that may be available (because the car just send request_data every 100ms until it gets the ok) , send the "ok" and sets connect_accepted true so, just only when it receive the request_data sets the connection true and start sending data.

Remote side connection function

void communication(){
if (connected){digitalWrite(ledConnected,HIGH);} //light up the connec led else {digitalWrite(ledConnected, LOW);} if (running){ //running, you can set it to false when you make some setins in the remote menus if(!connected){ last_time_recived = millis(); connect(); } if(connected){ if (Serial.available() > 0){ int first_byte = Serial.read(); lcd.setCursor(0,5); //debug lcd.print(first_byte); //debug switch (first_byte) { case request_data: last_time_recived = millis(); Send_data(); digitalWrite(ledSend, HIGH); //light up the send led

break; case incomming: last_time_recived = millis(); Recive_data(); digitalWrite(ledRecive, HIGH); //light up the receive led

break; case recived_ok: //case you get a recive ok feedback send request data last_time_recived = millis(); Serial.write(request_data);

break; case err_max_packet_overflow: //case the car say you send too many bytes last_time_recived = millis(); Serial.flush(); lcd.setCursor(0,5); //debug lcd.print("RRerr_max_packet"); //debug Send_data(); break; case err_data_overflow: //case the car say you send too many values

last_time_recived = millis(); Serial.flush(); lcd.setCursor(0,6); //debug lcd.print("RRerr_data_"); //debug Send_data(); break; default: Serial.flush(); } } } } }

The receiver communication() is almost the same, I'll put the entire code in a file.

Remote side send_data()

void Send_data(){
Serial.write(incomming); // send a head's up so the car can go to recive_data() Serial.write(nr_data_send); //nr of data to send (if more it will send a too many values error) Serial.write(throttle); // sending the values Serial.write(break_); Serial.write(stear); Serial.write(EEPROMReadInt(76)); // read from the eprom the value Serial.write(EEPROMReadInt(72)); }

Remote side receive_data()

void Recive_data(){
if(Serial.available() > 0){ while (Serial.available() > 0) { packets[Cur_Packet_Index++] = Serial.read(); //saveing the data in an array the array must be

delay(2); //long enough to store all the data

if (Cur_Packet_Index >= MaxPacketNum){ //send too many byte error Serial.write(err_max_packet_overflow); Serial.flush(); lcd.setCursor(0,5); //debug lcd.print("err_max_packet"); //debug Cur_Packet_Index = 0; break; } } if(packets[0] != Cur_Packet_Index - 1){ //send too many values error Serial.write(err_data_overflow); Serial.flush(); lcd.setCursor(0,6); //debug lcd.print("err_data_"); //debug } else{ //save the values if no error car_bat_motor = packets[1]; car_bat_controler = packets[2]; RPM = packets[3]; last_time_recived = millis(); // set the time reference for time out connection Serial.write(recived_ok); } Cur_Packet_Index = 0; } }

I put here the remote and the car files, updated and also just the communication files for receiver an remote, and you can find all of them on google drive here

Microcontroller Contest

Participated in the
Microcontroller Contest