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.