Back to Parent

/*
 * Destresser by Robert "Zach"arias (rzachari@andrew.cmu.edu)
 * version 1.1, 9/15/15
 *
 * Summary: Arduino with attached mic, which is positioned on a table surface,
 * reads the tempo of tapping sounds on that table. If the tapping tempo is slow
 * or absent, the indicator shows green. If the tempo is mid-pace, the indicator
 * shows yellow. And if the tapping becomes very fast, the indicator shows
 * red and triggers a servomotor to swab the user's forehead with a moist sponge.
 * (The servo is mounted on the visor of the user's cap.)
 * 
 * Tempo is measured by the gap between taps, which I call opmet (haha), 
 * smoothed by a simple exponential decay algorithm.
 *
 * 9V is run into the Arduino's VIN, and 5V at about 1A is run to the small hobby 
 * servo, with the grounds tied together.
 *
 * Project for 48-739 "Making Things Interactive" at Carnegie Mellon, taught by 
 * Jake Marsico.
 *
 * Released to the public domain by the author.
 */

#include <Servo.h>
Servo myservo;

int micPin = A3; // simple electret with built-in amp, threshold set to trigger when table tapped
int blinker = 13; // for debugging; will light when a tap is detected
int servoPin = 9;

// LEDs
int redLED = 5;
int yellowLED = 6;
int greenLED = 7;

int micThreshold = 800; // cuts out most noise
int minBetweenTaps = 70; // 70 milliseconds between taps, to remove echoes

float weightOfNewData = 0.75; // closer to 1 biases towards old data, closer to 0 biases towards new data
unsigned long tapTime = 0; // will be set to millis() when tap is detected
unsigned long prevTapTime = 0; // stores the prior tap time value
unsigned long diff = 0; // stores the difference between the prior and current tap times
unsigned long runningAvg = 1000; // stores smoothed opmet data in ms, initialized to 1 second
int sleepTimer = 5000; // how long to wait before going into sleep mode, in ms

int longWait = 500; // long gap between taps, in ms
int shortWait = 170; // short gap between taps, in ms

// servo positions
int restPos = 0; // resting position
int middlePos = 70; // in between taps position
int headPos = 95; // tapping forehead position, empirically derived

int counter = 0;

void setup() {

  pinMode(micPin, INPUT);
  pinMode(blinker, OUTPUT);
  pinMode(redLED, OUTPUT);
  pinMode(yellowLED, OUTPUT);
  pinMode(greenLED, OUTPUT);

  myservo.attach(servoPin);
  myservo.write(restPos);

  Serial.begin(9600);

}

void loop() {

  int micRead = analogRead(micPin);

  // if "tap" sound, i.e. mic reads high value, record timer value and do math to recalculate opmet
  if (micRead >= micThreshold) {
    digitalWrite(blinker, HIGH); // board blinks for debugging
    tapTime = millis();  // draw line in sand
    diff = tapTime - prevTapTime; // calculate distance from last line in sand
    prevTapTime = millis(); // make the new line in the sand

    // simple exponential decay to calculate running average
    runningAvg = (diff * (1 - weightOfNewData)) + (runningAvg * weightOfNewData);

    Serial.print("runningAvg = "); Serial.println(runningAvg);

    delay(minBetweenTaps); // cuts out hearing the same tap twice or thrice in a row
    digitalWrite(blinker, LOW);
  }

  if (runningAvg > longWait) {
    green();
  }

  else if (runningAvg <= longWait && runningAvg > shortWait) {
    yellow();
  }

  else {
    red();
    headPad();
  }

  /*if you don't hear anything for a while, simulate one tap per second
    to go back to green and not skew average opmet too far upwards */
  if (counter == 100 && millis() - prevTapTime > sleepTimer) {
    counter = 0; // reset counter
    Serial.println("sleeping");
    runningAvg = 1000; // pretend the average opmet is one second
  }
  counter++; // implemented because sleep procedure was eating up too many cycles

}

void red() {
  digitalWrite(redLED, HIGH);
  digitalWrite(yellowLED, LOW);
  digitalWrite(greenLED, LOW);
}

void yellow() {
  digitalWrite(redLED, LOW);
  digitalWrite(yellowLED, HIGH);
  digitalWrite(greenLED, LOW);
}

void green() {
  digitalWrite(redLED, LOW);
  digitalWrite(yellowLED, LOW);
  digitalWrite(greenLED, HIGH);
}

void headPad() {
  // blocking code that swabs the forehead
  myservo.write(headPos);
  delay(500);
  myservo.write(middlePos);
  delay(500);
  myservo.write(headPos);
  delay(500);
  myservo.write(middlePos);
  delay(500);
  myservo.write(headPos);
  delay(500);
  myservo.write(restPos);

  // restore baseline 1000 opmet value
  runningAvg = 1000;
}
Click to Expand

Content Rating

Is this a good/useful/informative piece of content to include in the project? Have your say!

0