Source Code

Let's control the mouth animation dynamically with a little Arduino and an electret microphone.
I prefer to adjust the gain on the microphone... although you could do it in code.
Here's how I hooked up the mouth LEDs to the Arduino.

  • yellow wire to pin 9: drives the LEDs on the far right and left
  • blue wire to pin 10: drives the LEDs between the center and outside
  • purple wire to pin 11: drives the LED in the center of the mouth
  • green wire to ground

And this is the sketch that drives the LEDs based on the sound levels captured by the microphone.

There are some settings in there like HI_CUT, LO_CUT, and DC_OFFSET that you might have to adjust depending on what kind of audio you intend to drive the microphone with and how you've hooked it up.

As-is, this sketch works pretty well with the mic attached to your shirt just below the mouth.
Download: file
// Connect LED positive to pins 9, 10, 11 with common ground.

#define MIC_PIN   A0      // connected to the out pin on the mic 
#define DC_OFFSET 3.3     // mic is plugged into 3.3v 

const byte HI_CUT = 255;  // upper brightness limit
const byte LO_CUT = 40;   // lower iimit turns LEDs off

// sample window width in mS (50 mS = 20Hz)
const int sampleWindow = 50;
unsigned int sample;

int LED_CENTER = 11;  // LED at center of the mouth
int LED_MIDDLE = 10;  // LEDs between the center and outside
int LED_OUTSDE = 9;   // LEDs on the outside of the mouth

void setup() {                
  // Serial.begin(9600);  // remove comment for debugging

  pinMode( LED_CENTER, OUTPUT );
  pinMode( LED_MIDDLE, OUTPUT );  
  pinMode( LED_OUTSDE, OUTPUT );  
}

void loop() {
  unsigned long startMillis= millis();  // start of sample window
  unsigned int peakToPeak = 0;          // peak-to-peak level
  unsigned int signalMax = 0;
  unsigned int signalMin = 1024;
  double lvl = 0;

  // collect data for 50 mS within a reasonable range
  while (millis() - startMillis < sampleWindow) {
    sample = analogRead(MIC_PIN);

    if (sample < 1024) {
      if (sample > signalMax) {
        signalMax = sample;
      } 
      else if (sample < signalMin) {
        signalMin = sample;
      }
    }
  }

  lvl = signalMax - signalMin;              // max - min = peak-peak amplitude
  lvl = ( lvl * DC_OFFSET );                // reduce operational range
  lvl = ( lvl < LO_CUT ? 0 : lvl );         // low cut filter for noise
  lvl = ( lvl > HI_CUT ? HI_CUT : lvl );    // high cut filter for clipping

  analogWrite( LED_CENTER, lvl );           // 100% brightness
  analogWrite( LED_MIDDLE, lvl * .8 );      // 80% brightness of center
  analogWrite( LED_OUTSDE, lvl * .3 );      // 30% brightness of center

  // Serial.println(lvl);  // remove comment for debugging
}


Have fun with your new robotic friend!
This guide was first published on Nov 11, 2013. It was last updated on Nov 11, 2013. This page (Source Code) was last updated on Nov 08, 2019.