Hacking a RC Car – MakeICT Workshop

Transcription

Hacking a RC Car – MakeICT Workshop
By: Ivan R Quiroz
Hacking a RC Car – MakeICT Workshop
1 PROJECT GOAL
Hack a remote control car as a testing robot platform. The use of a differential drive RC car allows 360
turning. Servo driven sonar detects obstacles in front of robot. Arduino provides the brains to move the
motors as well as swipe the environment with the sonar. The robot will be controlled via the original Hbridge that comes with the RC car, with the controlled lines hacked into the Arduino for control purposes.
•
•
Main Goal: Have a mobile platform with a sonar sensor
Time permits: Incorporate Servo and IR sensor into design
2 WORKSHOP AGENDA
•
•
•
•
•
•
•
•
•
•
•
•
Introduction and setup
Power connections
H-bridge description
Hacking RC toy, find H-bridge
Connecting to Arduino
Arduino programming
Sonar description
Connecting the Sonar
Programming the Sonar
Adjusting Sonar to H-bridge
IR Sensor and Servo (Appendix)
Closing
(15 min)
(30 min)
(15 min)
(45 min)
(15 min)
(30 min)
(10 min)
(15 min)
(30 min)
(30 min)
(30 min)
(15 min)
Total time: 4hr 40 min (1PM – 5:20PM)
3 TOOLS NEEDED
In order to accomplish the hack we will need the following tools
•
•
•
•
•
•
•
•
Soldering Iron and Solder
Solder wick and/or sucker
Scrap Cables
Wire stripper and cutters
Velcro and/or wire ties
Hot glue gun
Screws and nuts
Cordless drill
By: Ivan R Quiroz
4 MATERIALS NEEDED
•
•
Thunder Tumbler Remote Control Car – or equivalent (differential wheels)
o
http://www.bedbathandbeyond.com/product.asp?sku=16903540&
($5.99 + free shipping)
Arduino Nano (recommended due to size)
o
http://www.ebay.com/itm/New-Nano-size-V3-ATmega328P-AU-Micro-controller-with-PINS-Arduino-compatible/300851625159?pt=LH_DefaultDomain_0&hash=item460c277cc7
•
Sonar Sensor (HC-SR04 based)
o
http://www.ebay.com/itm/1pcs-New-Arduino-Ultrasonic-Module-HC-SR04-Distance-Measuring-Transducer-Sensor/121027295838?pt=LH_DefaultDomain_0&hash=item1c2dc9fa5e
•
($12.79 + free shipping)
($2.17 + free shipping)
Qty:6 x 1N5817 Diodes – Noise suppression $2
o
http://www.digikey.com/product-detail/en/1N5817-E3%2F54/1N5817-E3%2F54GICT-ND/1023533
($3.00 + shipping)
•
Qty: 5 Capacitors – Noise Isolation $2
o
•
http://www.digikey.com/product-detail/en/ECA-1VM100/P5161-ND/245020
Batteries, Wire, - $6
($1.00 + shipping)
($6 + free shipping)
Optional:
• Servo Motor http://www.hobbyking.com/hobbyking/store/__34112__HK15178_Analog_Servo_10g_1_4kg_0_09s_USA_Warehouse_.html
($3.00 + shipping)
•
Li-ion rechargeable batteries - $5
http://www.hobbyking.com/hobbyking/store/__20820__Turnigy_1000mAh_2S_30C_Lipo_Pack_USA_Warehouse_.html
($6.54 + shipping)
•
IR Sensor (Sharp GP2D120)
o
•
($14.50 + 4.95 shipping)
($4.49 + shipping)
Total = $30.92 ($50 with optional items) +Shipping
http://www.acroname.com/robotics/parts/R146-GP2D120.html
Li-Ion charger - $5
By: Ivan R Quiroz
5 BLOCK DIAGRAM
6V BATTERY (4- AAA)
D9
D10
M1_PWM
M1_PWM
H-BRIDGE
PIN6
PIN7
D11
D3
M2_PWM
M2_PWM
PIN10
PIN11
+5V
SIGNAL
GROUND
SERVO
(Optional)
A0
+5V
SIGNAL
GROUND
IR SENSOR
(Optional)
D2
D12
+5V
TRIG
ECHO
GROUND
ARDUINO
GREEN
M3
WHITE
M4
ML
LEFT
BLACK
M2
M1 RED
MR
RIGHT
SONAR
6 HARDWARE BUILDING BLOCKS
BLOCKS
6.1 PULSE WIDTH MODULATION (PWM)
PWM is a fancy word to describe the turning ON and OFF a signal, with the attempt to control the speed of a
motor. PWM is also used to control the intensity of a
LED. It works by turning OFF and ON the signal by a
defined amount of time (PERIOD). The ratio of ON vs
OFF in the PERIOD defines the speed of a motor or the
intensity of an LED. The longer the ON time compared
to the OFF time the faster the motor goes or the
brighter the LED is. So if we turn ON the signal 80%
while OFF 20% of the time intervals that we pick then
the motor will see 80% of the power on average. On the
same token if we turn ON the signal 10% and OFF 90%
of the time then the motor effectively will see 10% of
the power on average.
Graphically it looks as following:
(source: http://arduino.cc/en/Tutorial/PWM)
By: Ivan R Quiroz
6.1.1 Frequency
Each of the vertical lines show the period of time. This period is refer to in terms of frequency (calculated
1/PERIOD). Frequency is defined in the term of Hertz. If you do 1 push up per second you are doing push-ups
at a frequency 1Hz. If you do 10 push-ups per second, you are doing push-ups at a frequency of 10Hz. Why is
this important? Because that is the speed at which we will be turning ON and OFF the signal to the motors.
We would normally pick 100Hz-1000Hz for small motors.
6.1.2 Duty Cycle
So once we defined how fast we are switching the signals ON and OFF, we need to pick the ratio of ON and
OFF (Duty Cycle). Back to the push-up the amount you are up vs the amount you spend down resting is what
is used to define the duty cycle. For example, if we apply a signal to the motor for 2sec and OFF for 9 sec,
then ON for 2 sec and OFF for 8 sec, then ON for 2sec OFF for 8…etc, we have a PERIOD of 8+2 = 10 sec. The
frequency = 1/10 sec = 0.1Hz. The ON Duty Cycle is 20% (2sec/10sec), so the motor sees only 20% of the
power, equates to run slow speed. The closer we get to 100% the more power the motor sees the faster it
moves.
In short PWM uses a frequency and a duty cycle to control the speed or intensity of a voltage applied to a
load. We will use this to control the speed of a motor.
6.1.3 Timers
The Arduino Nano has a 16Mhz oscillator. The Arduino IDE environment set the prescalar to 64 effectively
ensuring that Timer 0 uses a 64 prescalar. If we want to run the motors at a higher PWM frequency than the
default ~400Hz then we have to change the prescalar, in the example described here we will use a prescalar
of 8 to increase the PWM frequency allowing us to control the RC car better a lower speeds.
When modifying the register to change the PWM frequency, you will have to make changes to the Arduino
files to account for the new prescalar and still maintain the delay functions.
6.2 H-BRIDGE
The H-bridge is the most common way to control a motor back and forward. It does this by switching the
polarity of the motor leads so that the motor moves in one direction or another. In its simplest form the Hbridge looks as following:
When the TOP-RIGHT and the BOTTOM-LEFT switches are ON, then the motor turns one way. You can stop it
by closing both BOTTOM switches or opening both.
By: Ivan R Quiroz
We will hack into the H-bridge already used in the RC car. Tapping into the lines needed by the Arduino to be
able to control the motors.
Why not connect the Arduino directly to the motors? It is all
about the current needed by a motor and what the Arduino can
deliver. The pins in an Arduino should not draw more than
25mA or the chip will start to suffer and eventually burn.
Instead we use an H-bridge made out of MOSFETs to control
larger currents needed by the motor, with smaller currents
controlled by the Arduino.
6.3 DIFFERENTIAL DRIVING
This is the simplest way to control a robot. Each wheel has a
motor. You spin both forward or backwards to move ahead or
reverse. When you want to move right or left, you move one
wheel forward and the other backwards depending on which
side you want to turn. This allows 360 degrees turn radius.
6.4 SONAR SENSOR
The Sonar Sensor works by generating a sound and detecting the sound that bounces of an obstacle. Similar
to bats. The sonar sensor generates ultrasound, undetectable to humans. It is ultrasound due to the
frequency at which the sound waves vibrate. It is used based on the knowledge that sound waves travel at a
speed of 343 m/s (1,126 ft/s) in dry air at room temperature and pressure. So if you send a sound wave and
sit and listen you can determine how far away and object is.
(source: http://www.parallax.com/Portals/0/Downloads/docs/prod/acc/28015-PING-v1.6.pdf)
To sense an object we set the TRIG pin high for 10 usec then back LOW. This signal initiate 8 burst of ultra
sound at 40Khz called “chirp”, and any object infront of the sensor will produce a pulse in the ECHO line,
proportional to the sound sensed. We measure how long the ECHO signal remains ON for, and use the
following formula to get the distance:
•
Distance in cm = Pulse Width (in uSec) / 58
By: Ivan R Quiroz
•
Distance in in = Pulse Width (in uSec) / 148
If nothing is detected the Pulse Width will be 38msec, the detection range is from 2cm to 450cm (pulse
widths from 116 usec to 26100 usec). So you will need to have a WORD/LONG size variable to measure that
distance.
There is also a factor that affect the reading and that is the temperature and pressure. So we will use the
sensor to roughly say that there is an object and based on the measurement we can have an error of 12%.
We could account for this when detecting the close items as they might be half an inch closer than what is
reported (or 0.5 inch further too).
The HC-SR04 module parameters are as following
• Working Voltage:
5V (DC).
• Static current:
Less than 2mA.
• Output signal:
Electric frequency signal, high level 5V, low level 0V.
• Sensor angle:
Not more than 15 degrees.
• Detection distance: 2cm-450cm.
• High precision:
Up to 0.3cm.
• Input trigger signal: 10us TTL impulse.
• Echo signal:
output TTL PWL signal.
By: Ivan R Quiroz
6.5 BATTERIES
7 HACKING THUNDER TUMBLER
By: Ivan R Quiroz
RC Car
1. Remove top cover by taking the screws from the bottom. Remove
battery holder
2. Carefully remove the LED from the top cover to free it up
3. Note the color codes from the
motors into controller
4. Remove the screws holding the controller board
5. Carefully remove the power switch from the bottom of the car
6. Remove power cables and add diodes switch to cable to controller board
Remove extra LEDs, and antenna cables with a soldering iron
7. Remove motor cables noting their location and pairing them up
8. Remove the RC IC from the board
By: Ivan R Quiroz
9.
10.
11.
12.
13.
Add capacitor to the input power pins (note polarity)
Add diodes across the power transistors of the H-bridge
Locate the control pins for the H-bridge
Solder long enough wires to the control pins pads noted above.
Solder a piece of wire to ground for later reference point.
Arduino
1.
2.
3.
4.
5.
6.
7.
8.
9.
Solder a wire from the battery via diode to the Arduino input power and ground
Solder a wire from the 5V pin to the Sonar Pin 5V power
Solder a wire from D2 to the TRIG? Pin in the Sonar sensor
Solder a wire from D12 to the ECHO pin in the Sonar sensor
Solder a wire from GND to the Sonar GND
Solder a wire from Pin 6 RC controller to D9 of Arduino
Solder a wire from Pin 7 solder to D10 of Arduino
Solder a wire from Pin 10 solder to D11 of Arduino
Solder a wire from Pin 11 solder to D3 of Arduino
Power
1. Solder wire from battery holder to middle of switch
2. From the other side of the switch, 2 diodes back to back should be connected
a. Notice the diode can be placed at each of the boards for easier mounting.
b. Single wire from switch to Arduino power via diode
Battery
c. Single wire from switch to RC Controller power via diode
Switch V+
Battery
Switch V+
Battery
GND
Battery
GND
By: Ivan R Quiroz
8 SCHEMATIC DEFINITION
8.1 RC CONTROLLER CIRCUIT
By: Ivan R Quiroz
8.2 HACKED H-BRIDGE
8.2.1
8.2.2
8.2.3
8.2.3.1
•
•
•
•
Add a capacitor to the input power close to the motors
Add protection diodes across the H-bridge main transistors (8-total), figure below:
Remove the IC2 mentioned above
Add wires to the pads of Pin 6,7,10,11 of IC2
Wire from Pin 6 solder to D9 of Arduino
Wire from Pin 7 solder to D10 of Arduino
Wire from Pin 10 solder to D11 of Arduino
Wire from Pin 11 solder to D13 of Arduino
9 SOFTWARE FLOW
1.
2.
3.
4.
5.
Start
Setup IO and timers
Delay 2 seconds, blink LED
Check Sensor
Is there an obstacle?
NO – Move forward
YES – Stop, reverse and Rotate right, blink LED
6. Go to 4
Considerations:
•
•
•
•
Under no circumstance can the program enable both legs of the H-bridge or the MOSFETs will suff
due to a short caused by the program
If the robot is moving too fast it will have a hard time stopping so make sure you have enough
distance measured to allow for breaking time
Different surfaces will limit the speed of the car
The sensor will not always detect an obstacle, it just happens
10 ARDUINO SOFTWARE LINKS
INKS
10.1 PWM TIMER0 CHANGES
http://playground.arduino.cc/Main/TimerPWMCheatsheet
10.2 PING
http://arduino.cc/en/Tutorial/Ping?from=Tutorial.UltrasoundSensor
10.3 TIMERS
http://letsmakerobots.com/node/28278
By: Ivan R Quiroz
10.4 PWM
http://arduino.cc/en/Tutorial/PWM
11 IMPROVEMENT IDEAS
Travel in a straight line via encoders
Include a bumper switch to ensure you do not run into things
Reduce speed of motors as it approach an obstacle
12 CONTEST
Obstacle course.
By: Ivan R Quiroz
13 SIMPLE CODE
// [Hack a RC Toy]===============================================================================
// By: Ivan Quiroz (Feb, 2013) v0.1
=
// File Name: Simple_RC_hack.ino
=
//
=
// Controls an H-bridge with 4 signals via PWM at ~400Hz using AnalogWrite function
=
// H-Bridge uses 4 control lines (2 per motor)
=
// Uses a sonar sensor and a IR sensor to avoid obstacles
=
// Libraries such as Servo.h can be used since motor lines are attached to D9, D10, D11, D3
=
// Timer0 for generating PWM
=
// Compiled for a Arduino Nano
=
//===============================================================================================
//Servo sonarServo;
// Object servo to be used by program
// [Variables Definition]
long IR_dist = 0;
;
//
long Sonar_dist = 0;
;
//
int pos = 90;
;
//
int M_move = 0;
;
//
int Lspeed = 0;
;
//
int Rspeed = 0;
;
//
const int mFWD = 70;
;
//
const int mRWD = 66;
;
//
const int mLEFT = 76;
;
//
const int mRIGHT = 82;
;
//
const int mSTOP = 83;
;
//
Variable used to read IR sensor
Variable used to read Sonar sensor
Variable to position servo
Test variables used with serial port
Test variables used with serial port
Test variables used with serial port
Variable ASCII F
Variable ASCII B
Variable ASCII L
Variable ASCII R
Variable ASCII S
// [Arduino Nano HW Pin definitions]
int servoPin = 6;
;
// Not implented in HW yet, change pin if needed
int sonarEcho = 12;
;
// Sonar Echo Pin
int sonarTrig = 2;
;
// Sonar Trig Pin
int IRpin = A0;
;
// IR signal Pin
int LeftM3 = 3;
;
// Right H-brige control
int LeftM4 = 11;
;
// Right H-brige control
int RightM1 = 9;
;
// Left H-brige control
int RightM2 = 10;
;
// Left H-brige control
int LED = 13;
;
// On board LED off D13
// [SETUP] the setup routine runs once when you press reset:
void setup()
()
{
Serial.
.begin (9600);
);
pinMode(
(sonarTrig,
, OUTPUT);
);
pinMode(
(sonarEcho,
, INPUT);
);
pinMode(
(RightM1,
, OUTPUT);
);
pinMode(
(RightM2,
, OUTPUT);
);
pinMode(
(LeftM3,
, OUTPUT);
);
pinMode(
(LeftM4,
, OUTPUT);
);
pinMode(
(IRpin,
, INPUT);
);
//pinMode(servoPin, OUTPUT);
pinMode(
(LED,
, OUTPUT);
);
// Use it to debug
// Define Sonar pin behavior for TRIG
// Define Sonar pin behavior for ECHIO
// Define Motor1 Output
// Define Motor2 Output
// Define Motor3 Output
// Define Motor4 Output
// Define IR Sensor as Input
// Define ServoPin as Output
// Define LED Pin as Output
digitalWrite(
(RightM1,
, LOW);
);
digitalWrite(
(RightM2,
, LOW);
);
digitalWrite(
(LeftM3,
, LOW);
);
digitalWrite(
(LeftM4,
, LOW);
);
//
//
//
//
//sonarServo.attach(servoPin);
setPWM();
();
// attach servo object
// Enable PWM registers to use 122Hz PWM channels, with starting 0 duty cycle
Make
Make
Make
Make
sure
sure
sure
sure
all
all
all
all
motors
motors
motors
motors
are
are
are
are
stopped
stopped
stopped
stopped
By: Ivan R Quiroz
digitalWrite(
(LED,
,
delay(
(500);
);
digitalWrite(
(LED,
,
delay(
(500);
);
digitalWrite(
(LED,
,
delay(
(500);
);
digitalWrite(
(LED,
,
delay(
(500);
);
HIGH);
);
LOW);
);
HIGH);
);
LOW);
);
//
//
//
//
//
//
//
//
turn
wait
turn
wait
turn
wait
turn
wait
the
for
the
for
the
for
the
for
LED
500
LED
500
LED
500
LED
500
on (HIGH is the voltage level)
msecond
off by making the voltage LOW
msecond
on (HIGH is the voltage level)
msecond
off by making the voltage LOW
msecond
}
// [LOOP] Main program loop
void loop()
() {
Sonar_dist = getSonar();
();
IR_dist = getIR();
();
// Get Sonar sensor
// Get distance from IR Sensor
while (IR_dist < 25 | Sonar_dist < 35)
) {
// while (IR_dist < 30) {
UpdateMotors (mSTOP,
, 0,
, 0);
);
// Stop Motor
delay(
(50);
);
// wait for 500 msecond
UpdateMotors (mRWD,
, 110,
, 110);
);
// Reverse Motor: good value for floor 70 good value for carpet 110
delay(
(300);
);
// wait for 500 msecond
UpdateMotors (mSTOP,
, 0,
, 0);
);
// Stop Motor
digitalWrite(
(LED,
, HIGH);
);
// turn the LED on (HIGH is the voltage level)
delay (400);
);
digitalWrite(
(LED,
, LOW);
);
// turn the LED off by making the voltage LOW
delay(
(500);
);
// wait for 500 msecond
UpdateMotors (mRWD,
, 110,
, 110);
);
// Reverse Motor: good value for floor 70 good value for carpet 110
digitalWrite(
(LED,
, HIGH);
);
// turn the LED on (HIGH is the voltage level)
delay (500);
);
UpdateMotors (mSTOP,
, 0,
, 0);
);
// Stop Motor
digitalWrite(
(LED,
, LOW);
);
// turn the LED off by making the voltage LOW
delay (500);
);
UpdateMotors (mRIGHT,
, 170,
, 180);
); // Move Right: carpet - 150 floor -90
digitalWrite(
(LED,
, HIGH);
);
// turn the LED on (HIGH is the voltage level)
delay(
(400);
);
// wait for 500 msecond
digitalWrite(
(LED,
, LOW);
);
// turn the LED off by making the voltage LOW
delay(
(400);
);
// wait for 500 msecond
digitalWrite(
(LED,
, HIGH);
);
// turn the LED on (HIGH is the voltage level)
delay(
(400);
);
// wait for 500 msecond
digitalWrite(
(LED,
, LOW);
);
// turn the LED off by making the voltage LOW
delay(
(400);
);
// wait for 500 msecond
UpdateMotors (mSTOP,
, 0,
, 0);
);
delay (500);
);
Sonar_dist = getSonar();
();
IR_dist = getIR();
();
// Stop Motor
// Get Sonar sensor
// Get distance from IR Sensor
}
UpdateMotors (mFWD,
, 100,
, 100);
);
}
// Move Forward
// ------------------------------------------------------------------------------// FUNTCTION: Update Motors
void UpdateMotors(
(int dir,
, int Lspeed_M,
, int Rspeed_M)
)
// Need a direction F=Forward=70 B=Back=66 L=Left=76 R=Right=82
S=Stop=83 (F,B,L,R,S ASCII decimal equivalent). speed_M = 0-255 (a.k.a. Duty Cycle)
{
switch(
) {
switch(dir)
case 70:
:
// Case F: Forward
// Motor Right: M1 = High M2= LOW
analogWrite(
(RightM2,
, 0);
);
// Disable one side of H-bridge
analogWrite(
(RightM1,
, Rspeed_M);
);
// PWM the correct side of the motor
// Motor Left: M3 = High M4= LOW
By: Ivan R Quiroz
analogWrite(
(LeftM4,
, 0);
);
analogWrite(
(LeftM3,
, Lspeed_M)
);
break;
break;
// Disable one side of H-bridge
// PWM the correct side of the motor
case 66:
:
// Case B: Back
// Motor Right: M2 = High M1= LOW
analogWrite(
(RightM1,
, 0);
);
// Disable one side of H-bridge
analogWrite(
(RightM2,
, Rspeed_M);
);
// PWM the correct side of the motor
// Motor Left: M4 = High M3= LOW
analogWrite(
(LeftM3,
, 0);
);
analogWrite(
(LeftM4,
, Lspeed_M);
);
break;
reak;
// Disable one side of H-bridge
// PWM the correct side of the motor
case 76:
: // Case L: Left
// Motor Right: M2 = High M1= LOW
analogWrite(
(RightM1,
, 0);
);
// Disable one side of H-bridge
analogWrite(
(RightM2,
, Rspeed_M);
);
// PWM the correct side of the motor
// Motor Left: M3 = High M4= LOW
analogWrite(
(LeftM4,
, 0);
);
analogWrite(
(LeftM3,
, Lspeed_M);
);
break;
break;
// Disable one side of H-bridge
// PWM the correct side of the motor
case 82:
:
// Case R: Right
// Motor Right: M1 = High M2= LOW
analogWrite(
(RightM2,
, 0);
);
// Disable one side of H-bridge
analogWrite(
(RightM1,
, Rspeed_M);
);
// PWM the correct side of the motor
// Motor Left: M4 = High M3= LOW
analogWrite(
(LeftM3,
, 0);
);
analogWrite(
(LeftM4,
, Lspeed_M);
);
break;
break;
case 83:
:
PWMout(
(RightM1,
, 0);
);
PWMout(
(RightM2,
, 0);
);
PWMout(
(LeftM3,
, 0);
);
PWMout(
(LeftM4,
, 0);
);
break;
break;
// Disable one side of H-bridge
// PWM the correct side of the motor
//
//
//
//
//
Case S:
Disable
Disable
Disable
Disable
default:
default:
//On default make sure H-brisge is disabled
PWMout(
(RightM1,
, 0);
);
// Disable
PWMout(
(RightM2,
, 0);
);
// Disable
PWMout(
(LeftM3,
, 0);
);
// Disable
PWMout(
(LeftM4,
, 0);
);
// Disable
break;
break;
Stop
one side
one side
one side
one side
of
of
of
of
H-bridge
H-bridge
H-bridge
H-bridge
one
one
one
one
of
of
of
of
H-bridge
H-bridge
H-bridge
H-bridge
side
side
side
side
}
}
// ------------------------------------------------------------------------------// FUNTCTION: IR Sensor reading
// Based on function http://www.acroname.com/robotics/info/articles/irlinear/irlinear.html
// Datasheet located at: http://www.sharpsma.com/webfm_send/1205
// Sharp sensor GP2D120 analog, max distance 12 in (35cm), min range 3 in
long getIR()
() {
long IR_calc_dist;
;
long analogIR_dist = analogRead(
(IRpin);
);
IR_calc_dist = (2914 / (analogIR_dist + 5))
)) - 1;
;
Serial.
.print(
(IR_calc_dist);
);
Serial.
.println(
(" cm============== [IR]");
);
By: Ivan R Quiroz
delay(
(40);
);
return IR_calc_dist;
;
}
// ------------------------------------------------------------------------------// FUNTCTION: Sonar HC-SR04 sensor, measures 2cm-450cm distances (150usec to 25msec) 38msec if out of range
// Returns: distance
long getSonar()
()
{
long echoBack,
, distance;
;
//Generate chirp via trigger
digitalWrite(
(sonarTrig,
, LOW);
);
delayMicroseconds(
(2);
);
digitalWrite(
(sonarTrig,
, HIGH);
);
delayMicroseconds(
(10);
);
digitalWrite(
(sonarTrig,
, LOW);
);
//Read Trigger generated echo
echoBack = pulseIn(
(sonarEcho,
, HIGH);
);
distance = (echoBack/
/58);
);
//
//
//
//
//
Send a triggering signal
low for 2usec
then high for 10 usec
10 usec delay
end pulse with a low signal
// Read response back from echo (in usec)
// Per datasheet calculation (in cm)
// Sensor will give out of range over 450cms ignore if so
){
if (distance >= 655 || distance <= 1){
Serial.
.print(
(distance);
);
Serial.
.println(
("Out of range");
);
}
else {
Serial.
.print(
(distance);
);
Serial.
.println(
(" cm ))) Sonar");
);
}
//delay(500);
// Comment out to test get slower readings from sonar
return distance;
;
}
// ------------------------------------------------------------------------------// FUNTCTION: Move Servo position
// Send a value pos from 0 to 180 (dregrees)
// In reallity stop at higher than 0 and lower than 180 as to not hit mechanical stop of servo
// Make sure to refresh servo position every 18 msec
/*
void moveServo(int pos){
sonarServo.write(pos);
delay(15);
// tell servo to go to position in variable 'pos'
// signal expected every 18 msec
}
*/
// ------------------------------------------------------------------------------// FUNCTION: Initial Set up of PWM for Arduino
// TImer1-16bit: PWM frequency = 16MHz / (2 * 8 * 255)= 3.92KHz
// Timer2-8bit: PWM frequency = 16Mhz / (8 * 510) = 3.92 KHz
// Note that adjusting the Timer 0 prescalar (TCCR0B) will cahnge the setting for milli() and delay()
void setPWM()
() {
// Settings for Pin D3 and D11 (_BV sets bit to 1) - Check page 158 of datasheet
http://www.atmel.com/Images/doc8161.pdf
TCCR2A = _BV(
(COM2A1)
) | _BV(
(COM2B1)
) | _BV(
(WGM20);
);
// Set Phase correct PWM mode, TOP mode
TCCR2B = _BV(
(CS21);
);
// Set clk/8 prescalar
OCR2A = 0;
;
// Adjust duty cycle for pin D11
OCR2B = 0;
;
// Adjust duty cycle for pin D3
// Settings for Pin D9 and D10 - Check page 106 of datasheet http://www.atmel.com/Images/doc8161.pdf
By: Ivan R Quiroz
// Adjusting the TCCR0 will break the delay function. If want to use must change Arduino core file
hardware\arduino\cores\arduino\wiring.c
// change the line: #define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(xx* 256)) where xx default 64,
modify to prescaler you want to use.
TCCR1A = 0x0;
;
TCCR1B = 0x0;
;
ICR1 = 0x00ff;
;
TCCR1A = _BV(
(COM1A1)
) | _BV(
(COM1B1)
) | _BV(
(WGM10);
); // // Set Phase correct PWM mode, TOP mode
TCCR1B = _BV(
(CS11);
);
// Set clk/8 prescalar
OCR1A = 0;
;
// Adjust duty cycle for pin D9
OCR1B = 0;
;
// Adjust duty cycle for pin D10
}
//
//
//
//
//
------------------------------------------------------------------------------FUNCTION: PWM out for Arduino, takes a pin (5,6,3,11) and duty cycle (0-255)
PWM frequency = 16Mhz / (8 * 510) = 3.921 KHz
expecting varPin to definge the pin (5,6,3,11)
expecting varDutyC expecting 0-255 (255=100%)
void PWMout(
(int varPin,
, int varDutyC)
) {
switch (varPin)
) {
case 9:
:
OCR1A = varDutyC;
;
break;
break;
// Adjust duty cycle for pin D9
case 10:
:
OCR1B = varDutyC;
;
break;
break;
// Adjust duty cycle for pin D10
case 3:
:
OCR2B = varDutyC;
;
break;
break;
// Adjust duty cycle for pin D3
case 11:
:
OCR2A = varDutyC;
;
break;
break;
// Adjust duty cycle for pin D11
}
}
By: Ivan R Quiroz
14 ADVANCED CODE
// [Hack a RC Toy]===============================================================================
// By: Ivan Quiroz (Feb, 2013) v0.1
=
// File Name: Advanced_RC_hack.ino
=
//
=
// Controls an H-bridge with 4 signals via PWM at 3.9KHz
=
// H-Bridge uses 4 control lines (2 per motor)
=
// Uses a soner sensor to avoid obstacles
=
// Make sure not to use libraries such as the Servo.h as it could short the H-bridge
=
// Timer1 and Timer2 used for generating PWM
=
// Compiled for a Arduino Nano
=
//===============================================================================================
// [Variables Definition]
long IR_dist = 0;
;
//
long Sonar_dist = 0;
;
//
int pos = 90;
;
//
int M_move = 0;
;
//
int Lspeed = 0;
;
//
int Rspeed = 0;
;
//
const int mFWD = 70;
;
//
const int mRWD = 66;
;
//
const int mLEFT = 76;
;
//
const int mRIGHT = 82;
;
//
const int mSTOP = 83;
;
//
Variable used to read IR sensor
Variable used to read Sonar sensor
Variable to position servo
Test variables used with serial port
Test variables used with serial port
Test variables used with serial port
Variable ASCII F
Variable ASCII B
Variable ASCII L
Variable ASCII R
Variable ASCII S
// [Arduino Nano HW Pin definitions]
int servoPin = 6;
;
// Not implented in HW yet, change pin if needed
int sonarEcho = 12;
;
// Sonar Echo Pin
int sonarTrig = 2;
;
// Sonar Trig Pin
int IRpin = A0;
;
// IR signal Pin
int LeftM3 = 3;
;
// Right H-brige control
int LeftM4 = 11;
;
// Right H-brige control
int RightM1 = 9;
;
// Left H-brige control
int RightM2 = 10;
;
// Left H-brige control
int LED = 13;
;
// On board LED off D13
// [SETUP] the setup routine runs once when you press reset:
void setup()
()
{
Serial.
.begin (9600);
);
pinMode(
(sonarTrig,
, OUTPUT);
);
pinMode(
(sonarEcho,
, INPUT);
);
pinMode(
(RightM1,
, OUTPUT);
);
pinMode(
(RightM2,
, OUTPUT);
);
pinMode(
(LeftM3,
, OUTPUT);
);
pinMode(
(LeftM4,
, OUTPUT);
);
pinMode(
(IRpin,
, INPUT);
);
pinMode(
(LED,
, OUTPUT);
);
//
//
//
//
//
//
//
//
//
Use it
Define
Define
Define
Define
Define
Define
Define
Define
digitalWrite(
(RightM1,
, LOW);
);
digitalWrite(
(RightM2,
, LOW);
);
digitalWrite(
(LeftM3,
, LOW);
);
digitalWrite(
(LeftM4,
, LOW);
);
//
//
//
//
Make
Make
Make
Make
setPWM();
();
digitalWrite(
(LED,
, HIGH);
);
delay(
(500);
);
digitalWrite(
(LED,
, LOW);
);
to debug
Sonar pin behavior for TRIG
Sonar pin behavior for ECHIO
Motor1 Output
Motor2 Output
Motor3 Output
Motor4 Output
IR Sensor as Input
LED Pin as Output
sure
sure
sure
sure
all
all
all
all
motors
motors
motors
motors
are
are
are
are
stopped
stopped
stopped
stopped
// Enable PWM registers to use 122Hz PWM channels, with starting 0 duty cycle
// turn the LED on (HIGH is the voltage level)
// wait for 500 msecond
// turn the LED off by making the voltage LOW
By: Ivan R Quiroz
delay(
(500);
);
digitalWrite(
(LED,
, HIGH);
);
delay(
(500);
);
digitalWrite(
(LED,
, LOW);
);
delay(
(500);
);
//
//
//
//
//
wait
turn
wait
turn
wait
for
the
for
the
for
500
LED
500
LED
500
msecond
on (HIGH is the voltage level)
msecond
off by making the voltage LOW
msecond
}
// [LOOP] Main program loop
void loop()
() {
Sonar_dist = getSonar();
();
IR_dist = getIR();
();
// Get Sonar sensor
// Get distance from IR Sensor
while (IR_dist < 22 | Sonar_dist < 35)
) {
UpdateMotors (mSTOP,
, 0,
, 0);
);
// Stop Motor
delay(
(50);
);
// wait for 500 msecond
UpdateMotors (mRWD,
, 80,
, 80);
);
// Reverse Motor: good value for floor 70 good value for carpet 110
delay(
(300);
);
// wait for 500 msecond
UpdateMotors (mSTOP,
, 0,
, 0);
);
// Stop Motor
digitalWrite(
(LED,
, HIGH);
);
// turn the LED on (HIGH is the voltage level)
delay (400);
);
digitalWrite(
(LED,
, LOW);
);
// turn the LED off by making the voltage LOW
delay(
(500);
);
// wait for 500 msecond
UpdateMotors (mRWD,
, 110,
, 110);
);
// Reverse Motor: good value for floor 70 good value for carpet 110
//delay (halfSec);
// Delay 1 second (1000 msec) due to change to Timer 0
digitalWrite(
(LED,
, HIGH);
);
// turn the LED on (HIGH is the voltage level)
delay (500);
);
UpdateMotors (mSTOP,
, 0,
, 0);
);
// Stop Motor
//delay (halfSec);
// Delay 1 second (1000 msec) due to change to Timer 0
digitalWrite(
(LED,
, LOW);
);
// turn the LED off by making the voltage LOW
delay (500);
);
UpdateMotors (mRIGHT,
, 80,
, 90);
); // Move Right: carpet - 150
floor -90
//delay (oneSec);
digitalWrite(
(LED,
, HIGH);
);
// turn the LED on (HIGH is the voltage level)
delay(
(400);
);
// wait for 500 msecond
digitalWrite(
(LED,
, LOW);
);
// turn the LED off by making the voltage LOW
delay(
(400);
);
// wait for 500 msecond
digitalWrite(
(LED,
, HIGH);
);
// turn the LED on (HIGH is the voltage level)
delay(
(400);
);
// wait for 500 msecond
digitalWrite(
(LED,
, LOW);
);
// turn the LED off by making the voltage LOW
delay(
(400);
);
// wait for 500 msecond
UpdateMotors (mSTOP,
, 0,
, 0);
);
delay (500);
);
Sonar_dist = getSonar();
();
IR_dist = getIR();
();
}
UpdateMotors (mFWD,
, 80,
, 80)
) ;
}
//
//
//
//
//
//
// Stop Motor
// Get Sonar sensor
// Get distance from IR Sensor
// Move Forward all the time until sensor is found
------------------------------------------------------------------------------FUNTCTION: Update Motors
Expects a direction dir equivalent to F=Forward=70 B=Back=66 L=Left=76 R=Right=82 S=Stop=83
ASCII(70)=F, ASCII(66)=B,..L,R,S ASCII decimal equivalent).
Expect Lspeed_M and Rspeed_M = 0-255 (a.k.a. Duty Cycle 0%=0 100%=255)
Different duty cycle allows to compensate for difference in motors
void UpdateMotors(
(int dir,
, int Lspeed_M,
, int Rspeed_M)
)
{
switch(
) {
switch(dir)
case 70:
:
// Motor Right: M1 = High M2= LOW
PWMout(
(RightM2,
, 0);
);
// Case F: Forward
// Disable one side of H-bridge
By: Ivan R Quiroz
PWMout(
(RightM1,
, Rspeed_M);
);
// Motor Left: M3 = High M4= LOW
PWMout(
(LeftM4,
, 0);
);
PWMout(
(LeftM3,
, Lspeed_M);
);
break;
break;
case 66:
:
// Motor Right:
PWMout(
(RightM1,
,
PWMout(
(RightM2,
,
// Disable one side of H-bridge
// PWM the correct side of the motor
// Case B: Back
M2 = High M1= LOW
0);
);
Rspeed_M);
);
// Motor Left: M4 = High M3= LOW
PWMout(
(LeftM3,
, 0);
);
PWMout(
(LeftM4,
, Lspeed_M);
);
break;
break;
case 76:
: // Case
// Motor Right:
PWMout(
(RightM1,
,
PWMout(
(RightM2,
,
// PWM the correct side of the motor
L: Left
M2 = High M1= LOW
0);
);
Rspeed_M);
);
// Motor Left: M3 = High M4= LOW
PWMout(
(LeftM4,
, 0);
);
PWMout(
(LeftM3,
, Lspeed_M);
);
break;
break;
case 82:
:
// Motor Right: M1 = High M2= LOW
PWMout(
(RightM2,
, 0);
);
PWMout(
(RightM1,
, Rspeed_M);
);
// Motor Left: M4 = High M3= LOW
PWMout(
(LeftM3,
, 0);
);
PWMout(
(LeftM4,
, Lspeed_M);
);
break;
break;
case 83:
:
PWMout(
(RightM1,
, 0);
);
PWMout(
(RightM2,
, 0);
);
PWMout(
(LeftM3,
, 0);
);
PWMout(
(LeftM4,
, 0);
);
break;
break;
// Disable one side of H-bridge
// PWM the correct side of the motor
// Disable one side of H-bridge
// PWM the correct side of the motor
// Disable one side of H-bridge
// PWM the correct side of the motor
// Disable one side of H-bridge
// PWM the correct side of the motor
// Case R: Right
// Disable one side of H-bridge
// PWM the correct side of the motor
// Disable one side of H-bridge
// PWM the correct side of the motor
//
//
//
//
//
Case S:
Disable
Disable
Disable
Disable
default:
default:
//On default make sure H-brisge is disabled
PWMout(
(RightM1,
, 0);
);
// Disable
PWMout(
(RightM2,
, 0);
);
// Disable
PWMout(
(LeftM3,
, 0);
);
// Disable
PWMout(
(LeftM4,
, 0);
);
// Disable
break;
break;
Stop
one side
one side
one side
one side
of
of
of
of
H-bridge
H-bridge
H-bridge
H-bridge
one
one
one
one
of
of
of
of
H-bridge
H-bridge
H-bridge
H-bridge
side
side
side
side
}
}
// ------------------------------------------------------------------------------// FUNTCTION: IR Sensor reading
// Based on function http://www.acroname.com/robotics/info/articles/irlinear/irlinear.html
// Datasheet located at: http://www.sharpsma.com/webfm_send/1205
// Sharp sensor GP2D120 analog, max distance 12 in (35cm), min range 3 in
long getIR()
() {
long IR_calc_dist;
;
long analogIR_dist = analogRead(
(IRpin);
);
By: Ivan R Quiroz
IR_calc_dist = (2914 / (analogIR_dist + 5))
)) - 1;
;
Serial.
.print(
(IR_calc_dist);
);
Serial.
.println(
(" cm============== [IR]");
);
delay(
(40);
);
return IR_calc_dist;
;
}
// ------------------------------------------------------------------------------// FUNTCTION: Sonar HC-SR04 sensor, measures 2cm-450cm distances (150usec to 25msec) 38msec if out of range
// Returns: distance 2-450
//
long getSonar()
()
{
long echoBack,
, distance;
;
//Generate chirp via trigger
digitalWrite(
(sonarTrig,
, LOW);
);
delayMicroseconds(
(2);
);
digitalWrite(
(sonarTrig,
, HIGH);
);
delayMicroseconds(
(10);
);
digitalWrite(
(sonarTrig,
, LOW);
);
//Read Trigger generated echo
echoBack = pulseIn(
(sonarEcho,
, HIGH);
);
distance = (echoBack/
/58);
);
//
//
//
//
//
Send a triggering signal
low for 2usec
then high for 10 usec
10 usec delay
end pulse with a low signal
// Read response back from echo (in usec)
// Per datasheet calculation (in cm)
// Sensor will give out of range over 450cms ignore if so
){
if (distance >= 655 || distance <= 1){
Serial.
.print(
(distance);
);
Serial.
.println(
("Out of range");
);
}
else {
Serial.
.print(
(distance);
);
Serial.
.println(
(" cm ))) Sonar");
);
}
//delay(500);
// Comment out to test get slower readings from sonar
return distance;
;
}
// ------------------------------------------------------------------------------// FUNCTION: Initial Set up of PWM for Arduino
// TImer1-16bit: PWM frequency = 16MHz / (2 * 8 * 255)= 3.92KHz
// Timer2-8bit: PWM frequency = 16Mhz / (8 * 510) = 3.92 KHz
// Note that adjusting the Timer 0 prescalar (TCCR0B) will cahnge the setting for milli() and delay()
void setPWM()
() {
// Settings for Pin D3 and D11 (_BV sets bit to 1) - Check page 158 of datasheet
http://www.atmel.com/Images/doc8161.pdf
TCCR2A = _BV(
(COM2A1)
) | _BV(
(COM2B1)
) | _BV(
(WGM20);
);
// Set Phase correct PWM mode, TOP mode
TCCR2B = _BV(
(CS21);
);
// Set clk/8 prescalar
OCR2A = 0;
;
// Adjust duty cycle for pin D11
OCR2B = 0;
;
// Adjust duty cycle for pin D3
// Settings for Pin D9 and D10 - Check page 106 of datasheet http://www.atmel.com/Images/doc8161.pdf
TCCR1A = 0x0;
;
TCCR1B = 0x0;
;
ICR1 = 0x00ff;
;
TCCR1A = _BV(
(COM1A1)
) | _BV(
) | _BV(
(WGM10);
);
// Set Phase correct PWM mode, TOP mode
(COM1B1)
TCCR1B = _BV(
(CS11);
);
// Set clk/8 prescalar
OCR1A = 0;
;
// Adjust duty cycle for pin D9
OCR1B = 0;
;
// Adjust duty cycle for pin D10
}
By: Ivan R Quiroz
//
//
//
//
//
------------------------------------------------------------------------------FUNCTION: PWM out for Arduino, takes a pin (5,6,3,11) and duty cycle (0-255)
PWM frequency = 16Mhz / (8 * 510) = 3.921 KHz
expecting varPin to definge the pin (5,6,3,11)
expecting varDutyC expecting 0-255 (255=100%)
void PWMout(
(int varPin,
, int varDutyC)
) {
switch (varPin)
) {
case 9:
:
OCR1A = varDutyC;
;
break;
break;
// Adjust duty cycle for pin D9
case 10:
:
OCR1B = varDutyC;
;
break;
break;
// Adjust duty cycle for pin D10
case 3:
:
OCR2B = varDutyC;
;
break;
break;
// Adjust duty cycle for pin D3
case 11:
:
OCR2A = varDutyC;
;
break;
break;
// Adjust duty cycle for pin D11
}
}
By: Ivan R Quiroz
15 APPENDIX
15.1 IR SENSOR
There are a variety of different infrared sensors. I will discuss here the analog version of the Sharp IR sensor
GP2D120 (http://www.sharpsma.com/webfm_send/1203).
The sensor feature are:
•
•
•
•
•
•
•
•
Power from 5V
Analog output
Effective Range: 10 to 80 cm
LED pulse cycle duration: 32 ms
Typical response time: 39 ms
Typical start up delay: 44 ms
Average current consumption: 33 mA
Detection area diameter @ 80 cm: 6 cm
The graph shows you that the closer you are to the
object the higher the voltage up to 2.6V at 10 cm. Then
the voltage drops sharply when items are closer than
10cm. We will need to consider this case during
programing as if there is a close by obstacle can be lost
from range and considered to be further than what it
is. This also shows why a bumper switch is also a good
idea, so that if you start the robot facing a wall, you
will soon run into it if you had just power up the unit.
The connections needed to the Arduino will require an analog channel to read the output from the sensor.
The Arduino will also need to supply the 5V to the sensor. A hacked in capacitor is recommended
15.2 SERVO MOTOR
The servo motor is a special type of motor in that it contains the H-bridge as well a special control circuitry.
The Servo motor is design to move a range of 180 degrees back and forward. The servo motor maintains a
locked position based on a signal duration. There are 3 pins to a servo. One is the 5V pin (red most of the
times) another is the ground (Black) and the last one is the signal (blue or white).
By: Ivan R Quiroz
To control the servo, a signal width is sent
depending on what position you want the servo to
be. Then you continue sending that signal every 18
msec. The image show what widths will move the
servo to its extremes and middle
By generating different pulse widths between
1msec and 2 msec we can have the servo sweep
an aera of 180 degrees.
The following are the most common servo wire
pinout. While some servos are designed for 6V,
you can overdrive them with 7.2V without much
issues.
CAUTION:
While the Arduino environment has a Servo library available, care must be taken to use it if modifying the
PWM via the registers. The Servo library uses D9 as the signal pin, and it could short your H-bridge if using to
control it.