Skip to content

Instantly share code, notes, and snippets.

@spencersugarman
Created June 17, 2012 18:16
Show Gist options
  • Save spencersugarman/2945266 to your computer and use it in GitHub Desktop.
Save spencersugarman/2945266 to your computer and use it in GitHub Desktop.
Rover, the pet shoe
/*
* -----------------
* 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