Roaming with PING Arduino Code
Here is the Arduino code listing for the Shield-Bot and PING))) sensor on a servo mounting bracket:
/*
* Roam_with_Ping_on_Servo_Turret
* BOE Shield-Bot application roams with Ping))) mounted on servo turret.
* Source: https://learn.parallax.com/BoeShield/RoamingPingShieldBot
*
* IMPORTANT: This is an early revision. Please check back periodically,
* and if you find any bugs, please email me at alindsay@parallax.com.
*
* Created May 17th, 2012
* by Andy Lindsay
* v0.82
*/
#include <Servo.h> // Include servo library
Servo servoLeft; // Servo object instances
Servo servoRight;
Servo servoTurret;
const int servoLeftPin = 13; // I/O Pin constants
const int servoRightPin = 12;
const int servoTurretPin = 11;
const int pingPin = 10;
const int piezoPin = 4;
const int msPerTurnDegree = 6; // For maneuvers
const int tooCloseCm = 30; // For distance decisions
const int bumpedCm = 6;
int ccwLim = 1400; // For turret servo control
int rtAngle = 900;
const int usTocm = 29; // Ping))) conversion constant
// Sequence of turret positions.
int sequence[] = {0, 2, 4, 6, 8, 10, 9, 7, 5, 3, 1};
// Declare array to store that many cm distance elements using pre-calculated
// number of elements in array.
const int elements = sizeof(sequence);
int cm[elements];
// Pre-calculate degrees per adjustment of turret servo.
const int degreesTurret = 180/(sizeof(sequence)/sizeof(int)-1);
int i = -1; // Global index
int sign = 1; // Sign of increments
int theta = -degreesTurret; // Set up initial turret angle
void setup() // Built-in initialization block
{
tone(piezoPin, 3000, 1000); // Play tone for 1 second
delay(1000); // Delay to finish tone
Serial.begin(9600); // Open serial connection
servoLeft.attach(servoLeftPin); // Attach left signal to pin 13
servoRight.attach(servoRightPin); // Attach right signal to pin 12
servoTurret.attach(servoTurretPin); // Attach turret signal to pin 12
maneuver(0, 0, 1000); // Stay still for 1 second
turret(0); // Set turret to 0 degrees
}
void loop() // Main loop auto-repeats
{
maneuver(200, 200); // Go full speed forward UFN
i++; // Increment turret index
// Advance turret servo to next position in sequence and wait for it to get there.
theta = sequence[i] * degreesTurret;
turret(theta);
delay(100);
cm[i] = cmDistance(); // Measure cm from this turret angle
// If object in +/- 36 degrees from center is within tooCloseCm threshold...
if ((sequence[i]>=3) && (sequence[i]<=7) && (cm[i] < tooCloseCm))
{
maneuver(0, 0); // Stop moving
int theta = findOpening(); // Get opening (in terms of sequence element
theta *= degreesTurret; // Convert sequence element to degree angle
// Convert degree angle to time the BOE Shield-Bot wheels will have to turn to face
// direction of turret.
int turnAngleTime = (90 - theta) * msPerTurnDegree;
if(turnAngleTime < 0) // If negative turning angle,
{
maneuver(-200, 200, -turnAngleTime); // then rotate CCW for turningAngleTime ms
}
else // If positive turning angle,
{
maneuver(200, -200, turnAngleTime); // then rotate CW for turningAngleTime ms
}
maneuver(200, 200); // Start going forward again
}
if(i == 10) // If turret at max, go back to zero
{
i = -1;
}
}
/*
* Control BOE Shield-Bot servo direction, speed, set and forget version.
* Parameters: speedLeft - left servo speed
* speedRight - right servo speed
* Backward Linear Stop Linear Forward
* -200 -100......0......100 200
*/
void maneuver(int speedLeft, int speedRight)
{
// Call maneuver with just 1 ms blocking; servos will keep going indefinitely.
maneuver(speedLeft, speedRight, 1);
}
/*
* Control BOE Shield-Bot servo direction, speed and maneuver duration.
* Parameters: speedLeft - left servo speed
* speedRight - right servo speed
* Backward Linear Stop Linear Forward
* -200 -100......0......100 200
* msTime - time to block code execution before another maneuver
* Source: https://learn.parallax.com/ManeuverFunction
*/
void maneuver(int speedLeft, int speedRight, int msTime)
{
servoLeft.writeMicroseconds(1500 + speedLeft); // Set Left servo speed
servoRight.writeMicroseconds(1500 - speedRight); // Set right servo speed
if(msTime==-1) // if msTime = -1
{
servoLeft.detach(); // Stop servo signals
servoRight.detach();
}
delay(msTime); // Delay for msTime
}
/*
* Position the horn of a Parallax Standard Servo
* Parameter: degreeVal in a range from 90 to -90 degrees.
*/
void turret(int degreeVal)
{
servoTurret.writeMicroseconds(ccwLim - rtAngle + (degreeVal * 10));
}
/*
* Get cm distance measurment from Ping Ultrasonic Distance Sensor
* Returns: distance measurement in cm.
*/
int cmDistance()
{
int distance = 0; // Initialize distance to zero
do // Loop in case of zero measurement
{
int us = ping(pingPin); // Get Ping))) microsecond measurement
distance = convert(us, usTocm); // Convert to cm measurement
delay(3); // Pause before retry (if needed)
}
while(distance == 0);
return distance; // Return distance measurement
}
/*
* Converts microsecond Ping))) round trip measurement to a useful value.
* Parameters: us - microsecond value from Ping))) echo time measurement.
* scalar - 29 for us to cm, or 74 for us to in.
* Returns: distance measurement dictated by the scalar.
*/
int convert(int us, int scalar)
{
return us / scalar / 2; // Echo round trip time -> cm
}
/*
* Initiate and capture Ping))) Ultrasonic Distance Sensor's round trip echo time.
* Parameter: pin - Digital I/O pin connected to Ping)))
* Returns: duration - The echo time in microseconds
* Source: Ping by David A. Mellis, located in File -> Examples -> Sensors
* To-Do: Double check timing against datasheet
*/
long ping(int pin)
{
long duration; // Variables for calculating distance
pinMode(pin, OUTPUT); // I/O pin -> output
digitalWrite(pin, LOW); // Start low
delayMicroseconds(2); // Stay low for 2 us
digitalWrite(pin, HIGH); // Send 5 us high pulse
delayMicroseconds(5);
digitalWrite(pin, LOW);
pinMode(pin, INPUT); // Set I/O pin to input
duration = pulseIn(pin, HIGH, 25000); // Measure echo time pulse from Ping)))
return duration; // Return pulse duration
}
/*
* Find an opening in system's 180-degree field of distance detection.
* Returns: Distance measurement dictated by the scalar
* To-do: Clean up and modularize
* Incorporate constants
*/
int findOpening()
{
int Ai; // Initial turret angle
int Af; // Final turret angle
int k = sequence[i]; // Copy sequence[i] to local variable
int ki = k; // Second copy of current sequence[i]
int inc; // Increment/decrement variable
int dt; // Time increment
int repcnt = 0; // Repetitions count
int sMin; // Minimum distance measurement
// Increment or decrement depending on where turret is pointing
if(k <= 5)
{
inc = 1;
}
else
{
inc = -1;
}
// Rotate turret until an opening becomes visible. If it reaches servo limit,
// turn back to first angle of detection and try scanning toward other servo limit.
// If still no opening, rotate robot 90 degrees and try again.
do
{
repcnt++; // Increment repetition count
if(repcnt > ((sizeof(sequence) / sizeof(int)))*2)// If no opening after two scans
{
maneuver(-200, -200, 100); // Back up, turn, and stop to try again
maneuver(-200, 200, 90*6);
maneuver(0, 0, 1);
}
k += inc; // Increment/decrement k
if(k == -1) // Change inc/dec value if limit reached
{
k = ki;
inc = -inc;
dt = 250; // Turret will take longer to get there
}
if(k == 11)
{
k = ki;
inc = -inc;
dt = 250;
}
// Look for index of next turret position
i = findIn(k, sequence, sizeof(sequence)/sizeof(int));
// Set turret to next position
int theta = sequence[i] * degreesTurret;
turret(theta); // Position turret
delay(dt); // Wait for it to get there
dt = 100; // Reset for small increment turret movement
cm[i] = cmDistance(); // Take Ping))) measurement
}
while(cm[i] < 30); // Keep checking to edge of obstacle
sMin = 1000; // Initialize minimum distance to impossibly large value
for(int t = 0; t <= 10; t++)
{
if(sMin > cm[t]) sMin = cm[t]; // Use sMin to track smallest distance
}
if(sMin < 6) // If less than 6 cm, back up a little
{
maneuver(-200, -200, 350);
k = -1; // Get turret ready to start over
}
maneuver(0, 0); // Stay still indefinitely
// Keep rotating turret until another obstacle that's under 30 cm is detected or the turret
// reaches the servo limit. Keep track of the maximum distance and the angle when this portion
// of the scan started and stopped.
Ai = sequence[i]; // Save initial angle when obstacle disappeared from view
k = sequence[i]; // Make a copy of the turret position again
int aMax = -2; // Initialize maximum distance measurements to impossibly small values
int cmMax = -2;
do // Loop for scan
{
k += inc; // Inc/dec turret position
// Look up index for turret position
i = findIn(k, sequence, sizeof(sequence)/sizeof(int));
int theta = sequence[i] * degreesTurret; // Position turret
turret(theta);
delay(100);
cm[i] = cmDistance(); // Measure distance
if(cm[i]>cmMax) // Keep track of max distance and angle(max distance)
{
cmMax = cm[i];
aMax = sequence[i];
}
}
while((cm[i] > 30)&&(sequence[i]!=0)&&(sequence[i]!=10));
// Keep repeating while the distance measurement > 30 cm, and the turret is not near its
// mechanical limits.
Af = sequence[i]; // Record final turret position
int A = Ai + ((Af-Ai)/2); // Calculate middle of opening
// Set turret index for straight ahead in prep for turn.
i = findIn(5, sequence, sizeof(sequence)/sizeof(int));
int theta = sequence[i] * degreesTurret;
turret(theta);
if(sMin < 7) // Turn further if too close
{
if (A < aMax) return aMax; else return A;
}
else
{
if (A < aMax) return A; else return aMax;
}
}
/*
* Finds the first instance of a value in an array.
* Parameters: value to search for
* array[] to search in
* elements - number of elements to search
* Returns: index where the matching element was found
*/
int findIn(int value, int array[], int elements)
{
for(int i = 0; i < elements; i++)
{
if(value == array[i]) return i;
}
return -1;
}