Previously, our sketches only made the BOE Shield-Bot execute a list of movements predefined by you, the programmer. Now that you can write a sketch to make the Arduino monitor whisker switches and trigger action in response, you can also write a sketch that lets the BOE Shield-Bot drive and select its own maneuver if it bumps into something. This is an example of autonomous robot navigation.
The RoamingWithWhiskers sketch makes the BOE Shield-Bot go forward while monitoring its whisker inputs, until it encounters an obstacle with one or both of them. As soon as the Arduino senses whisker electrical contact, it uses an if…else if…else statement to decide what to do. The decision code checks for various whisker pressed/not pressed combinations, and calls navigation functions from Chapter 4 [1] to execute back-up-and-turn maneuvers. Then, the BOE Shield-Bot resumes forward motion until it bumps into another obstacle.
Let’s try the sketch first, and then take a closer look at how it works.
// Robotics with the BOE Shield - RoamingWithWhiskers // Go forward. Back up and turn if whiskers indicate BOE Shield bot bumped // into something. #include <Servo.h> // Include servo library Servo servoLeft; // Declare left and right servos Servo servoRight; void setup() // Built-in initialization block { pinMode(7, INPUT); // Set right whisker pin to input pinMode(5, INPUT); // Set left whisker pin to input tone(4, 3000, 1000); // Play tone for 1 second delay(1000); // Delay to finish tone servoLeft.attach(13); // Attach left signal to pin 13 servoRight.attach(12); // Attach right signal to pin 12 } void loop() // Main loop auto-repeats { byte wLeft = digitalRead(5); // Copy left result to wLeft byte wRight = digitalRead(7); // Copy right result to wRight if((wLeft == 0) && (wRight == 0)) // If both whiskers contact { backward(1000); // Back up 1 second turnLeft(800); // Turn left about 120 degrees } else if(wLeft == 0) // If only left whisker contact { backward(1000); // Back up 1 second turnRight(400); // Turn right about 60 degrees } else if(wRight == 0) // If only right whisker contact { backward(1000); // Back up 1 second turnLeft(400); // Turn left about 60 degrees } else // Otherwise, no whisker contact { forward(20); // Forward 1/50 of a second } } void forward(int time) // Forward function { servoLeft.writeMicroseconds(1700); // Left wheel counterclockwise servoRight.writeMicroseconds(1300); // Right wheel clockwise delay(time); // Maneuver for time ms } void turnLeft(int time) // Left turn function { servoLeft.writeMicroseconds(1300); // Left wheel clockwise servoRight.writeMicroseconds(1300); // Right wheel clockwise delay(time); // Maneuver for time ms } void turnRight(int time) // Right turn function { servoLeft.writeMicroseconds(1700); // Left wheel counterclockwise servoRight.writeMicroseconds(1700); // Right wheel counterclockwise delay(time); // Maneuver for time ms } void backward(int time) // Backward function { servoLeft.writeMicroseconds(1300); // Left wheel clockwise servoRight.writeMicroseconds(1700); // Right wheel counterclockwise delay(time); // Maneuver for time ms }
The if...else if...else statement in the loop function checks the whiskers for any states that require attention. The statement starts with if((wLeft == 0) && (wRight == 0)). Translated to English, it reads “if the wLeft variable AND the wRight variable both equal zero.” If both variables are zero, the two calls in the if statement’s code block get executed: backward(1000) and turnLeft(800).
if((wLeft == 0) && (wRight == 0)) // If both whiskers contact { backward(1000); // Back up 1 second turnLeft(800); // Turn left about 120 degrees }
In the if…else if…else statement, the sketch skips code blocks with conditions that are not true, and keeps checking until it either finds a condition that’s true or runs out of conditions. When the sketch finds a true statement, it executes whatever is in its code block, then it skips to the end of the if…else if…else statement without checking any more conditions, and moves on to whatever else comes next in the sketch.
So, if both whiskers are not pressed, that first if statement is not true and its code block is skipped. The sketch will check the first else if statement. So, maybe the left whisker is pressed and the calls in this statement’s code block will run. After backing up for one second and turning left for 0.4 seconds, the sketch skips the rest of the conditions and moves on to whatever comes after that last else statement.
else if(wLeft == 0) // If only left whisker contact { backward(1000); // Back up 1 second turnRight(400); // Turn right about 60 degrees }
If it’s the right whisker that detects an obstacle, the first two code blocks will be skipped, and the if(wRight == 0) block will run.
else if(wRight == 0) // If only right whisker contact { backward(1000); // Back up 1 second turnLeft(400); // Turn left about 60 degrees }
An else condition functions as a catch-all for when none of the statements preceding it were true. It’s not required, but in this case, it’s useful for when no whiskers are pressed. If that’s the case, it allows the BOE Shield-Bot to roll forward for 20 ms. Why so little time before the loop repeats? The small forward time before rechecking allows the BOE Shield-Bot to respond quickly to changes in the whisker sensors as it rolls forward.
else // Otherwise, no whisker contact { forward(20); // Forward 1/50 of a second }
The forward, backward, turnLeft and turnRight functions were introduced in Chapter 4, Activity #5 [2], and are used in the MovementsWithSimpleFunctions [3]sketch. These functions certainly simplified the coding. (Hopefully, they also help demonstrate that all the navigation coding practice from Chapter 4 has its uses!)
You can also modify the sketch’s if...else if...else statements to make the LED indicators broadcast which maneuver the BOE Shield-Bot is running. Just add digitalWrite calls that send HIGH and LOW signals to the indicator LED circuits. Here is an example:
if((wLeft == 0) && (wRight == 0)) // If both whiskers contact { digitalWrite(8, HIGH); // Left LED on digitalWrite(2, HIGH); // Right LED on backward(1000); // Back up 1 second turnLeft(800); // Turn left about 120 degrees } else if(wLeft == 0) // If only left whisker contact { digitalWrite(8, HIGH); // Left LED on digitalWrite(2, LOW); // Right LED off backward(1000); // Back up 1 second turnRight(400); // Turn right about 60 degrees } else if(wRight == 0) // If only right whisker contact { digitalWrite(8, LOW); // Left LED off digitalWrite(2, HIGH); // Right LED on backward(1000); // Back up 1 second turnLeft(400); // Turn left about 60 degrees } else // Otherwise, no whisker contact { digitalWrite(8, LOW); // Left LED off digitalWrite(2, LOW); // Right LED off forward(20); // Forward 1/50 of a second }
pinMode(8, OUTPUT); // Left LED indicator -> output pinMode(2, OUTPUT); // Right LED indicator -> output
Links
[1] https://learn.parallax.com/216
[2] https://learn.parallax.com/224
[3] https://learn.parallax.com/226