Created
June 17, 2012 18:16
-
-
Save spencersugarman/2945266 to your computer and use it in GitHub Desktop.
Rover, the pet shoe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* ----------------- | |
* Rover, the pet shoe! | |
* ----------------- | |
* | |
* Sleep code based on | |
* Sleep Demo Serial from http://www.arduino.cc/playground/Learning/ArduinoSleepCode | |
* Sleeping Arduino http://donalmorrissey.blogspot.com/2010/04/putting-arduino-diecimila-to-sleep.html | |
* | |
* Accelerometer code based on | |
* Sensoring Orientation With The ADXL355 http://bildr.org/2011/04/sensing-orientation-with-the-adxl335-arduino/ | |
* | |
* Timer code based on | |
* Timer1 http://www.arduino.cc/playground/Code/Timer1 | |
* | |
* -------------------------------------------------------------------------------- | |
* THE BEER-WARE LICENSE | |
* (Revision 42.B) | |
* | |
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
* | |
* Spencer Sugarman wrote this file. Everyone is permitted to copy and distribute | |
* verbatim or modified copies of this license document, and changing it is | |
* allowed as long as the name is changed. If we meet some day, and you think | |
* this stuff is worth it, you can buy me a beer in return. | |
* -------------------------------------------------------------------------------- | |
* | |
*/ | |
#include <avr/sleep.h> | |
#include "Wire.h" | |
#include "BlinkM_funcs.h" | |
#define blinkm_addr 0x09 | |
unsigned long count = 0; | |
unsigned long startTime; | |
const unsigned long maxIdleTime = 600000; | |
//Digital wake pin | |
const int button = 2; | |
int buttonState = 0; | |
int lastButtonState = 0; | |
//Vibration motor | |
const int motor = 13; | |
//Number of steps since beginning | |
unsigned long steps = 0; | |
//Control for checking lethargy and happiness | |
unsigned long timeAtLastCheck = 0; | |
const int checkPace = 1000; // run checks every 1000 ms | |
int resting = 0; | |
//Analog read pins | |
const int xPin = A0; | |
const int yPin = A1; | |
const int zPin = A2; | |
//The minimum and maximum values that came from | |
//the accelerometer while standing still | |
//You very well may need to change these | |
int minVal = 265; | |
int maxVal = 402; | |
//to hold the caculated values | |
float averageX = 0; | |
float averageY = 0; | |
float averageZ = 0; | |
void setup() | |
{ | |
Serial.begin(9600); | |
Serial.println("Awake!"); | |
pinMode(button, INPUT); | |
pinMode(motor, OUTPUT); | |
BlinkM_beginWithPower(); | |
BlinkM_stopScript( blinkm_addr ); // turn off startup script | |
BlinkM_fadeToRGB( blinkm_addr, 0,0xFF,0); | |
} | |
void loop() | |
{ | |
trackSteps(); | |
if( millis() - timeAtLastCheck >= checkPace ) { | |
timeAtLastCheck = millis(); | |
checkLethargy(); | |
checkHappiness(); | |
} | |
if( Serial.available() > 0 ) { | |
if( Serial.read() == 'Z' ) { | |
partyTime(); | |
} | |
} | |
} | |
void partyTime() | |
{ | |
BlinkM_stopScript( blinkm_addr ); | |
blinkm_script_line script1_lines[] = { | |
{ 1, {'f', 30,00,00}}, | |
{ 10, {'c', 0x00, 0xFF, 0xFF}}, // light blue | |
{ 10, {'c', 0xFF, 0xFF, 0x00}}, // yellow | |
{ 10, {'c', 0xFF, 0x00, 0xFF}}, // purple | |
{ 10, {'c', 0x00, 0x00, 0xFF}}, // blue | |
{ 10, {'c', 0x00, 0xFF, 0x00}}, // green | |
{ 10, {'c', 0xFF, 0x00, 0x00}}, // red | |
}; | |
int script1_len = 7; // number of script lines above | |
BlinkM_writeScript( blinkm_addr, 0, script1_len, 0, script1_lines); | |
BlinkM_playScript( blinkm_addr, 0,0,0 ); | |
digitalWrite( motor, HIGH ); | |
delay(300); | |
digitalWrite( motor, LOW ); | |
delay(500); | |
digitalWrite( motor, HIGH ); | |
delay(300); | |
digitalWrite( motor, LOW ); | |
delay(500); | |
digitalWrite( motor, HIGH ); | |
delay(300); | |
digitalWrite( motor, LOW ); | |
delay(700); | |
digitalWrite( motor, HIGH ); | |
delay(1500); | |
digitalWrite( motor, LOW ); | |
delay(500); | |
digitalWrite( motor, HIGH ); | |
delay(700); | |
digitalWrite( motor, LOW ); | |
BlinkM_stopScript( blinkm_addr ); | |
BlinkM_fadeToRGB( blinkm_addr, 0,0,0 ); | |
} | |
const int maxTime = 60; // in seconds | |
const int happySteps = 60; | |
unsigned long currentTime = 0; | |
unsigned int numberOfSteps[maxTime]; | |
unsigned long restingTime; | |
unsigned long maxRestingTime = 15000; | |
void checkHappiness() | |
{ | |
if( resting ) { | |
if( millis()-restingTime > maxRestingTime ) { | |
stopRest(); | |
} else { | |
return; | |
} | |
} else if( !resting && steps > happySteps*2 ) { | |
startRest(); | |
} | |
Serial.println(currentTime); | |
if( currentTime < maxTime ) { | |
numberOfSteps[currentTime] = steps; | |
if( currentTime < maxTime/4 ) { // e.g., minute 1 to 15 | |
BlinkM_fadeToRGB( blinkm_addr, 0,0xFF,0); | |
} else if( currentTime < maxTime/2 ) { // e.g., minute 15 to 30 | |
if( steps < happySteps/4 ) { | |
BlinkM_fadeToRGB( blinkm_addr, 0xFF,0xFF,0); | |
} else { | |
BlinkM_fadeToRGB( blinkm_addr, 0,0xFF,0); | |
} | |
} else if( currentTime < (maxTime/4)*3 ) { // e.g., minute 30 to 45 | |
if( steps < happySteps/4 ) { | |
BlinkM_fadeToRGB( blinkm_addr, 0xFF,0x66,0); | |
} else if( steps < happySteps/2 ) { | |
BlinkM_fadeToRGB( blinkm_addr, 0xFF,0xFF,0); | |
} else { | |
BlinkM_fadeToRGB( blinkm_addr, 0,0xFF,0); | |
} | |
} else { // e.g., minute 45 to 60 | |
if( steps < happySteps/4 ) { | |
BlinkM_fadeToRGB( blinkm_addr, 0xFF,0,0); | |
digitalWrite(motor, HIGH); | |
delay(1500); | |
digitalWrite(motor, LOW); | |
delay(500); | |
digitalWrite(motor, HIGH); | |
delay(1500); | |
digitalWrite(motor, LOW); | |
} else if( steps < happySteps/2 ) { | |
BlinkM_fadeToRGB( blinkm_addr, 0xFF,0x66,0); | |
if( currentTime%3 == 0 ) { | |
digitalWrite(motor, HIGH); | |
delay(500); | |
digitalWrite(motor, LOW); | |
delay(500); | |
digitalWrite(motor, HIGH); | |
delay(500); | |
digitalWrite(motor, LOW); | |
delay(500); | |
digitalWrite(motor, HIGH); | |
delay(500); | |
digitalWrite(motor, LOW); | |
} | |
} else if( steps < (happySteps/4)*3 ) { | |
BlinkM_fadeToRGB( blinkm_addr, 0xFF,0xFF,0); | |
} else { | |
BlinkM_fadeToRGB( blinkm_addr, 0,0xFF,0); | |
if( currentTime == maxTime - 1 ) { | |
currentTime = 0; | |
steps = 0; | |
} | |
} | |
} | |
} else { // e.g. minutes 60+ | |
int currentSteps = steps - numberOfSteps[0]; | |
for( int i = 0; i < maxTime; i++ ){ | |
if( i != 0 ) { | |
numberOfSteps[i-1] = numberOfSteps[i]; | |
} | |
} | |
numberOfSteps[maxTime-1] = currentSteps; | |
if( numberOfSteps[maxTime-1] < happySteps/2 ) { | |
BlinkM_fadeToRGB( blinkm_addr, 0xFF,0,0); | |
if( currentTime%5 == 0) { | |
digitalWrite(motor, HIGH); | |
delay(3000); | |
digitalWrite(motor, LOW); | |
} | |
} else if( numberOfSteps[maxTime-1] < (happySteps/4)*3 ) { | |
BlinkM_fadeToRGB( blinkm_addr, 0xFF,0xFF,0); | |
} else { | |
BlinkM_fadeToRGB( blinkm_addr, 0,0xFF,0); | |
currentTime = 0; | |
} | |
} | |
currentTime++; | |
} | |
void startRest() | |
{ | |
blinkm_script_line script1_lines[] = { | |
{ 1, {'f', 10,00,00}}, | |
{ 40, {'c', 0xED,0xED,0xED}}, // bright white | |
{ 80, {'c', 0x05,0x05,0x05}}, // dim white | |
}; | |
int script1_len = 3; // number of script lines above | |
BlinkM_writeScript( blinkm_addr, 0, script1_len, 0, script1_lines); | |
BlinkM_playScript( blinkm_addr, 0,0,0 ); | |
resting = 1; | |
restingTime = millis(); | |
Serial.println("...phew, I am tired."); | |
} | |
void stopRest() | |
{ | |
BlinkM_stopScript( blinkm_addr ); | |
resting = 0; | |
} | |
void wakeNow() | |
{ | |
delay(100); | |
detachInterrupt(0); | |
Serial.println("Waking up!"); | |
steps = 0; | |
injectCaffeine(); | |
} | |
void sleepNow() | |
{ | |
BlinkM_fadeToRGB( blinkm_addr, 0,0,0); | |
attachInterrupt(0, wakeNow, LOW); | |
Serial.println("YAAAAAAAAAAWN! Going to sleep."); | |
delay(100); | |
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here | |
sleep_enable(); // enables the sleep bit in the mcucr register | |
sleep_mode(); // so sleep is possible. just a safety pin | |
// here the device is actually put to sleep!! | |
sleep_disable(); // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP | |
// first thing after waking from sleep: | |
// disable sleep... | |
} | |
void trackSteps() | |
{ | |
buttonState = digitalRead(button); | |
if( buttonState != lastButtonState ) { | |
if( buttonState == LOW) { | |
injectCaffeine(); | |
steps++; | |
Serial.print("Steps: "); | |
Serial.println(steps); | |
} | |
} | |
lastButtonState = buttonState; | |
delay(50); | |
} | |
void injectCaffeine() | |
{ | |
startTime = millis(); | |
count = 0; | |
averageX = 0; | |
averageY = 0; | |
averageZ = 0; | |
} | |
void checkLethargy() | |
{ | |
//read the analog values from the accelerometer | |
int xRead = analogRead(xPin); | |
int yRead = analogRead(yPin); | |
int zRead = analogRead(zPin); | |
//convert read values to degrees -90 to 90 - Needed for atan2 | |
int xAng = map(xRead, minVal, maxVal, -90, 90); | |
int yAng = map(yRead, minVal, maxVal, -90, 90); | |
int zAng = map(zRead, minVal, maxVal, -90, 90); | |
//Caculate 360deg values like so: atan2(-yAng, -zAng) | |
//atan2 outputs the value of -π to π (radians) | |
//We are then converting the radians to degrees | |
int x = RAD_TO_DEG * (atan2(-yAng, -zAng) + PI); | |
int y = RAD_TO_DEG * (atan2(-xAng, -zAng) + PI); | |
int z = RAD_TO_DEG * (atan2(-yAng, -xAng) + PI); | |
// Reading from the acceleromter is inexact at times | |
// Thus, we need to figure out the average value over the time range | |
++count; | |
unsigned long then = count - 1; | |
averageX = (x + ((then) * averageX))/count; | |
averageY = (y + ((then) * averageY))/count; | |
averageZ = (z + ((then) * averageZ))/count; | |
/* | |
Serial.print(x); | |
Serial.print(" | "); | |
Serial.print(y); | |
Serial.print(" | "); | |
Serial.println(z); | |
*/ | |
// Then, at maxIdleTime, we assume the accelerometer has remained | |
// stationary if the current value is within 5 degrees of the average | |
if( (millis() - startTime) > maxIdleTime && isWithinRange(z,averageZ) && isWithinRange(x,averageX) && isWithinRange(y,averageY) ) { | |
sleepNow(); | |
} | |
} | |
int isWithinRange( int value, int average ) | |
{ | |
if( (average-5) <= value && value <= (average+5) ) { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
/* Cumulative Moving Average | |
* See http://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average | |
* for information on the formula | |
*/ | |
int movingAverage( int number, int average, int i ) | |
{ | |
if( i == 0 ) { // if i = 0, there is no average to calculate | |
return 0; | |
} | |
return (number + ((i - 1) * average))/i; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment