The final program for the robot combines the building blocks we've worked on in the tutorial:
- Using the Circuit Playground library for microphone, NeoPixel, and switch functions
- Taking FFT information and looking for our command sounds
- Moving the robot based on the command soundsĀ
You might want to review how to program Circuit Playground. And definitely use a good USB data cable and not a charging cable (even I have done this and said "doh!").
Here is the code to run on the Circuit Playground for the tone controlled robot:
// A sound controlled robot for the Adafruit Circuit Playground board
// This uses two continuous rotation servos for movement and listens to
// commands via the built-in mic. Sound processing is done with the
// Circuit Playground FFT library function for AVR microcontrollers.
// The fast Fourier transform (FFT) algorithm converts a signal from the
// time domain to the frequency domain -- here, turning a specific audio
// signal into a command to the robot.
// Note the Circuit Playground slide switch when set to "-" stops the motors
// It is not a battery off switch so unplug or turn off the battery to save
// power when not in use.
// By Anne Barela for Adafruit Industries September, 2016 Version 1.0
#include <Adafruit_CircuitPlayground.h> // Circuit Playground library
#include <Servo.h> // Audiono servo control library
// Global Definitions ------------------------------------------------------------
// Fourier Transform
#define BINS 32 // FFT output is output in this many bins
#define FRAMES 4 // This many FFT cycles are averaged for leveling
#define THRESHOLD 150 // Max bin value to say a tone was detected
// Servo objects
Servo servoLeft; // Define left servo
#define leftStopAngle 96.2 // Angle that left servo will stop (calibrated)
Servo servoRight; // Define right servo
#define rightStopAngle 95.3 // Angle that right servo will stop (calibrated)
uint8_t moving = 0; // Is the robot currently moving value (0 = no, 1 = yes)
// SETUP FUNCTION - this runs once to set the robot up when it is powered on
void setup() {
servoLeft.attach(12); // Set left servo to digital pin 12
servoRight.attach(6); // Set right servo to digital pin 6
CircuitPlayground.begin(); // Initialize the CP library
CircuitPlayground.setBrightness(20); // NeoPixels are another visual que on what is happening
CircuitPlayground.clearPixels(); // Ensure all NeoPixels are off at start
Serial.begin(9600); // Set up the USB port for serial information for users
Serial.println("\n\nAdafruit Circuit Playground Robot");
stopRobot();
}
// LOOP FUNCTION - runs over and over to check what to do ---------------------
void loop() {
uint8_t i,j; // loop index values
uint16_t spectrum[BINS]; // FFT spectrum output buffer
uint16_t avg[BINS]; // The avaerage FFT values over FRAMES iterations
int16_t maxVal = 0, maxIndex = 0; // The maximum value and the FFT bin of that value
int8_t maxBins;
if( !CircuitPlayground.slideSwitch() ) { // if slide switch is moved to "-", shut robot down
if(moving) {
stopRobot(); // stop the robot
CircuitPlayground.clearPixels(); // turn all pixels off in low power mode
}
Serial.println("\nRobot stopped due to slide switch, move to '+' to resume");
return; // and go back to top of loop
}
for(j=1; j <= FRAMES; j++) { // Gather FRAMES samples of audio from the microphone
CircuitPlayground.mic.fft(spectrum); // This function gathers an audio sample and does FFT
for(i=0; i < BINS; i++) { // Add values to perform a simple average
if(spectrum[i] > 255) spectrum[i] = 255; // in library, sometimes values get "huge"
if(i == 0)
avg[i] = spectrum[i];
else
avg[i] = avg[i] + spectrum[i];
}
}
for(i=0; i < BINS; i++) { // For each output bin average
avg[i] = avg[i] / FRAMES; // calculate the average (unweighted)
}
maxBins = 0;
for(i=0; i < BINS; i++) { // Search for the highest value and the FFT bin it's in
if(avg[i] > 255) avg[i] = 255; // HACK 4 NOW
if(avg[i] >= maxVal) {
maxVal = avg[i]; // Important note: If there is an equal max value in higher bins
maxIndex = i; // note the later index (helps when recognizing a sound)
}
if(avg[i] >= 254) maxBins++;
}
// avg[] is now FRAME averaged FFT output, 32 bins.
// use onboard buttons to change behavior
if( CircuitPlayground.leftButton() ) {
maxIndex = 28; // stop
maxVal = THRESHOLD;
}
if( CircuitPlayground.rightButton() ) {
maxIndex = 11; // forward
maxVal = THRESHOLD;
}
// A detection is defined as a bin reaching a value of at least THRESHOLD
if( maxVal >= THRESHOLD ) {
if( maxBins > 3 ) { // Some loud broad spectrum sound, we don't want to act on that
Serial.println("\nMore than 3 bins are maxed out");
return;
}
// For visual review of the values the FFT has produced, print the FFTs 32 bins
for( uint8_t j=0; j < 32; j++) {
Serial.print(avg[j]);
Serial.print(" ");
}
Serial.println("");
// maxVal is the biggest bin, maxIndex is the index of that value
Serial.print("\nMax Value = "); Serial.print(maxVal);
Serial.print(", Index of Max Value = "); Serial.println(maxIndex);
CircuitPlayground.clearPixels(); // clear old pixel values
switch( maxIndex ) { // based on which bin had the detection, act on it
case 10: // 8000 hertz
case 11: unused(); // 7822 hertz
break;
case 18:
case 19: CircuitPlayground.strip.setPixelColor(0,0,240,0); // 2795 hertz
forward(); // forward
break;
case 20: // 2957 hertz
case 21: unused(); // 3094 hertz
break;
case 22: CircuitPlayground.strip.setPixelColor(1,0,240,0); // 3250 hertz
turnLeft();
break;
case 23: // 3436 hertz
case 24: unused(); // 3605 hertz
break;
case 25: CircuitPlayground.strip.setPixelColor(2,0,240,0); // 3700 hertz
turnRight();
break;
case 26: // 3876 hertz
case 27: unused(); // 4046 hertz
break;
case 28: CircuitPlayground.strip.setPixelColor(3,0,240,0); // 4192 hertz
stopRobot();
break;
case 29: // 4339 hertz
case 30: unused(); // 4517 hertz
break;
case 31: CircuitPlayground.strip.setPixelColor(4,0,240,0); // 4640 hertz
// reverse();
break;
default: CircuitPlayground.strip.setPixelColor(9,200,0,0); // if any other bin, light NeoPixel #9 red
stopRobot();
break;
} // end switch
CircuitPlayground.strip.show(); // show the neopixel assigned to the bin
} // end if
} // end function loop
// Servo motion routines for robot movement forward, reverse, turns, and stop
void stopRobot() {
moving = 0; // let program know we are stopped
servoLeft.write(leftStopAngle); // Get these values from a calibration routine
servoRight.write(rightStopAngle); // as continuous servos don't stop at exactly 90.0
Serial.println("Stopped");
}
void forward() {
if( moving == 1 ) { // if the robot currently is moving NEEDED?
stopRobot(); // stop it
}
else {
moving = 1; // flag we are going to move
}
servoLeft.write(0);
servoRight.write(180);
}
void reverse() {
if( moving == 1 ) { // if the robot currently is moving
stopRobot(); // stop it
}
else {
moving = 1; // flag we are going to move
}
servoLeft.write(180);
servoRight.write(0);
}
void turnRight() {
if( moving == 1 ) { // if the robot currently is moving
stopRobot(); // stop it
}
else {
moving = 1; // flag we are going to move
}
servoLeft.write(180);
servoRight.write(180);
}
void turnLeft() {
if( moving == 1 ) { // if the robot currently is moving
stopRobot(); // stop it
}
else {
moving = 1; // flag we are going to move
}
servoLeft.write(0);
servoRight.write(0);
}
void unused() { // Frequency not used at present
CircuitPlayground.strip.setPixelColor(8,0,0,255); // display a blue light on pixel 8
}
Do you wheels turn when in the robot stopĀ mode? You need to be sure the calibration calues for your servos are determined as on the previous page. I doubt the values I have will be the same as yours. They should be between 83 and 97.
Page last edited March 08, 2024
Text editor powered by tinymce.