Thursday, June 30, 2016

Testing Arduino_Motor_Shield_V1 with Intel Edison


OVERVIEW

In this post I am going to explain how I’ve achieved to control the motor shield with Intel Edison by SSH.The result is a little car controlled by the keyboard of my laptop.

Figure 1: Test Prototype for Motor Shield V1 and Intel Edison

INTRODUCTION

In summer begins my holidays and I need projects to spend my free time. Electronics is to me like a Playstation and a new project to me is like a new video game. So, looking for things in my storage room I found a 20 years old RC car broken. The chassis is in good condition and I thought in restore it. But I needed something new to do because I had already done RC cars controlled by radiofrequency and Bluetooth. Then, I remembered that I had an Intel Edison unworn, so voilà, I will make a Wi-Fi Car. The picture below show the car I want to control using an Intel Edison.

Figure 2: Stá tó wapo! I know
I took my Adafruit Motor Shield V1 in order to control the toy’s motor but looking for a compatible library in Internet, the people of some forums said that this shield is incompatible with Edison, oooh! Furthermore, this product is discontinued by Adafruit with the result that no one has made the effort to write a library for Edison. I visited https://www.adafruit.com/product/81 and I found this schematic:

Figure 3: Schematic of Motor Shield V1 for Arduino
I realized that if I am capable of control the PWM signal and the 74HC595 shift register, I will be able to control the motor shield. For this reason, the previous post were about PWM, 74HC595 and Intel Edison. In addition, I can find a lot of resources in internet such as PWM examples, Arduino libraries or C++ codes for 74HC595, therefore, the task will not be so difficult.

Figure 4: My Chinese version of Adafruit Motor Shield V1
The prototype presented here is a little toy car controlled by SSH over Wi-Fi using the Intel Edison prototyping board, the previously commented motor shield and a car chassis. My aim is to prepare the necessary electronics to transform then my RC-Car in a Wi-Fi Car.
     

COMPONENTS

1 x Motor Shield V1 designed by Adafruit and excellently copied by Chinese manufactures.
1 x Intel Edison prototyping board
2 x red LEDs
2 x 330 Ohms
1 x toy car chassis with: 2 wheels, two motors, a crazy wheel and the structure with their screws and nuts
7 x 1.5V batteries AA 860mAh
1 x battery holder 1x4
1 x battery holder 1x3
1 x RC barrel connector
Cables, double-sided tape, insulating tape, …

ASSEMBLY


Figure 5: the toy car from the top
1.     Mount the car chassis. Example: https://www.youtube.com/watch?v=gCQ6A9u5lqA
2.     Stick the battery holders on the chassis with double-side tape and place the batteries.
3.     Put the Motor Shield on the Intel Edison board and fix the circuits with some screws to the chassis.
4.     Connect the motor wires to the connectors M3 and M4
5.     Made a very simple LED circuits and connect the resulting four wires (5V, GND, LED right, LED left) to the servo pins that the Motor Shield have in a corner. There, you can find six pins where 2 pins are GND, the others two pins are VCC, the pin SERV1 is connected to the pin10 of Intel Edison and SERV2 is connected to the pin 9. Please, see the schematic of Figure 3.
6.     Start SSH between Intel Edison and your computer. Example:
7.    Create a file with the code that you can find at the end of this article. I like to write the programs using Microsoft Visual Studio and I send the files to Intel Edison using WinSCP. Example:
8.      Compile the code. Example: g++ -lmraa -o YourCode YourCode.cpp
9.    Then you have to make a special wire that connect the batteries to the power Jack connector of the Intel Edison and the EXT_PWR connector of the Motor Shield.
10.   Finally, run the code: ./YourCode
Figure 6: This is the Motor Shield Schematic with the external connections in the car such as the batteries, the LEDs and he jack to INtel edison

Then, the Intel Edison program is waiting for the key command. Here, the valid commands are: ‘q’, ’w’, ’e’, ’a’, ’s’, ’d’, ’g’, ’z’, ’x’, ’c’, ’ ‘, ’1’, ’2’, ’3’, ’4’.


Figure 6: Putty window with the program running on Edison

 

RESULT

The result is a little toy car that moves or stop when received a key command by SSH. The car is extremely difficult to control it  because the two motor don’t make the same torque to the wheels. If I send the ‘w’ command, the car must go straight ahead, however the car goes forward doing a little curve. The same happens when I select reverse with the key ‘s’. I always must be very fast pressing the SPACE command in order to avoid a collision.  

I have configured 4 speeds. I can select the different speeds by sending ‘1’, ‘2’, ‘3’ or ‘4’. It is easy to know if the car has changed the speed because if you pay attention, you will listen to the motors that they make different sounds.

The keys ‘q’, ‘e’, ‘z, ‘c’ provokes a rotary move by moving only one of the two wheel in one direction or the other.

To conclude, ‘a’, ‘d’ turns left or right the car by moving their wheels in opposite directions.

Figure 7: Another photo of the car
      


NEXT STEPS

-Add sounds to the car with a buzzer but creating a new thread in the C++ code.
-Control the car by WiFi but the car and the remote controller must be in differents networks.
-Adapt this electronic for my old RC car.
   

C++ Intel Edison test Code

/*     ---------------------------------------------------------
*     |  Intel Edison C++ Example Code                         |
*     |   PWM EXAMPLE                                         |
*     ---------------------------------------------------------
* the motor shield with Intel Edison by SSH
* Makefile: g++ -lmraa –o MOTOR MOTOR.cpp
* ./MOTOR
*/
#ilude <iostream>
#include <stdio.h>
#incnclude <signal.h>
#include <unistd.h>
#include <mraa.h>
#include <mraa/pwm.h>
#include <mraa/gpio.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
//74HC595 pins of the Motor Shield V1
#define PWM0A       6 //L293D IC2 1-2EN
#define PWM0B       5 //L293D IC2 3-4EN
//Servo pins of the Motor Shield V1
#define PWM1A       9 //SERVO_1
#define PWM1B       10//SERVO_2
//74HC595 pins of the Motor Shield V1
#define PWM2A       11//L293D IC1 1-2EN
#define PWM2B       3 //L293D IC1 3-4EN

//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//2 //QC
#define MOTOR1_B 3//3 //QD
#define MOTOR2_A 1//1 //QB
#define MOTOR2_B 4//4 //QE
#define MOTOR4_A 5//0 //QF
#define MOTOR4_B 7//6 //QH
#define MOTOR3_A 0//5 //QA
#define MOTOR3_B 6//7 //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 SPEED1   1
#define SPEED2   0.9
#define SPEED3   0.8
#define SPEED4   0.7

using namespace std;

static uint8_t latch_state=0;//GLobal variable

class Motor_Control
{
public:
      
       // No parameters need to be passed to the constructor.
       Motor_Control();
       mraa_pwm_context PWM_3_pin;
       mraa_pwm_context PWM_5_pin;
       mraa_pwm_context PWM_6_pin;
       mraa_pwm_context PWM_9_pin;
       mraa_gpio_context GPIO_10_pin;
       mraa_gpio_context GPIO_11_pin;
       int SetSpeed(mraa_pwm_context, float);
       int SetSpeed(mraa_gpio_context, bool);
       void run(uint8_t , uint8_t );
       void standby(bool );
private:
       mraa_gpio_context data_pin;
       mraa_gpio_context clock_pin;
       mraa_gpio_context latch_pin;
       mraa_gpio_context enable_pin;

       void shiftOut(mraa_gpio_context dataPin, mraa_gpio_context clockPin, uint8_t bitOrder, uint8_t val);
       void _74HC595();
       int check_Initialization();
       void errors(int);
};

/* 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);

       //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(PWM1A);

       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,1);
       SetSpeed(PWM_5_pin,0.8);
       SetSpeed(PWM_6_pin,0.8);
       SetSpeed(PWM_9_pin,1);

       //Initializing GPIO pins of the Motor Shield which PWM cannnot be configure
       GPIO_10_pin = mraa_gpio_init(PWM1B);
       GPIO_11_pin = mraa_gpio_init(PWM2A);

       mraa_gpio_dir(GPIO_10_pin, MRAA_GPIO_OUT);
       mraa_gpio_dir(GPIO_11_pin, MRAA_GPIO_OUT);

       SetSpeed(GPIO_10_pin, ON);
       SetSpeed(GPIO_11_pin, ON);
       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 registe
       mraa_gpio_write(latch_pin, HIGH); //Pulls the latch high displaying the data

       // The constructor disables the outputs of the PWM by asserting the standby
       //  pin on the controller. You *must* use the standby() function to enable
       //  them before proceeding!
       standby(ON);//Disable Motor Shield
}

/* 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_10_pin == NULL)
             error = 10;
       if (GPIO_11_pin == NULL)
             error = 11;
       if (latch_pin == NULL)
             error = 12;
       return error;
}

/* errors()
 * List of mesages 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 2:

                    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 (PWM1A)=SERVO_2...\n";
                    break;
             case 10:
                    cout << "Error in GPIO_10_pin (PWM2B)=SERVO_1...\n";
                    break;
             case 11:
                    cout << "Error in GPIO_11_pin (PWM0B)=IC2 1-2EN...\n";
                    break;
             case 12:
                    cout << "Error in latch_pin 74HC595...\n";
                    break;
             case 13:
                    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;
   
       }
}

/* stanby()
*  When stanby is OFF, the PWM signal worksn and the swift register is enabled
*  Otherwise, nothings will work.
*  Motor_bontrol.stanby(OFF) : PWM and 74HC595 work
*  Motor_bontrol.stanby(ON)  : PWM and 74HC595 don't work
*/
void Motor_Control::standby(bool OnOFF)
{
       mraa_pwm_enable(PWM_3_pin,OnOFF); //(PWM2B)=IC1 1-2EN

       mraa_pwm_enable(PWM_5_pin,OnOFF); //(PWM0B)=IC2 1-2EN

       mraa_pwm_enable(PWM_6_pin,OnOFF); //(PWM0A))=IC2 3-4EN

       mraa_pwm_enable(PWM_9_pin,OnOFF); //(PWM1A)=SERVO_2

       mraa_gpio_write(GPIO_10_pin, OnOFF); //(PWM2B)=SERVO_1

       mraa_gpio_write(GPIO_11_pin, OnOFF); //(PWM0B)=IC2 1-2EN =0

       mraa_gpio_write(enable_pin, 0); //It is enabled with a LOW volt level

}

/* 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)
       {
             mraa_pwm_write(N_Motor, duty);
             mraa_pwm_enable(N_Motor, 0);
       }
       else
             mraa_pwm_enable(N_Motor, 1);
             error=mraa_pwm_write(N_Motor, duty);
       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);
       }
}

sig_atomic_t volatile isrunning = 1;
void sig_handler(int signum);

int main()
{
       signal(SIGINT, &sig_handler);

       Motor_Control motor;//contrutor of the class Motor_Control

       cout << "Press any 'g' and then Enter to exit from the aplication..." << endl;
       while (isrunning)
       {
             char c = cin.get();
             switch (c)
             {
             case 'w'://STRAIGHT
                    motor.SetSpeed(motor.PWM_9_pin, 1);//Connected to LED
                    motor.SetSpeed(motor.GPIO_10_pin, 1);//Connected to LED
                    motor.run(Motor3, FORWARD);
                    motor.run(Motor4, BACKWARD);
                    break;
             case 's'://REVERSE
                    motor.SetSpeed(motor.PWM_9_pin, 1);//Connected to LED
                    motor.SetSpeed(motor.GPIO_10_pin, 1);//Connected to LED
                    motor.run(Motor3, BACKWARD);
                    motor.run(Motor4, FORWARD);
                    break;
             case 'a'://TURN LEFT
                    motor.SetSpeed(motor.PWM_9_pin, 0);//Connected to LED
                    motor.SetSpeed(motor.GPIO_10_pin, 1);//Connected to LED
                    motor.run(Motor3, BACKWARD);
                    motor.run(Motor4, BACKWARD);
                    break;
             case 'd'://TURN RIGHT
                    motor.SetSpeed(motor.PWM_9_pin, 1);//Connected to LED
                    motor.SetSpeed(motor.GPIO_10_pin, 0);//Connected to LED
                    motor.run(Motor3, FORWARD);
                    motor.run(Motor4, FORWARD);
                    break;
             case 'q'://ROTARY MOVE
                    motor.SetSpeed(motor.PWM_9_pin, 1);//Connected to LED
                    motor.SetSpeed(motor.GPIO_10_pin, 1);//Connected to LED
                    motor.run(Motor3, FORWARD);
                    motor.run(Motor4, STOP);
                    break;
             case 'e'://ROTARY MOVE
                    motor.SetSpeed(motor.PWM_9_pin, 1);//Connected to LED
                    motor.SetSpeed(motor.GPIO_10_pin, 1);//Connected to LED
                    motor.run(Motor3, BACKWARD);
                    motor.run(Motor4, STOP);
                    break;
             case 'z'://ROTARY MOVE
                    motor.SetSpeed(motor.PWM_9_pin, 1);//Connected to LED
                    motor.SetSpeed(motor.GPIO_10_pin, 1);//Connected to LED
                    motor.run(Motor4, FORWARD);
                    motor.run(Motor3, STOP);
                    break;
             case 'c'://ROTARY MOVE
                    motor.SetSpeed(motor.PWM_9_pin, 1);//Connected to LED
                    motor.SetSpeed(motor.GPIO_10_pin, 1);//Connected to LED
                    motor.run(Motor4, BACKWARD);
                    motor.run(Motor3, STOP);
                    break;
             case '1'://SET SPEED 100%
                    motor.SetSpeed(motor.PWM_5_pin, SPEED1);
                    motor.SetSpeed(motor.PWM_6_pin, SPEED1);
                    break;
             case '2'://SET SPEED 90%
                    motor.SetSpeed(motor.PWM_5_pin, SPEED2);
                    motor.SetSpeed(motor.PWM_6_pin, SPEED2);
                    break;
             case '3'://SET SPEED 80%
                    motor.SetSpeed(motor.PWM_5_pin, SPEED3);
                    motor.SetSpeed(motor.PWM_6_pin, SPEED3);
                    break;
             case '4'://SET SPEED 70%
                    motor.SetSpeed(motor.PWM_5_pin, SPEED4);
                    motor.SetSpeed(motor.PWM_6_pin, SPEED4);
                    break;
             case ' '://STOP
                    motor.SetSpeed(motor.PWM_9_pin, 0);//Connected to LED
                    motor.SetSpeed(motor.GPIO_10_pin, 0);//Connected to LED
                    motor.run(Motor4, STOP);
                    motor.run(Motor3, STOP);
                    break;
             case 'g':
                    return 0;
                    break;
             }
       }
       motor.standby(OFF);
       cout << "\nClosing application..." << endl;
       sleep(1);
       return MRAA_SUCCESS;
}

void sig_handler(int signum)
{     
       if (signum == SIGINT) isrunning = 0;
}