When working with what frequencies the robot responds to, the temptation is to whistle into the microphone and see what it does. This is what I did in uploading the Circuit Playground sample program vu meter which uses the microphone as a sound level meter. I did get some multicolor sounds with different whistles.
But do whistles allow you to control the robot? Maybe, if you are good at your whistles. Through testing the various bins my whistling fell in, I could get decent start and stop whistles. If I whistle in a frequency filling bins 5, 6, or 7, the robot moves forwards. A different, higher whistle filling one of the bins 8, 9, or 10, the robot stops. My whistles are not varied enough to get left and right turns. If you have some range of tones, you can change which bins make which motion in the program's switch
statement.
Here is the code for a two tone whistle bot. A lower tone starts the robot moving forward, a higher whistle stops the 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. // This version is designed to respond to two whistle tones for // start and stop. // By Anne Barela for Adafruit Industries September, 2016 #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 using Whistle Commands"); 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. if( CircuitPlayground.leftButton() ) { // use onboard buttons to change behavior maxIndex = 9; // stop maxVal = THRESHOLD; } if( CircuitPlayground.rightButton() ) { maxIndex = 7; // forward maxVal = THRESHOLD; } // A detection is defined as a bin reaching a value of at least THRESHOLD if( maxVal >= THRESHOLD ) { // 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 0: case 1: case 2: CircuitPlayground.strip.setPixelColor(0,0,240,0); // you can put a movement function call here break; case 3: case 4: CircuitPlayground.strip.setPixelColor(1,0,240,0); // you can put a movement function call here break; case 5: case 6: case 7: CircuitPlayground.strip.setPixelColor(2,10,225,10); // Low whistle (3rd LED Green) forward(); // forward break; case 8: case 9: case 10: CircuitPlayground.strip.setPixelColor(3,225,00,10); // higher whistle (4th LED red) stopRobot(); break; case 11: // 7822 hertz CircuitPlayground.strip.setPixelColor(3,0,240,0); // you can put a movement function call here break; case 18: case 19: CircuitPlayground.strip.setPixelColor(4,0,240,0); // 2795 hertz // you can put a movement function call here break; case 20: // 2957 hertz case 21: // 3094 hertz case 22: CircuitPlayground.strip.setPixelColor(5,0,240,0); // 3250 hertz // you can put a movement function call here break; case 23: // 3436 hertz case 24: // 3605 hertz case 25: CircuitPlayground.strip.setPixelColor(6,0,240,0); // 3700 hertz // you can put a movement function call here break; case 26: // 3876 hertz case 27: // 4046 hertz case 28: CircuitPlayground.strip.setPixelColor(7,0,240,0); // 4192 hertz (motor noise?) // you can put a movement function call here break; case 29: // 4339 hertz case 30: // 4517 hertz case 31: CircuitPlayground.strip.setPixelColor(8,0,240,0); // 4640 hertz (often goes here) // you can put a movement function call here break; default: CircuitPlayground.strip.setPixelColor(9,200,0,0); // if any other bin, light NeoPixel #9 red break; } // end switch CircuitPlayground.strip.show(); // show the neopixel assigned to the bin } // end if } // end function loop // Servo motion function 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); }
Using the Whistle Bot
Use a lower tone whistle to start it forward, a higher whistle to stop. You'll probably need to experiment with your whistle tones to find the right ones. It currently is calibrated for more of a male whistle (I'm told by my wife).
Whistle and see which NeoPixels light up. For me it's the 4th and 5th pixels (#3 and #4). If you see something like the second and third (NeoPixels #2 and #3), you can change where the movement function calls are in the switch..case
statement (there are comments where to put the calls in).
The left and right pushbuttons perform the same two functions (start & stop) if you would like to manually change the mode. If you change the switch..case
statement, put the case
numbers in the if
statements that check the left and right buttons on Circuit Playground.
Here is a video of the whistle bot in action:
Variations on the Whistle Bot
You could use a first low tone for start and a second low tone for stop. You would test if variable moving
is 1 then you's call stopRobot
instead of forward
. Then you have the higher tone to do something like calling reverse
.
If you can whistle in three tones, you can probably call all of the control functions with some variables which record what state you are in and what state you want the robot to change to when a whistle is received.
On the next page you can build an inexpensive tone generator to save you from having to whistle so precisely.
Text editor powered by tinymce.