Tactile switches are also called bumper switches or touch switches, and they have many uses in robotics. A robot programmed to pick up an object and move it to another conveyer belt might rely on a tactile switch to detect the object. Automated factory lines might use tactile switches to count objects, and to align parts for a certain step in a manufacturing process. In each case, switches provide inputs that trigger some form of programmed output. The inputs are electronically monitored by the equipments processor, which takes different actions depending on if the switch is pressed or not pressed.
In this chapter, you will build tactile switches, called whiskers, onto your BOE Shield-Bot and test them. You will then program the BOE Shield-Bot to monitor the states of these switches, and to decide what to do when it encounters an obstacle. The end result will be autonomous navigation by touch.
Whisker switches give the BOE Shield-Bot the ability to sense its surroundings through touch as it roams around, much like a cat’s whiskers. The activities in this chapter use the whiskers by themselves, but they can also be combined with other sensors you will learn about in later chapters.
Remember subsystem testing? First, we’ll build the whiskers circuits and write code to check their input states before using them in navigation sketches.
(2) whisker wires
(2) 7/8″ pan head 4-40 Phillips screws
(2) ½″ round spacer
(2) nylon washers, size #4
(2) 3-pin m/m headers
(2) resistors, 220 Ω (red-red-brown)
(2) resistors, 10 kΩ (brown-black-orange)
(misc) jumper wires
The whiskers are connected to ground (Vss) because the plated holes at the outer edge of the board are all connected to Vss. The metal standoffs and screws provide the electrical connection to each whisker.
Since each whisker is connected to digital I/O, the Arduino can be programmed to detect which voltage is applied to each circuit, 5 V or 0 V. First, set each pin to input mode with pinMode(pin, mode), and then detect the pin’s state, HIGH or LOW, with digitalRead(pin) function.
Take a look at the illustrations below. On the left, the circuit applies 5 V when the whisker is not pressed, so digitalRead(7) returns 1 (HIGH). On the right, the circuit applies 0 V when the whisker is pressed, so digitalRead(7) returns 0 (LOW).
Most importantly, your sketch can store the return values in variables, such as wLeft and wRight, and then use them to trigger actions or make decisions. The next example sketch will demonstrate how.
Switch Lingo: Each whisker is both the mechanical extension and the ground electrical connection of a normally open (off until pressed) momentary (on only while pressed) single-pole (one set of electrical contact points), single-throw (only one position conducts) switch.
The next sketch tests the whiskers to make sure they are functioning properly, by displaying the binary values returned by digitalRead(7) and digitalRead(5). This way, you can press each whisker against its 3-pin header on the breadboard, and see if the Arduino’s digital pin is sensing the electrical contact.
When neither whisker is pressed up against its 3-pin header, you can expect your Serial Monitor to display two columns of 1’s, one for each whisker. If you press just the right whisker, the right column should report 0, and the display should read 10. If you press just the left whisker, the left column should report 1 and the display should read 01. Of course, if you press both whiskers, it should display 00.
Active-low Output
The whisker circuits are wired for active-low output, which means that they each send a low signal when they are pressed (active) and a high signal when they are not pressed. Since digitalRead returns 0 for a low signal and 1 for a high signal, 0 is what tells your sketch that a whisker is pressed, and 1 tells it that a whisker is not pressed.
/* * Robotics with the BOE Shield - DisplayWhiskerStates * Display left and right whisker states in Serial Monitor. * 1 indicates no contact; 0 indicates contact. */ void setup() // Built-in initialization block { tone(4, 3000, 1000); // Play tone for 1 second delay(1000); // Delay to finish tone pinMode(7, INPUT); // Set right whisker pin to input pinMode(5, INPUT); // Set left whisker pin to input Serial.begin(9600); // Set data rate to 9600 bps } void loop() // Main loop auto-repeats { byte wLeft = digitalRead(5); // Copy left result to wLeft byte wRight = digitalRead(7); // Copy right result to wRight Serial.print(wLeft); // Display left whisker state Serial.println(wRight); // Display right whisker state delay(50); // Pause for 50 ms }
These steps are important!
Seriously, you’ve got to make sure your circuit and code pass these tests before continuing. The rest of the examples in this chapter rely on the whiskers working correctly. If you haven’t tested and corrected any errors, the rest of the examples won’t work right.
In the setup function, pinMode(7, INPUT) and pinMode(5, INPUT) set digital pins 7 and 5 to input so they can monitor the voltages applied by the whisker circuits.
pinMode(7, INPUT); // Set right whisker pin to input pinMode(5, INPUT); // Set left whisker pin to input
In the loop function, each call to digitalRead returns a 0 if the whisker is pressed or 1 if it is not. Those values get copied to variables named wLeft and wRight, which are short for whisker-left and whisker-right.
byte wLeft = digitalRead(5); // Copy left result to wLeft byte wRight = digitalRead(7); // Copy right result to wRight
Next, Serial.print displays the value of wLeft to the Serial Monitor, and Serial.println displays the value of wRight and a carriage return.
Serial.print(wLeft); // Display left whisker state Serial.println(wRight); // Display right whisker state
Before the next repetition of the loop function, there’s a delay(50). This slows down the number of messages the Serial Monitor receives each second. Although it’s probably not needed, we leave it in to prevent possible computer buffer overruns (too much data to store) for older hardware and certain operating systems.
Your sketch doesn’t actually need to use variables to store the values from digitalRead. Instead, the (1 or 0) value that digitalRead returns can be used directly by nesting the function call inside Serial.print and sending its return value straight to the Serial Monitor. In that case, your loop function would look like this:
void loop() // Main loop auto-repeats { Serial.print(digitalRead(5)); // Display wLeft Serial.println(digitalRead(7)); // Display wRight delay(50); // Pause for 50 ms }
What if you have to test the whiskers at some later time away from a computer? In that case, the Serial Monitor won’t be available, so what can you do? One solution would be to use LED circuits to display the whisker states. All it takes is a simple sketch that turns an LED on when a whisker is pressed or off when it’s not pressed.
(2) resistors, 220 Ω (red-red-brown)
(2) LEDs, red
pinMode(8, OUTPUT); // Left LED indicator -> output pinMode(2, OUTPUT); // Right LED indicator -> output
if(wLeft == 0) // If left whisker contact { digitalWrite(8, HIGH); // Left LED on } else // If no left whisker contact { digitalWrite(8, LOW); // Left LED off } if(wRight == 0) // If right whisker contact { digitalWrite(2, HIGH); // Right LED on } else // If no right whisker contact { digitalWrite(2, LOW); // Right LED off }
Recall that if...else statements execute blocks of code based on conditions. Here, if wLeft stores a zero, it executes the digitalWrite(8, HIGH) call. If wLeft instead stores a 1, it executes the digitalWrite(8, LOW) call. The result? The left LED turns on when the left whisker is pressed or off when it’s not pressed. The second if…else statement does the same job with wRight and the right LED circuit.
/* * Robotics with the BOE Shield - TestWhiskersWithLeds * Display left and right whisker states in Serial Monitor. * 1 indicates no contact; 0 indicates contact. * Display whisker states with LEDs. LED on indicates contact; * off indicates none. */ void setup() // Built-in initialization block { pinMode(7, INPUT); // Set right whisker pin to input pinMode(5, INPUT); // Set left whisker pin to input pinMode(8, OUTPUT); // Left LED indicator -> output pinMode(2, OUTPUT); // Right LED indicator -> output tone(4, 3000, 1000); // Play tone for 1 second delay(1000); // Delay to finish tone Serial.begin(9600); // Set serial data rate to 9600 } 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) // If left whisker contact { digitalWrite(8, HIGH); // Left LED on } else // If no left whisker contact { digitalWrite(8, LOW); // Left LED off } if(wRight == 0) // If right whisker contact { digitalWrite(2, HIGH); // Right LED on } else // If no right whisker contact { digitalWrite(2, LOW); // Right LED off } Serial.print(wLeft); // Display wLeft Serial.println(wRight); // Display wRight delay(50); // Pause for 50 ms }
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 [2] 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 [3], and are used in the MovementsWithSimpleFunctions [4]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
You may have noticed that with the last sketch, the BOE Shield-Bot tends to get stuck in corners. As it enters a corner, its left whisker contacts the wall on the left, so it backs up and turns right. When the BOE Shield-Bot moves forward again, its right whisker contacts the wall on the right, so it backs up and turns left. Then it contacts the left wall again, and then the right wall again, and so on, until somebody rescues it from its predicament.
RoamingWithWhiskers can be expanded to detect this problem and act upon it. The trick is to count the number of times that alternate whiskers make contact with objects. To do this, the sketch has to remember what state each whisker was in during the previous contact. Then, it has to compare those states to the current whisker contact states. If they are opposite, then add 1 to a counter. If the counter goes over a threshold that you (the programmer) have determined, then it’s time to do a U-turn and escape the corner, and also reset the counter.
This next sketch relies on the fact that you can nest if statements, one inside another. The sketch checks for one condition, and if that condition is true, it checks for another condition within the first if statement’s code block. We’ll use this technique to detect consecutive alternate whisker contacts in the next sketch.
This sketch will cause your BOE Shield-Bot to execute a reverse and U-turn to escape a corner at either the fourth or fifth alternate whisker press, depending on which one was pressed first.
/* * Robotics with the BOE Shield - EscapingCorners * Count number of alternate whisker contacts, and if it exceeds 4, get out * of the corner. */ #include <Servo.h> // Include servo library Servo servoLeft; // Declare left and right servos Servo servoRight; byte wLeftOld; // Previous loop whisker values byte wRightOld; byte counter; // For counting alternate corners void setup() // Built-in initialization block { pinMode(7, INPUT); // Set right whisker pin to input pinMode(5, INPUT); // Set left whisker pin to input pinMode(8, OUTPUT); // Left LED indicator -> output pinMode(2, OUTPUT); // Right LED indicator -> output 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 wLeftOld = 0; // Init. previous whisker states wRightOld = 1; counter = 0; // Initialize counter to 0 } void loop() // Main loop auto-repeats { // Corner Escape byte wLeft = digitalRead(5); // Copy right result to wLeft byte wRight = digitalRead(7); // Copy left result to wRight if(wLeft != wRight) // One whisker pressed? { // Alternate from last time? if ((wLeft != wLeftOld) && (wRight != wRightOld)) { counter++; // Increase count by one wLeftOld = wLeft; // Record current for next rep wRightOld = wRight; if(counter == 4) // Stuck in a corner? { wLeft = 0; // Set up for U-turn wRight = 0; counter = 0; // Clear alternate corner count } } else // Not alternate from last time { counter = 0; // Clear alternate corner count } } // Whisker Navigation 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 }
This sketch is a modified version of RoamingWithWhiskers, so we’ll just look at the new code for detecting and escaping corners.
First, three global byte variables are added: wLeftOld, wRightOld, and counter. The wLeftOld and wRightOld variables store the whisker states from a previous whisker contact so that they can be compared with the states of the current contact. Then counter is used to track the number of consecutive, alternate contacts.
byte wLeftOld; // Previous loop whisker values byte wRightOld; byte counter; // For counting alternate corners
These variables are initialized in the setup function. The counter variable can start with zero, but one of the “old” variables has to be set to 1. Since the routine for detecting corners always looks for an alternating pattern, and compares it to the previous alternating pattern, there has to be an initial alternate pattern to start with. So, wLeftOld and wRightOld are assigned initial values in the setup function before the loop function starts checking and modifying their values.
wLeftOld = 0; // Initialize previous whisker wRightOld = 1; // states counter = 0; // Initialize counter to 0
The first thing the code below // Corner Escape has to do is check if one or the other whisker is pressed. A simple way to do this is to use the not-equal operator (!= ) in an if statement. In English, if(wLeft != wRight) means “if the wLeft variable is not equal to the wRight variable…”
// Corner Escape if(wLeft != wRight) // One whisker pressed?
If they are not equal it means one whisker is pressed, and the sketch has to check whether it’s the opposite pattern as the previous whisker contact. To do that, a nested if statement checks if the current wLeft value is different from the previous one and if the current wRight value is different from the previous one. That’s if( (wLeft != wLeftOld) && (wRight != wRightOld)). If both conditions are true, it’s time to add 1 to the counter variable that tracks alternate whisker contacts. It’s also time to remember the current whisker pattern by setting wLeftOld equal to the current wLeft and wRightOld equal to the current wRight.
if((wLeft != wLeftOld) && (wRight != wRightOld)) { counter++; // Increase count by one wLeftOld = wLeft; // Record current for next rep wRightOld = wRight;
If this is the fourth consecutive alternate whisker contact, then it’s time to reset the counter variable to 0 and execute a U-turn. When the if(counter == 4) statement is true, its code block tricks the whisker navigation routine into thinking both whiskers are pressed. How does it do that? It sets both wLeft and wRight to zero. This makes the whisker navigation routine think both whiskers are pressed, so it makes a U-turn.
if(counter == 4) // Stuck in a corner? { wLeft = 0; // Set up whisker states for U-turn wRight = 0; counter = 0; // Clear alternate corner count } }
But, if the conditions in if((wLeft != wLeftOld) && (wRight != wRightOld)) are not all true, it means that this is not a sequence of alternating whisker contacts anymore, so the BOE Shield-Bot must not be stuck in a corner. In that case, the counter variable is set to zero so that it can start counting again when it really does find a corner.
else // Not alternate from last time { counter = 0; // Clear alternate corner count } }
One thing that can be tricky about nested if statements is keeping track of opening and closing braces for each statement’s code block. The picture below shows some nested if statements from the last sketch. In the Arduino editor, you can double-click on a brace to highlight its code block. But sometimes, printing out the code and simply drawing lines to connect opening and closing braces helps to see all the blocks at once, which is useful for finding bugs in deeply nested code.
In this picture, the if(wLeft != wRight) statement’s code block contains all the rest of the decision-making code. If it turns out that wLeft is equal to wRight, the Arduino skips to whatever code follows that last closing brace }. The second level if statement compares the old and new wLeft and wRight values with if ((wLeft != wLeftOld) && (wRight != wRightOld)). Notice that its code block ending brace is just below the one for the if(counter==4) block. The if ((wLeft != wLeftOld) && (wRight != wRightOld)) statement also has an else condition with a block that sets counter to zero if the whisker values are not opposite from those of the previous contact.
One of the if statements in EscapingCorners checks to see if counter has reached 4.
This chapter introduced the first sensor system for the BOE Shield-Bot, and allowed the robot to roam around on its own and navigate by touch. The project built on skills acquired in the last chapter, and employed a variety of new ones:
// Robotics with the BOE Shield Chapter 5, Exercise 1 // Value from 0 to 3 indicates whisker states: // 0 = both, 1 = left, 2 = right, 3 = neither. void setup() // Built-in initialization block { tone(4, 3000, 1000); // Play tone for 1 second delay(1000); // Delay to finish tone pinMode(7, INPUT); // Set right whisker pin to input pinMode(5, INPUT); // Set left whisker pin to input Serial.begin(9600); // Set data rate to 9600 bps } void loop() // Main loop auto-repeats { byte whiskers = 2 * digitalRead(5); whiskers += digitalRead(7); Serial.println(whiskers); // Display wLeft delay(50); // Pause for 50 ms }
void loop() // Main loop auto-repeats { byte wLeft = digitalRead(5); // Copy right result to wLeft byte wRight = digitalRead(7); // Copy left result to wRight if((wLeft == 0) && (wRight == 0)) // If both whiskers contact { pause(500); // Pause motion for 0.5 seconds backward(1000); // Back up 1 second turnLeft(800); // Turn left about 120 degrees } else if(wLeft == 0) // If only left whisker contact { pause(500); // Pause motion for 0.5 seconds backward(1000); // Back up 1 second turnRight(400); // Turn right about 60 degrees } else if(wRight == 0) // If only right whisker contact { pause(500); // Pause motion for 0.5 seconds 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 pause(int time) // Pause drive wheels { servoLeft.writeMicroseconds(1500); // Left wheel stay still servoRight.writeMicroseconds(1500); // Right wheel stay still delay(time); // Maneuver for time ms }
void loop() // Main loop auto-repeats { byte wLeft = digitalRead(5); // Copy right result to wLeft byte wRight = digitalRead(7); // Copy left result to wRight if((wLeft == 0) && (wRight == 0)) // If both whiskers contact { pause(500); // Pause motion for 0.5 seconds backward(1000); // Back up 1 second turnLeft(800); // Turn left about 120 degrees } else if(wLeft == 0) // If only left whisker contact { pause(500); // Pause motion for 0.5 seconds backward(1000); // Back up 1 second turnRight(400); // Turn right about 60 degrees } else if(wRight == 0) // If only right whisker contact { pause(500); // Pause motion for 0.5 seconds 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 } }
// RoamingWithWhiskers Chapter 5 Project 1 // 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 right result to wLeft byte wRight = digitalRead(7); // Copy left result to wRight if((wLeft == 0) && (wRight == 0)) // If both whiskers contact { tone(4, 4000, 100); // Play a 0.1 ms tone pause(200); // Stop for 0.2 seconds tone(4, 4000, 100); // Play a 0.1 ms tone pause(200); // Stop for 0.2 seconds backward(1000); // Back up 1 second turnLeft(800); // Turn left about 120 degrees } else if(wLeft == 0) // If only left whisker contact { tone(4, 4000, 100); // Play a 0.1 ms tone pause(200); // Stop for 0.2 seconds backward(1000); // Back up 1 second turnRight(400); // Turn right about 60 degrees } else if(wRight == 0) // If only right whisker contact { tone(4, 4000, 100); // Play a 0.1 ms tone pause(200); // Stop for 0.2 seconds 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 pause(int time) // Backward function { servoLeft.writeMicroseconds(1500); // Left wheel clockwise servoRight.writeMicroseconds(1500); // Right wheel counterclockwise delay(time); // Maneuver for time ms } 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 }
// Robotics with the BOE Shield - Chapter 5, project 2 – WhiskerCircle // BOE Shield-Bot navigates a circle of 1 yard diameter. // Tightens turn if right whisker pressed, or reduces turn if left whisker // is pressed. #include <Servo.h> // Include servo library Servo servoLeft; // Declare left and right servos Servo servoRight; int turn; 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 Port 13 servoRight.attach(12); // Attach right signal to Port 12 turn = 0; // servoLeft.detach(); // Stop sending servo signals // servoRight.detach(); } void loop() // Main loop auto-repeats { // Nothing needs repeating int wLeft = digitalRead(5); int wRight = digitalRead(7); if(wLeft == 0) { turn -= 10; } else if(wRight == 0) { turn += 10; } // Arc to the right servoLeft.writeMicroseconds(1600); // Left wheel counterclockwise servoRight.writeMicroseconds(1438 + turn); // Right wheel clockwise slower delay(50); // ...for 25.5 seconds }
Links
[1] https://learn.parallax.com/sites/default/files/content/shield/robo_ch5/RoboticsBOEShield_Ch5_20120313.zip
[2] https://learn.parallax.com/216
[3] https://learn.parallax.com/224
[4] https://learn.parallax.com/226
[5] https://learn.parallax.com/234