Wi-Fi Car with Intel Edison


  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
Figure 2: wi-fi car in action
     

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.

Figure 4: Battery holders under the car and the swicht
  
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);
}