RC_CAR TRANSFORMED IN Wi-Fi_CAR
OVERVIEW
In this project I have transformed an old
broken toy car controlled by radio-control into a toy car controlled by Wi-Fi
using SSH in the same local network.
Figure 1: my old broken radio-control car. |
The hardware of this toy is an Intel Edison
development board with the Adafruit Motor Shield V1 powered by 7x1.5 AAA
batteries. In addition to this, I have added two LED and a buzzer that they
also can be controlled by SSH. The buzzer has been configured with a police
siren.
This project is the continuation of a previous
work with the Adafruit motor shield v1: http://vellamy.blogspot.com.es/2016/06/testing-arduinomotorshieldv1-with-intel.html
INTRODUCTION
This toy has two DC motors. One controls the
“traction” and the other controls the “steering”. In other words, one motor is
for going forward and backward and the other for turning left and right. The
traction is wired to the M3 connector and the steering is wired to M4 connector
of the Motor Shield. These motor are originals, they are integrated inside the
plastic of the car and they are around 20 years old.
The car has three LED. One LED is always on.
This LED is in the middle of the car and it lights red and blue. The other LEDs
are the headlights. One is green and it is
controlled by the digital pin number 14. The right LED is red and controlled by
the digital output number 15. Both bright when the car is going forward and
backward. They are off if the car is stopped. If the car is turning right, only the
red led is on. Instead, when the car is turning left, only the green LED is
on. Furthermore, the ‘l’ key activate and deactivate the headlights at anytime. I programmed this option in order to test the code.
Figure 3: inside the toy you can find the Intel Edison with the Motor Shield, a buzzer and three LED. |
In addition, I have added a buzzer to the
circuit to make some noise when the ‘f’ is pressed. This key turns on/off a police
siren. This sound is produced with the PWM of the pin number 9. The buzzer is
wired through a 100 ohm resistor connected to the pin SERVO2 which is hardwired
to the pin number 9 in the Motor Shield. The sound function runs in a parallel
thread to the main thread.
The toy has two battery holders with seven AA batteries
connected in serie, so the Intel Edison and the Motor Shield are powered with
10.5V. The positive wire that comes from the battery holder is attached to
a switch that switch on/off the toy. The two boards need to be powered
independently.
To take over control of the car is necessary to
open a SSH connection between Edison and a computer or Smartphone. I use Putty
in my computer and the app called JuiceSSH in my Android device. My Intel
Edison is configured to connect to my home wifi or to the Wifi created with my
phone. So I can play with my toy everywhere…
COMPONENTS
1 x
Motor Shield V1 designed by Adafruit and excellently copied by Chinese
manufactures.
1 x
Intel Edison prototyping board
1 x red
LED
1 x
green LED
1 x
blue&red LED
3 x 330
Ohms
1 x 100
Ohms
1 x toy
car chassis with: 4 wheel and, two motors and a power switch
7 x 1.5V
batteries AA 860mAh
1 x
battery holder 1x4
1 x
battery holder 1x3
1 x
buzzer
Cables,
double-sided tape, insulating tape, …
DEVELOPMENT
I have followed this schematic to assembly all the components inside de car. The next picture is the Motor Shield V1 electric schematic in which I have added the LEDs, resistors, buzzer, etc...
Figure 5: schematic of the hardware of the toy |
Once you
are login in Intel Edison, you must go to the folder where the code is. Then,
the code needs to be compiled. In my case I have to write the following sentence:
g++ -pthread -lmraa –o rc_car rc_car.cpp Motor_Shield.cpp
g++ is the linux c++ compiler I use. –pthread is necessary because I use the
library Pthread for producing the sound in a parallel thread. –lmraa is the library of the hardware
of Intel Edison. –o no tengo ni puta idea para que sirve. rc_car is the executable
output file. In rc _car.cpp is the
main code. Finally, in Motor_Shield.cpp there
are the class that a I use to control the DC_Motors and another class to
control things like the buzzer and LEDs.
To execute
the program I write this in the command line:
./rc_car
The program
begins with a welcome message:
Welcome to my RC_car
And with
the instructions to control the toy:
Press 'a' to turn left
Press
'd' to turn right
Press
'w' to go straight ahead
Press
's' to go reverse
Press
'f' to turn on//off the claxon
Press
SPACE to stop
Press
'l' to stop and turn on/off the lights
Press
'g' and then Enter to exit from the application…
Figure 6: screenshot of the Putty connection with the Intel Edison where you can see how I compile the code, how execute it and the initial message of the car. |
RESULTS
In the video below you can see how I control the car, I move it, switch on/off the lights and play the sound.
CONCLUSIONS
Using the described hardware before and 7 AA batteries a can move and control the car, however, it has not the require power to make the toy movement faster. As you can see, the car moves slowly.
The Adafruit Motor Shield V1 is compatible with Intel Edison. Only is needed the correct C++ code.
I have restored an old broken RF-car into a new one whic h I can play using my laptop and smartphone.
FUTURE WORKS
I would
like to control this toy remotely, control the car using Internet but being the
car and the computer in different networks. To achieve this I think I need an
IOT web service and adapt the main code. I will see how to do it.
C++ FILES
I am conscious that the codes can be improved and optimized and also it could be an easier way achieve the same result, however, this time I wanted to reused the previous code I made before. I wanted to make the program using classes and this is a hobby, not professional, so maybe you can find some bugs that I didn’t realize.
rc_car.cpp
/*
* This code control the Adafruit Motor_Shield_V1
and Intel Edison
* that the have been assembly in a old rc car
*
* Makefile: g++ -pthread-lmraa –o rc_car
rc_car.cpp Motor_Shield.cpp
* ./rc_car
*/
#include "Motor_Shield.h"
using namespace std;
sig_atomic_t volatile isrunning = 1;
void sig_handler(int signum);
rc_car
Ravid;//construtor of the class Motor_Control
int main()
{
signal(SIGINT, &sig_handler);
bool flagL = true;
bool flagC = true;
sleep(1);
cout
<< "\t\tWelcome to my
RC_car\n";
cout
<< " Press 'a' to turn
left\n";
cout
<< " Press 'd' to turn
right\n";
cout
<< " Press 'w' to go
straight ahead\n";
cout
<< " Press 's' to go
reverse\n";
cout
<< " Press 'f' to turn
on//off the claxon\n";
cout
<< " Press SPACE to
stop\n";
cout
<< " Press 'l' to stop and turn on//off the lights\n";
cout
<< " Press 'g' and then
Enter to exit from the aplication...\n" << endl;
flagL
= Ravid.lights(flagL);
while (isrunning)
{
char c = cin.get();
switch (c)
{
case 'a'://TURN LEFT
Ravid.turn_left();
break;
case 'd'://TURN RIGHT
Ravid.turn_right();
break;
case 'w'://STRAIGHT
Ravid.straight();
break;
case 's'://REVERSE
Ravid.reverse();
break;
case 'f'://claxon
flagC=
Ravid.claxon(flagC);
break;
case ' '://STOP
Ravid.stop();
break;
case 'l'://light
Ravid.stop();
flagL
= Ravid.lights(flagL);
break;
case 'g'://stop everything
Ravid.stop();
Ravid.claxon(false);
Ravid.lights(false);
return 0;
break;
}
}
cout
<< "\nClosing
application..." << endl;
Ravid.claxon(false);
Ravid.remove();
pthread_exit(NULL);
sleep(1);
return MRAA_SUCCESS;
}
void sig_handler(int signum)
{
Ravid.claxon(false);
if (signum == SIGINT) isrunning = 0;
}
Motor_shield.h
#ifndef Motor_Shield_h
#define Motor_Shield_h
#include <iostream>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <mraa.h>
#include <mraa/pwm.h>
#include <mraa/gpio.h>
#include <pthread.h>
//Pin Definitions
//74HC595 pins of the Motor Shield V1
#define DIR_CLOCK 4
#define DIR_EN 7
#define DIR_SER 8
#define DIR_LATCH 12
//IC2 L293D
pins of the Motor Shield V1
#define PWM0A 6 //L293D
IC2 1-2EN
#define PWM0B 5 //L293D
IC2 3-4EN
//IC1 L293D pins of the Motor Shield V1
#define PWM2A 11//L293D
IC1 1-2EN
#define PWM2B 3 //L293D
IC1 3-4EN
//Servo pins of the Motor Shield V1
#define BUZZER 9 //SERVO_1 in the motor shield
#define LED1 15//A1 in Motor shield. LED RED
//The arduino pin 14 is also the analog pin 0,
but here is used as a digital output
#define LED2 14//A0 in Motor shield, GREEN LED
//Special commands
#define LOW 0
#define HIGH 1
#define LSBFIRST 0
#define MSBFIRST 1
#define ON 1
#define OFF 0
#define OK 0
#define NOK 1
// Bit positions in the 74HCT595 shift register
output
#define MOTOR1_A 2//QC
#define MOTOR1_B 3//QD
#define MOTOR2_A 1//QB
#define MOTOR2_B 4//QE
#define MOTOR4_A 5//QF
#define MOTOR4_B 7//QH
#define MOTOR3_A 0//QA
#define MOTOR3_B 6//QG
// Constants that the user passes in to the
motor calls
#define FORWARD 1
#define BACKWARD 2
#define STOP 3
#define Motor1 1
#define Motor2 2
#define Motor3 3
#define Motor4 4
#define SPEED0 0
#define SPEED1 1
#define SPEED2 0.9
#define SPEED3 0.8
#define SPEED4 0.7
#define SPEED5 0.5
#define SPEED05 0.05
#define NUM_THREADS 1
using namespace std;
static uint8_t latch_state = 0;//GLobal
variables
void *sound(void *threadid);
static mraa_pwm_context
PWM_9_pin;
class Motor_Control
{
public:
Motor_Control();//constructor
int SetSpeed(mraa_pwm_context, float);//change PWM duty
int SetSpeed(mraa_gpio_context, bool);// change GPIO state
void run(uint8_t, uint8_t);
protected:
mraa_pwm_context
PWM_3_pin;
mraa_pwm_context
PWM_5_pin;
mraa_pwm_context
PWM_6_pin;
mraa_gpio_context
GPIO_10_pin;
mraa_gpio_context GPIO_11_pin;
mraa_gpio_context GPIO_14_pin;
mraa_gpio_context GPIO_15_pin;
mraa_gpio_context
data_pin;
mraa_gpio_context
clock_pin;
mraa_gpio_context
latch_pin;
mraa_gpio_context
enable_pin;
private:
void shiftOut(mraa_gpio_context dataPin, mraa_gpio_context
clockPin, uint8_t bitOrder, uint8_t val);
void _74HC595();
int check_Initialization();
void errors(int);
};
class rc_car : public Motor_Control
{
public:
rc_car();//constructor
void turn_left();
void turn_right();
void straight();
void reverse();
void stop();
bool claxon(bool);
bool lights(bool);
void remove();
private:
//void sound(void*);
protected:
};
#endif // !rc_car
Motor_car.cpp
#include "Motor_Shield.h"
/* Motor_Control()--> Constructor
*
*/
Motor_Control::Motor_Control()
{
mraa_init();
//Initializing GPIO pins of the 74HC595
data_pin = mraa_gpio_init(DIR_SER);
clock_pin =
mraa_gpio_init(DIR_CLOCK);
latch_pin
= mraa_gpio_init(DIR_LATCH);
enable_pin = mraa_gpio_init(DIR_EN);
mraa_gpio_dir(data_pin,
MRAA_GPIO_OUT);
mraa_gpio_dir(clock_pin,
MRAA_GPIO_OUT);
mraa_gpio_dir(latch_pin,
MRAA_GPIO_OUT);
mraa_gpio_dir(enable_pin,
MRAA_GPIO_OUT);
mraa_gpio_write(enable_pin,
0);//active witha low level
//Initializing PWM pins
PWM_3_pin
= mraa_pwm_init(PWM2B);
PWM_5_pin
= mraa_pwm_init(PWM0B);
PWM_6_pin
= mraa_pwm_init(PWM0A);
PWM_9_pin
= mraa_pwm_init(BUZZER);
mraa_pwm_period_us(PWM_3_pin,
100);//period 100us
mraa_pwm_period_us(PWM_5_pin,
100);//period 100us
mraa_pwm_period_us(PWM_6_pin,
100);//period 100us
mraa_pwm_period_us(PWM_9_pin,
100);//period 100us
SetSpeed(PWM_3_pin,
SPEED0);
SetSpeed(PWM_5_pin,
SPEED1);
SetSpeed(PWM_6_pin,
SPEED1);
SetSpeed(PWM_9_pin,
SPEED0);
//Initializing GPIO pins of the Motor Shield
GPIO_11_pin = mraa_gpio_init(PWM2A);
GPIO_14_pin =
mraa_gpio_init(LED2);
GPIO_15_pin
= mraa_gpio_init(LED1);
mraa_gpio_dir(GPIO_15_pin,
MRAA_GPIO_OUT);
mraa_gpio_dir(GPIO_11_pin,
MRAA_GPIO_OUT);
mraa_gpio_dir(GPIO_14_pin,
MRAA_GPIO_OUT);
SetSpeed(GPIO_11_pin,
SPEED0);
SetSpeed(GPIO_14_pin,
SPEED1);
SetSpeed(GPIO_15_pin,
SPEED1);
errors(check_Initialization());
mraa_gpio_write(latch_pin,
LOW); //Pulls the chips latch low
shiftOut(data_pin,
clock_pin, MSBFIRST, 0
>> 8); //Shifts out the 8 bits to the shift register
mraa_gpio_write(latch_pin,
HIGH); //Pulls the latch high displaying
the data
}
/* speed(PWM);
* This function configure the speed of the
motors.
* This sets the PWM duty between the 0% and 100%
* Motor_Control.speed(PWM_3_pin,1.0)
*/
int Motor_Control::SetSpeed(mraa_pwm_context N_Motor, float duty)
{
int error = 0;
if (duty >
1 || duty < 0)
{
duty = 0;
error
= 20;
}
if (duty ==
0)
{
error
= mraa_pwm_write(N_Motor, duty);
mraa_pwm_enable(N_Motor, OFF);//disable PWM
}
else
{
error
= mraa_pwm_write(N_Motor, duty);
mraa_pwm_enable(N_Motor, ON);//enable PWM
}
return error;
}
/* speed(GPIO);
* This function configure the speed of the
motors
* Only has two speeds for pins 10 and 11.
*
* Motor_Control.speed(GPIO_11_pin,ON)
*/
int Motor_Control::SetSpeed(mraa_gpio_context N_Motor, bool OnOff)
{
int error = 0;
error
= mraa_gpio_write(N_Motor, OnOff);
return error;
}
void Motor_Control::run(uint8_t motornum, uint8_t action)
{
uint8_t a, b, error;
if (motornum >
4 || motornum < 0)
error
= 21;
else
{
switch (motornum)
{
case 1:
a
= 1 << MOTOR1_A; b =
1 << MOTOR1_B; break;
case 2:
a
= 1 << MOTOR2_A; b =
1 << MOTOR2_B; break;
case 3:
a
= 1 << MOTOR3_A; b =
1 << MOTOR3_B; break;
case 4:
a
= 1 << MOTOR4_A; b =
1 << MOTOR4_B; break;
default:
return;
}
}
if (action >
3 || action < 0)
error
= 22;
else
{
switch (action)
{
case FORWARD:
latch_state
|= a;//
latch_state
&= ~b;
_74HC595();
break;
case BACKWARD:
latch_state
&= ~a;
latch_state
|= b;
_74HC595();
break;
case STOP:
latch_state
&= ~a; // A
and B both low
latch_state
&= ~b;
_74HC595();
break;
}
}
}
void Motor_Control::_74HC595()
{
mraa_gpio_write(latch_pin,
LOW); //Pulls the chips latch low
shiftOut(data_pin,
clock_pin, MSBFIRST,
latch_state); //Shifts out the 8 bits to the shift register
mraa_gpio_write(latch_pin,
HIGH); //Pulls the latch high displaying
the data
}
void Motor_Control::shiftOut(mraa_gpio_context dataPin, mraa_gpio_context clockPin, uint8_t bitOrder, uint8_t val)
{
uint8_t i;
for (i = 0; i < 8; i++) {
if (bitOrder == LSBFIRST)
mraa_gpio_write(dataPin, !!(val
& (1 << i)));
else
mraa_gpio_write(dataPin, !!(val
& (1 << (7 - i))));
mraa_gpio_write(clockPin, HIGH);
mraa_gpio_write(clockPin, LOW);
}
}
/* check_Initialization()
* This function checks every pin initializated
before. If there is a problem,
* it will return an integer attached to the last
problem detected
* Motor_Control.check_Initialization();
*/
int Motor_Control::check_Initialization()
{
int error = 0;//If Error=0,
there are any problems.Otherwise, something has happened in the initilization
if (PWM_3_pin == NULL)
error
= 3;
if (clock_pin == NULL)
error
= 4;
if (PWM_5_pin == NULL)
error
= 5;
if (PWM_6_pin == NULL)
error
= 6;
if (enable_pin == NULL)
error
= 7;
if (data_pin == NULL)
error
= 8;
if (PWM_9_pin == NULL)
error = 9;
if
(GPIO_11_pin == NULL)
error = 11;
if
(GPIO_14_pin == NULL)
error = 14;
if
(GPIO_15_pin == NULL)
error = 15;
if
(latch_pin == NULL)
error
= 12;
return error;
}
/* errors()
* List of messages of posible errors
* Motor_Control.errors();
*/
void Motor_Control::errors(int error)
{
switch (error)
{
case 0:
cout
<< "OK... \n";
break;
case 1:
cout
<< "NOK!!! \n";
break;
case 3:
cout
<< "Error in PWM_3_pin
(PWM2B)=IC1 1-2EN...\n";
break;
case 4:
cout
<< "Error in clock_pin
74HC595...\n";
break;
case 5:
cout
<< "Error in PWM_5_pin
(PWM0B)=IC2 1-2EN...\n";
break;
case 6:
cout
<< "Error in PWM_6_pin
(PWM0A))=IC2 3-4EN...\n";
break;
case 7:
cout
<< "Error in enable_pin
74HC595...\n";
break;
case 8:
cout
<< "Error in data_pin
74HC595...\n";
break;
case 9:
cout
<< "Error in
PWM_9_pin BUZZER...\n";
break;
case 11:
cout
<< "Error in GPIO_11_pin
(PWM0B)=IC2 1-2EN...\n";
break;
case 14:
cout
<< "Error in GPIO_14_pin
LED2...\n";
break;
case 15:
cout
<< "Error in GPIO_15_pin
LED1...\n";
break;
case 12:
cout
<< "Error in latch_pin
74HC595...\n";
break;
case 20:
cout
<< "Motor Speed is
incorrect. Speed must be between 0.0 and 1.0\n";
break;
case 21:
cout
<< "Motor Selection is
incorrect. The motor number must be 1,2,3 or 4\n";
break;
case 22:
cout
<< "Motor action is
incorrect. The motor number must be 1,2 or 3 \n";
break;
default:
break;
}
}
void rc_car::turn_left()
{
SetSpeed(GPIO_14_pin,
ON);//Connected to LED1
SetSpeed(GPIO_15_pin,
OFF);//Connected to LED2
run(Motor3, FORWARD);
}
void rc_car::turn_right()
{
SetSpeed(GPIO_14_pin,
OFF);//Connected to LED1
SetSpeed(GPIO_15_pin,
ON);//Connected to LED2
run(Motor3, BACKWARD);
}
void rc_car::straight()
{
SetSpeed(GPIO_14_pin,
ON);//Connected to LED1
SetSpeed(GPIO_15_pin,
ON);//Connected to LED2
run(Motor4, BACKWARD);
}
void rc_car::reverse()
{
SetSpeed(GPIO_14_pin,
ON);//Connected to LED1
SetSpeed(GPIO_15_pin,
ON);//Connected to LED2
run(Motor4, FORWARD);
}
void rc_car::stop()
{
SetSpeed(GPIO_15_pin,
OFF);//Connected to LED1
SetSpeed(GPIO_14_pin,
OFF);//Connected to LED2
run(Motor3, STOP);
run(Motor4, STOP);
}
bool rc_car::lights(bool flagL)
{
if (flagL == true)
{
SetSpeed(GPIO_14_pin,
ON);//Connected to LED1
SetSpeed(GPIO_15_pin,
ON);//Connected to LED2
flagL = false;
}
else if (flagL == false)
{
SetSpeed(GPIO_14_pin,
OFF);//Connected to LED1
SetSpeed(GPIO_15_pin,
OFF);//Connected to LED2
flagL = true;
}
return flagL;
}
bool rc_car::claxon(bool flagg)
{
if (flagg)
{
SetSpeed(PWM_9_pin,
SPEED5);
flagg = false;
}
else
{//I repeat this sentences three times
SetSpeed(PWM_9_pin,
SPEED0);
SetSpeed(PWM_9_pin,
SPEED0);
SetSpeed(PWM_9_pin,
SPEED0);
flagg = true;
}
return flagg;
}
rc_car::rc_car()//constructor
{
int i = 0, rc;
//Set up thread of sound
pthread_t
threads[NUM_THREADS];
rc =
pthread_create(&threads[i], NULL,
sound, (void *)i);
if (rc)
{
cout
<< "Error:unable to create
thread," << rc << endl;
exit(-1);
}
}
void rc_car::remove()//destroy thread
{
pthread_cancel(3);
}
void *sound(void *threadid)
{
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
while (1)
{
pthread_testcancel();
mraa_pwm_period_us(PWM_9_pin,
1031);//(1.0 / 969.0)
usleep(500000);
mraa_pwm_period_us(PWM_9_pin,
1250);//(1.0 / 800.0)
usleep(500000);
}
pthread_exit(NULL);
}