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.
Text editor powered by tinymce.