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
|
RESULTS
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;
}