Last active
March 12, 2016 23:11
-
-
Save john212/d1339fa0cf29c1306c05 to your computer and use it in GitHub Desktop.
Vex Mini Robot - Arduino Uno - Vex 393 DC Motors - Sparkfun IR 9-Button Remote - Ping Ultrasonic Rangefinder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/************************************************** | |
Udemy Arduino Step-by-Step Course | |
Section 5 DC Motors Lecture 47 Part 2 Using the L298N H Bridge | |
To Control Speed/Direction of a 2-Motor Robot with A 9-Button | |
Infrared Remote Control and an Ultrasonic Distance Sensor. | |
by J. Cantlin | |
February 17, 2016 | |
***************************************************/ | |
/************************************************** | |
Description of Program | |
An Arduino Uno is connected to a L298N H- Bridge DC Motor Controller | |
which is used to control two DC motors. The DC motors are | |
the drive motors of a small robot made using Vex Robotics components. | |
The L298N changes the polarity of the DC voltage supply to the motors | |
when signalled to do so by the Arduino. The will cause the motors | |
to rotate in the opposite direction. The L298H takes the low | |
voltage (5vdc) digital high/low signal from the Arduino and reverses | |
the polarity of the higher voltage (7.2 vdc in this case) supplied to the DC motors. | |
The motor speed ramps from high speed to low speed proportional | |
to a PWM signal sent to the L298N from the Arduino. | |
For this sketch, a simple 9-button infrared remote control from | |
Sparkfun is used to send commands to a 38kHz demodulating Infrared Receiver | |
Modules connected to the Arduino to control the DC motors via the L298N. | |
A battery pack with 6 rechargable NiCad batteries is used to provide | |
7.2 vdc to the H-Bridge and DC motors. The Arduino is powered by a | |
9 vdc battery or, if more power is needed, by a LiPo Backup USB Charger. | |
A mini DC voltmeter is mounted on the back of the robot to | |
monitor the voltage of the 6 AA battery pack. This voltmeter was from | |
on eBay and costs $2.99 - it works great. | |
http://www.ebay.com/itm/201320503051?_trksid=p2060353.m2749.l2649&ssPageName=STRK%3AMEBIDX%3AIT | |
***************************************************/ | |
/************************************************** | |
L298N DC Motor Controller | |
This controller, mounted on a breakout board, can control | |
one or two DC Motors or DC Stepper Motors. | |
A supply voltage of about 6vdc min to a max of 35vdc is supplied | |
to the L298N board. The IC converts the low voltage signals | |
form the Arduino to the appropriate higher voltage signals to | |
control the voltage (speed) and direction of rotation (polarity, | |
positive or negative) of the two motors. | |
Summary of L298N H Bridge Contoller Pins/Terminals/Jumpers Used: | |
OUT1 = + Supply Voltage to "LEFT" DC Motor = DC Motor 1 | |
OUT2 = - Supply Voltage to "LEFT" DC Motor = DC Motor 1 | |
OUT3 = - Supply Voltage to "RIGHT" DC Motor = DC Motor 2 | |
OUT4 = + Supply Voltage to "RIGHT" DC Motor = DC Motor 2 | |
12V Jumper = On since supply volts = 7.2vdc, take off if supply > 12vdc | |
if off, the 5vdc supply pin is not used, not using this anyway. | |
+12V In = Supply is 7.2vdc for this project. About 6-35vdc OK. | |
GND = Ground Note: common ground to Arduino should be connected. | |
+5V Out = Not Used. Regulated 5vdc available if <=12vdc supplied. | |
ENA1 Jumper = OFF, PWM Signal to Motor 1 from Arduino Digital Pin 9 | |
IN1 = Arduino Digital Pin 4 = HIGH/LOW = CW/CCW Rotation Motor 1 | |
IN2 = Arduino Digital Pin 5 = PWM = Speed Motor 1 | |
IN3 = Arduino Digital Pin 6 = PWM = Speed Motor 2 | |
IN4 = Arduino Digital Pin 7 = HIGH/LOW = CW/CCW Rotation Motor 2 | |
ENA2 Jumper = OFF, PWM Signal to Motor 2 from Arduino Digital Pin 10 | |
The Arduino Uno PWM pins, identified with a ~, can send their PWM signal directly | |
to the ENA1 and ENA2 pins on the L298N. To do this, remove the jumpers on these | |
pins (this exposes two pins each) and connect the Arduino PWM pins to the pin exposed by | |
removing the jumper that is closest to the edge of the board. The inboard pin is only used, | |
as far as I know, by the "shoting" jumper that is installed by default. | |
The PWM pins on the Arduino Uno operate at different frequencies, to match the | |
PWMs best to the two robot drive motors, it seems like good practice to | |
use pins with the same PWM frequency, so I did. | |
Arduino Uno PWM Pins and Frequencies: | |
* Pins 5 and 6: controlled by timer0, base frequency 62500Hz | |
* Pins 9 and 10: controlled by timer1, base frequency 31250Hz, used here | |
* Pins 11 and 3: controlled by timer2, base frequency 31250Hz | |
A good L298N tutorial referenced in the Udemy course is at: | |
http://tronixlabs.com/news/tutorial-l298n-dual-motor-controller-module-2a-and-arduino/ | |
A well written tutorial on using the L298N and the PWM control is at: | |
http://tronixstuff.com/2014/11/25/tutorial-l298n-dual-motor-controller-modules-and-arduino/ | |
***************************************************/ | |
/************************************************** | |
Ping Ultasonic Distance Sensor | |
This sketch uses a PING ultrasonic distance sensor (made by Parallax) to find the | |
distance to the closest object in the sensor's range (3 cm to 3.3 meters). If set up | |
properly, it can be accurate to the nearest half centimeter. To measure a distance, | |
the Arduino sends a pulse to the sensor to initiate a "ping" via the sensor's speaker | |
and the sensor then listens for a echo of the "ping" to retun via its microphone. | |
The time it takes to hear the return echo is how long it takes sound to travel | |
to the object and back. Knowing the speed of sound in air at sea level (approximately | |
774 mph) allows calculating the distance from the detected object from the sensor. | |
Sensor connections: | |
* +5V and Ground | |
* One Digital Pin (any) of the Arduino | |
Ref.: http://www.arduino.cc/en/Tutorial/Ping | |
***************************************************/ | |
/************************************************** | |
DC Motors | |
Two Vex 393 DC Motors are used in this project. These motors | |
can run > 12vdc but are designed for running at 7.2vdc; which | |
was the voltage supplied to the L298N controller in this project. | |
More information on the 393 motors is at: | |
http://www.vexrobotics.com/wiki/2-Wire_Motor_393. | |
***************************************************/ | |
/************************************************** | |
IR Remote | |
The 9 buttons IR Remote is a simple infrared remote from Sparkfun, | |
kit #13235 which includes: | |
* A CR2025 Coin Cell Battery for the remote (used). | |
* Two 38kHz demodulating Infrared Receiver Modules (one used). | |
* Two 950nm-emitting Infrared LEDs (not used). | |
* A handful of 330Ω resistors (not used). | |
This nine button remote emits unique 32-bit codes for each button press. | |
Button Code | |
POWER 0x10EF D827 | |
A 0x10EF F907 | |
B 0x10EF 7887 | |
C 0x10EF 58A7 | |
^ 0x10EF A05F | |
< 0x10EF 10Ef | |
o 0x10EF 20DF | |
> 0x10EF 807F | |
\/ 0x10EF 00FF | |
The output of an IR Receiver Module (38 kHz demodulating | |
version) is connected to any Arduino's digital pin. | |
The IR Receiver Module should also be powered off the | |
Arduino's 5V and GND rails. The remote only sends commands to | |
the Arduino, so only the IR Receiver is needed on the robot. | |
The IRLibrary by Ken Shirriff is used and is on GitHub. | |
Reference Ken Shirriff's blog at: | |
http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html | |
This infrared remote library consists of two parts: IRsend transmits IR remote packets, | |
while IRrecv receives and decodes an IR message. IRsend uses an infrared LED | |
connected to a digital pin (pin 3 in the example). To send a message, call the send method | |
for the desired protocol with the data to send and the number of bits to send. Sending | |
is not needed or used to control the robot's motors. | |
That is, to transmit commands to the robot, only the IR Receiver will be used. | |
Some library documentation excerpts FYI: | |
The IRrecv class performs the decoding, and is initialized with enableIRIn(). | |
The decode() method is called to see if a code has been received; if so, | |
it returns a nonzero value and puts the results into the decode_results | |
structure. Once a code has been decoded, the resume() method must be called | |
to resume receiving codes. Note that decode() does not block; the sketch | |
can perform other operations while waiting for a code because the codes | |
are received by an interrupt routine. The IRrecv library consists of two parts. | |
An interrupt routine is called every 50 microseconds, measures the length | |
of the marks and spaces, and saves the durations in a buffer. The user | |
calls a decoding routine to decode the buffered measurements into the | |
code value that was sent (typically 11 to 32 bits). The decode library tries | |
decoding different protocols in succession, stopping if one succeeds. It | |
returns a structure that contains the raw data, the decoded data, the | |
number of bits in the decoded data, and the protocol used to decode the data. | |
Bottom line, the IR Remote library code is surprisgly...complicated. Use | |
the library, I don't recommend trying to write this code yourself. :-) | |
***************************************************/ | |
//Include the IR Remote Control libraries. | |
#include <IRremote.h> | |
#include <IRremoteInt.h> | |
/***************************************************/ | |
/* ************************************************ | |
Define the IR remote button codes. Only the least signinficant two bytes | |
of these codes are defined. Each one should actually has 0x10EF in front of it. | |
If your remote is not documented, find these codes by running the IRrecvDump | |
example sketch included with the IRremote library. Sparkfun provides these | |
codes with the IR Remote Control kit documentation. | |
****************************************************/ | |
#define NUM_BUTTONS 9 // The remote has 9 buttons | |
const uint16_t BUTTON_POWER = 0xD827; // i.e. 0x10EFD827 prefix for all is 0x10EF | |
const uint16_t BUTTON_A = 0xF807; | |
const uint16_t BUTTON_B = 0x7887; | |
const uint16_t BUTTON_C = 0x58A7; | |
const uint16_t BUTTON_UP = 0xA05F; | |
const uint16_t BUTTON_DOWN = 0x00FF; | |
const uint16_t BUTTON_LEFT = 0x10EF; | |
const uint16_t BUTTON_RIGHT = 0x807F; | |
const uint16_t BUTTON_CIRCLE = 0x20DF; | |
/* Connect the output of the IR receiver diode to pin 11. */ | |
int RECV_PIN = 11; | |
/* Initialize the irrecv part of the IRremote library */ | |
IRrecv irrecv(RECV_PIN); | |
decode_results results; // This will store the IR received codes | |
uint16_t lastCode = 0; // This keeps track of the last code RX'd | |
/************************************************** | |
Summary of Arduino Uno Analog Pins Used: | |
A0 = | |
A1 = | |
A2 = | |
A3 = | |
A4 = | |
A5 = | |
***************************************************/ | |
/************************************************** | |
Summary of Arduino Uno Digital Pins Used: | |
To use PWM, call analogWrite(pin, dutyCycle), where dutyCycle | |
is a value from 0 to 255, and pin is one of the PWM pins | |
(3, 5, 6, 9, 10, or 11). A square wave is output, there is | |
no control over frequency but higher duty cycle = higher volts. | |
00 = | |
01 = | |
02 = | |
03 = | |
04 = L298N IN1 Motor 1 = "LEFT" Motor Direction of Rotation | |
05 = L298N IN2 Motor 1 = "LEFT" Motor Direction of Rotation | |
06 = L298N IN3 Motor 2 = "RIGHT" Motor Direction of Rotation | |
07 = L298N IN5 Motor 2 = "RIGHT" Motor Direction of Rotation | |
08 = Ping Ultrasonic Distance Sensor ping out/in | |
09 = L298N ENA1 Motor 1 = "LEFT" Motor Speed via ~PWM Pin | |
10 = L298N ENA2 Motor 2 = "RIGHT" Motor Speed via ~PWM Pin | |
11 = IR Receiver Module (38 kHz demodulating version) ~PWM Pin | |
12 = | |
13 = | |
***************************************************/ | |
//Global variable assignments | |
//motor related | |
int M1T1 = 4; //Motor 1 Rotation - High/Low changes direction, T=Terminal | |
int M1T2 = 5; //Motor 1 Rotation - High/Low changes direction, T=Terminal | |
int M2T1 = 6; //Motor 2 Rotation - High/Low changes direction, T=Terminal | |
int M2T2 = 7; //Motor 2 Rotation - High/Low changes direction, T=Terminal | |
int ENAPWM1 = 9; //Motor 1 Speed via PWM High/Low Enables/Stops | |
int ENAPWM2 = 10; //Motor 2 Speed via PWM High/Low Enables/Stops | |
//IR Remote related | |
int slow = 64; //use IR Remote Button A | |
int medium = 127; //use IR Remote Button B, default | |
int fast = 255; //use IR Remote Button C | |
int speedSetButton = 127; //initial speed | |
//Ping Ultrasonic distance sensor related | |
const int pingPin = 8; //assign pin to the Ping ultrasonic distance sensor. | |
int reset_Value = 0; //distance sensor triggered reset value | |
void setup() | |
{//****start setup function**** | |
Serial.begin(9600); //set the serial port to 9600 baud if monitor is used. | |
// set all the motor control pins on L298N to outputs | |
pinMode(M1T1, OUTPUT); | |
pinMode(M1T2, OUTPUT); | |
pinMode(M2T1, OUTPUT); | |
pinMode(M2T2, OUTPUT); | |
pinMode(ENAPWM1, OUTPUT); | |
pinMode(ENAPWM2, OUTPUT); | |
//set the pin used for the Ping ultasonic distance sensor to an output | |
pinMode(pingPin, OUTPUT); | |
// Start the IR Receiver Module | |
irrecv.enableIRIn(); | |
}//****endof setup function**** | |
void loop() | |
{//****start infinite loop**** | |
/***********************************/ | |
//Find distance to object using Ping ultrasonic distance sensor | |
// establish variables for duration of the ping, | |
// and the distance result in inches and centimeters: | |
long duration, inches, cm; | |
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds. | |
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse: | |
pinMode(pingPin, OUTPUT); | |
digitalWrite(pingPin, LOW); | |
delayMicroseconds(2); | |
digitalWrite(pingPin, HIGH); | |
delayMicroseconds(5); | |
digitalWrite(pingPin, LOW); | |
// The same pin is used to read the signal from the PING))): a HIGH | |
// pulse whose duration is the time (in microseconds) from the sending | |
// of the ping to the reception of its echo off of an object. | |
pinMode(pingPin, INPUT); | |
duration = pulseIn(pingPin, HIGH); | |
// convert the time into a distance | |
inches = microsecondsToInches(duration); | |
cm = microsecondsToCentimeters(duration); | |
//debugging print lines ... comment out when not needed | |
//Serial.print(inches); | |
//Serial.print("in, "); | |
//Serial.print(cm); | |
//Serial.print("cm"); | |
//Serial.println(); | |
delay(10); | |
if (irrecv.decode(&results)) | |
{ | |
/* read the RX'd IR into a 16-bit variable: */ | |
uint16_t resultCode = (results.value & 0xFFFF); | |
/* The remote will continue to transmit 0xFFFFFFFF if a | |
button is held down. If we get 0xFFFFFFF, let's just | |
assume the previously pressed button is being held down */ | |
if (resultCode == 0xFFFF) | |
{ | |
resultCode = lastCode; | |
} | |
else | |
{ | |
lastCode = resultCode; | |
} | |
// This switch statement checks the received IR code against | |
// all of the known codes. Each button press produces a | |
// serial output, and has an effect on the IR LED output. | |
switch (resultCode) | |
{ | |
case BUTTON_POWER: | |
Serial.println("Power"); | |
//button pressed action here | |
//stop all motors | |
digitalWrite(M1T1, LOW); | |
digitalWrite(M1T2, LOW); | |
digitalWrite(M2T1, LOW); | |
digitalWrite(M2T2, LOW); | |
break; | |
case BUTTON_A: | |
Serial.println("A"); | |
//button pressed action here | |
speedSetButton = slow; | |
break; | |
case BUTTON_B: | |
Serial.println("B"); | |
//button pressed action here | |
speedSetButton = medium; | |
break; | |
case BUTTON_C: | |
Serial.println("C"); | |
//button pressed action here | |
speedSetButton = fast; | |
break; | |
case BUTTON_UP: | |
Serial.println("Up"); | |
//button pressed action here | |
digitalWrite(M1T1, LOW); //Set Forward Rotation Direction | |
digitalWrite(M1T2, HIGH); //Set Forward Rotation Direction | |
digitalWrite(M2T1, HIGH); //Set Forward Rotation Direction | |
digitalWrite(M2T2, LOW); //Set Forward Rotation Direction | |
analogWrite(ENAPWM1, speedSetButton); //Set Speed 0-255 | |
analogWrite(ENAPWM2, speedSetButton); //Set Speed 0-255 | |
Serial.print("SpeedSetButton: "); | |
Serial.println(speedSetButton); | |
break; | |
case BUTTON_DOWN: | |
Serial.println("Down"); | |
//button pressed action here | |
digitalWrite(M1T1, HIGH); //Set Reverse Rotation Direction | |
digitalWrite(M1T2, LOW); //Set Reverse Rotation Direction | |
digitalWrite(M2T1, LOW); //Set Reverse Rotation Direction | |
digitalWrite(M2T2, HIGH); //Set Reverse Rotation Direction | |
analogWrite(ENAPWM1, speedSetButton); //Set Speed 0-255 | |
analogWrite(ENAPWM2, speedSetButton); //Set Speed 0-255 | |
break; | |
case BUTTON_LEFT: | |
Serial.println("Left"); | |
//button pressed action here | |
digitalWrite(M1T1, HIGH); //Set Reverse Rotation Direction | |
digitalWrite(M1T2, LOW); //Set Reverse Rotation Direction | |
digitalWrite(M2T1, HIGH); //Set Forward Rotation Direction | |
digitalWrite(M2T2, LOW); //Set Forward Rotation Direction | |
analogWrite(ENAPWM1, 255); //Set Speed 0-255 | |
analogWrite(ENAPWM2,255); //Set Speed 0-255 | |
break; | |
case BUTTON_RIGHT: | |
Serial.println("Right"); | |
//button pressed action here | |
digitalWrite(M1T1, LOW); //Set Forward Rotation Direction | |
digitalWrite(M1T2, HIGH); //Set Forward Rotation Direction | |
digitalWrite(M2T1, LOW); //Set Reverse Rotation Direction | |
digitalWrite(M2T2, HIGH); //Set Reverse Rotation Direction | |
analogWrite(ENAPWM1, 255); //Set Speed 0-255 | |
analogWrite(ENAPWM2, 255); //Set Speed 0-255 | |
break; | |
case BUTTON_CIRCLE: | |
Serial.println("Circle"); | |
//button pressed action here | |
//stop all motors | |
digitalWrite(M1T1, LOW); | |
digitalWrite(M1T2, LOW); | |
digitalWrite(M2T1, LOW); | |
digitalWrite(M2T2, LOW); | |
break; | |
default: | |
Serial.print("Unrecognized code received: 0x"); | |
Serial.println(results.value, HEX); | |
break; | |
} | |
irrecv.resume(); // Receive the next value | |
} | |
//if robot senses an object is too close, stop, back up a little, repeat until OK | |
if(inches < 4) | |
{ | |
Serial.print("inches less than 4, "); | |
Serial.println(inches); | |
//if (reset_Value == 0) Note: reset_Value not currently used | |
{ | |
//stop all motors as long as sensed object is too close | |
digitalWrite(M1T1, LOW); | |
digitalWrite(M1T2, LOW); | |
digitalWrite(M2T1, LOW); | |
digitalWrite(M2T2, LOW); | |
delay(1000); //time to unfreeze robot | |
//reverse robot for short period of time. | |
digitalWrite(M1T1, HIGH); //Set Reverse Rotation Direction | |
digitalWrite(M1T2, LOW); //Set Reverse Rotation Direction | |
digitalWrite(M2T1, LOW); //Set Reverse Rotation Direction | |
digitalWrite(M2T2, HIGH); //Set Reverse Rotation Direction | |
analogWrite(ENAPWM1, 180); //Set Speed 0-255 | |
analogWrite(ENAPWM2, 180); //Set Speed 0-255 | |
delay(3500); | |
//stop all motors | |
digitalWrite(M1T1, LOW); | |
digitalWrite(M1T2, LOW); | |
digitalWrite(M2T1, LOW); | |
digitalWrite(M2T2, LOW); | |
//reset_Value = 1; | |
} | |
//else | |
//{ | |
//reset_Value = 0; | |
//} | |
} | |
}//****endof infinite loop**** | |
//SUPPORTING FUNCTIONS | |
/***********************************/ | |
long microsecondsToInches(long microseconds) { | |
// According to Parallax's datasheet for the PING))), there are | |
// 73.746 microseconds per inch (i.e. sound travels at 1130 feet per | |
// second). This gives the distance travelled by the ping, outbound | |
// and return, so we divide by 2 to get the distance of the obstacle. | |
// See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf | |
return microseconds / 74 / 2; | |
} | |
/***********************************/ | |
long microsecondsToCentimeters(long microseconds) { | |
// The speed of sound is 340 m/s or 29 microseconds per centimeter. | |
// The ping travels out and back, so to find the distance of the | |
// object we take half of the distance travelled. | |
return microseconds / 29 / 2; | |
} | |
/***********************************/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment