Destresser
Made by Robert Zacharias
Made by Robert Zacharias
When you’re stressed out, the Destresser is there to hear your tapped pleas and wipe your brow.
Created: September 15th, 2015
The Destresser is a simple device, consisting of a mic/control unit and a specially adapted hat. It accompanies you while you work on homework, taxes, or whatever stressful things you might be doing at your desk. The Destresser listens for your stress by listening to how fast you tap on the desk. The Destresser doesn't judge. It's here to help.
If you’re tapping slowly, the Destresser shows a green light. You’re ok. A little tap here or there is natural. Don’t sweat it.
If you start tapping faster—at around what a musician would call an andante (“walking”) pace, the Destresser will notice that you’re sounding a little worried, and indicate with a yellow light that it’s starting to get concerned. Take a chill pill, friend. Maybe it's a good moment to rest your eyes. You can get through this.
But if you persist and start tapping very fast, the Destresser knows you’re really struggling and it wants to help. It does this, of course, by giving you a calm, reassuring touch in the form of a friendly dabbing of the forehead with a moist cloth. There's not much in the world that feels better than a well-timed forehead dabbing.
There, there. All is well. The Destresser is here to help.
Four wires run into the control/sensor box:
White: 5V for the hobby servo
Red: 9V for the Arduino's barrel plug
Black and Green: grounds (tied together at the supply)
Three wires run out: the motor's power, and ground lines (which simply pass through the box), and the motor logic control line.
I wrapped these wires into the configuration I wanted by clamping the far side in a vise and using a cordless drill to twist the bundle together.
/*
* 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
The code essentially listens for taps and keeps a running average of the millisecond gap between successive taps. One issue I encountered early on was that during certain procedures the Arduino's single thread was tied up inside of if statements and unable to hear new taps come in; I needed to make sure it was available for listening more often.
I had a sleep procedure that was running every cycle, checking that if the Destresser hadn't heard a tap in a while that it wouldn't just keep running the clock up, since this would skew the timing average very high when tapping resumed. This procedure was important to include, but in order to prevent it from burdening the main loop too much I wrapped it in a two-condition if: 1) counter value is 100, and 2) it's been more than 5 seconds since the last tap was heard.
The counter would increment by one each loop cycle. Using counter == 100 as the first condition of the if, I incorporated a short-circuit evaluation which would only run the rest of the if statement one out of one hundred loops. This freed up the Arduino to listen to the mic and greatly reduced false negatives in detecting taps.
Making Things Interactive (MTI) is a studio course based on physical prototyping and computing. You will develop novel sensing, interaction and display techniques through projects and short assignm...more
When you’re stressed out, the Destresser is there to hear your tapped pleas and wipe your brow.