Skip to content

Instantly share code, notes, and snippets.

@theterg
Last active December 15, 2015 19:49
Show Gist options
  • Save theterg/5314097 to your computer and use it in GitHub Desktop.
Save theterg/5314097 to your computer and use it in GitHub Desktop.
SleepPulseFirmware.ino
/*
* Sleep Pulse Firmware - firmware code for the Sleep Pulse Device.
* This runs on an Atmel 328P microprocessor, at 5 volts, with a 16 MHz crystal
* Within the arduino IDE, select "Arduino Pro or Pro Mini (5V, 16MHz) w/ATmega328" as the Board
*/
#include <math.h>
#include <EEPROM.h>
// Prototype pin definitions - don't change these!
#define LED1 3
#define LED2 5
#define LED3 6
#define BUTPWR 8
#define POWER 9
#define MOTOR 2
// Change-able settings:
// How long do we need to hold down the button to turn off the device? (in milliseconds)
#define LONG_PRESS 2000
// When the button is pressed, how long do the LEDs stay turned on?
#define LED_WAKE_INTERVAL 5000
// Lists of possible device settings
// You can change these to adjust the different options available to the user.
// intensities: different solenoid "strength" settings
// each value corresponds to the number of microseconds to turn on the solenoid
// really just need to experiment with different values. Anything beyond 5000 is pretty much the maximum.
#define NUM_INTENSITY_SETTINGS 3
unsigned long intensities[NUM_INTENSITY_SETTINGS] = {1000, 2000, 5000};
// intensities: different solenoid frequency settings
// each value corresponds to the number of milliseconds to wait before generating another heart beat.
// 1000 = 1Hz, 2000 = 0.5Hz, 500 = 2Hz
#define NUM_FREQUENCY_SETTINGS 3
unsigned long frequencies[NUM_FREQUENCY_SETTINGS] = {2000, 1000, 500};
// durations: how long to wait until the device turns itself off, in minutes
// currently only the middle value in this list is used as the user has no way to pick between them.
#define NUM_DURATION_SETTINGS 3
unsigned long durations[NUM_DURATION_SETTINGS] = {10, 15, 20};
// on start up, which item in the lists above to we use?
// These start at 0, so 1 is the middle item in the list
// any code in readSettingsFromEEPROM() below will override these default values.
char intensity_pos = 1;
char frequency_pos = 1;
char duration_pos = 1;
// These are other variables used in the code, nothing to change here.
char buff[200];
unsigned long lastloop;
boolean last_butpwr;
boolean last_butsel;
unsigned long butpwr_time;
unsigned long butsel_time;
unsigned char selection = 0;
void setup()
{
// Initialze the motor (solenoid), but make sure it's off
digitalWrite(MOTOR, LOW);
pinMode(MOTOR, OUTPUT);
// Flash all of the LEDs for 500 ms
analogWrite(LED1, 255);
analogWrite(LED2, 255);
analogWrite(LED3, 255);
delay(500);
analogWrite(LED1, 0);
analogWrite(LED2, 0);
analogWrite(LED3, 0);
// Turn on the POWER pin.
// If the user releases the power button before this point, the device will turn back off.
pinMode(POWER, OUTPUT);
digitalWrite(POWER, HIGH);
// There, now the SPD will remain powered on until POWER is set LOW again.
pinMode(BUTPWR, INPUT);
digitalWrite(BUTPWR, LOW);
// Recall any settings that we saved when the SPD was last turned off:
readSettingsFromEEPROM();
// Set up the serial terminal, and send a welcome message.
Serial.begin(9600);
Serial.println("Instructions:");
Serial.println("Enter I<value> to set Intensity. Value: 1-100 percent");
Serial.println("Enter F<value> to set Frequency. Value: 10-5000 milliseconds");
Serial.println("Enter D<value> to set Duration. Value: 1-120 minutes");
Serial.println("Enter R<Parameter><Value> to recall a setting.");
Serial.println(" Where Parameter = 'I','F' or 'D' and Value = '0', '1' or '2'");
// Set up the current state
lastloop = millis();
last_butpwr = false;
}
// Loop() is run once for every time that the solenoid fires
void loop()
{
// Stay in this loop while it's too early for the solenoid to fire
// lastloop is the last time loop() was run
// frequencies is a list of the different motor frequencies
// frequency_pos the current frequency setting
while (millis() < lastloop+frequencies[frequency_pos]){
// Check if the user has pressed any buttons
processButtons();
// Serial.available() will be > 0 if the user has typed something into the serial terminal
if (Serial.available() > 0){
// wait until the user as entered an entire line (ending with <ENTER>)
readLine();
processLine();
}
}
// make a heart beat! The heart beat will feel stronger if the solenoid is turned on for longer
// intensities is a list containing the different motor strengths
// intensity_pos is the current intensity setting
// So turn on the motor, wait for a certian amount of time dictated by the intensity, and turn it back off
digitalWrite(MOTOR, HIGH);
delayMicroseconds(intensities[intensity_pos]);
digitalWrite(MOTOR, LOW);
// Print the current intensity and frequency to the serial terminal, helpful to see whats going on
Serial.print("Current intensity ");
Serial.print(intensities[intensity_pos]);
Serial.print(" frequency ");
Serial.println(frequencies[frequency_pos]);
// we must update lastloop otherwise we'll toggle the motor too quickly
lastloop = millis();
}
// The EEPROM is the only *persistent* storage we have
// Anything that we want to be user-adjustable, but saved when the SPD is turned off must be stored in it.
void readSettingsFromEEPROM() {
// Currently only intensity is stored in the EEPROM
// TODO: store a larger 'STRUCT' in the EEPROM to store multiple settings.
intensity_pos = EEPROM.read(0);
}
void saveSettingsToEEPROM() {
// Only write to the EEPROM if the value has changed!
// It's important to do this - the EEPROM has a limited number of times it can be written to.
if (intensity_pos != EEPROM.read(0)){
EEPROM.write(0, intensity_pos);
}
}
void allLEDsOff() {
analogWrite(LED1, 0);
analogWrite(LED2, 0);
analogWrite(LED3, 0);
}
// This must be called repeatedly to blink the LEDs in time with the heart beat.
void displayLevel() {
// brightness varies with the heart beat cycle:
// when the motor has just recently fired, millis() will be very close to lastloop
// this causes brightness to be roughly equal to 1000/4, or 250 (close to the maximum)
// as we get closer to when the motor is supposed to fire, millis()-lastloop will increase
unsigned char brightness = (frequencies[frequency_pos] - (millis()-lastloop))/4 ;
// Depending on what intensity level is selected, blink that LED.
if (intensity_pos == 0) {
analogWrite(LED1, brightness);
analogWrite(LED2, 0);
analogWrite(LED3, 0);
} else if (intensity_pos == 1) {
analogWrite(LED2, brightness);
analogWrite(LED1, 0);
analogWrite(LED3, 0);
} else if (intensity_pos == 2) {
analogWrite(LED3, brightness);
analogWrite(LED1, 0);
analogWrite(LED2, 0);
}
}
void processButtons() {
// Read the current value of the button (either 0 for not pressed, or 1 for pressed)
boolean butpwr = digitalRead(BUTPWR);
// Look for when the button value has CHANGED. This only runs once for each time the button is pressed OR released.
if (butpwr != last_butpwr) {
// If the button has changed, and it's now HIGH, button must have been just pressed.
if (butpwr) {
Serial.println("PWR PRESSED");
// If the button had been pressed within the last LED_WAKE_INTERVAL, change the intensity
// This makes it so that you press the button once to turn on the LEDs, and as second time to change the setting
if (millis() < butpwr_time+LED_WAKE_INTERVAL) {
// '(intensity_pos+1)%NUM_INTENSITY_SETTINGS' increases the intensity_pos value, making sure it's in range
intensity_pos = (intensity_pos+1)%NUM_INTENSITY_SETTINGS;
}
butpwr_time = millis();
} else {
// We don't do anything special when the button is released.
Serial.println("PWR released");
}
last_butpwr = butpwr;
}
// If the button has been pressed within the last LED_WAKE_INTERVAL, flash the LEDs
if (millis() < butpwr_time+LED_WAKE_INTERVAL) {
displayLevel();
} else {
allLEDsOff();
}
// Check to see if it's time to turn the SPD off. There are two conditions here:
// 1) durations[duration_pos] is the turn off time, in minutes. Check if we have been turned on longer than that
// 2) check to see if the button has been pressed for more than LONG_PRESS seconds. If so, turn off
if ((millis() > durations[duration_pos]*60000)||(butpwr && (millis()-butpwr_time > LONG_PRESS))) {
TurnMyselfOff();
}
delay(10);
}
// This will turn off the power switch. Nothing will run after this function is called.
void TurnMyselfOff() {
saveSettingsToEEPROM();
// Flash the LEDs for 500 ms
analogWrite(LED1, 255);
analogWrite(LED2, 255);
analogWrite(LED3, 255);
delay(500);
allLEDsOff();
Serial.println("Goodbye!");
// Turn the power switch off.
// SPD will remain powered until the user releases the button
cli();
digitalWrite(POWER, LOW);
sei();
// Do absolutely nothing until the user releases the button
// Otherwise we will accidentally turn back on again.
// asm("nop"); makes sure that this loop will not be removed by the compiler.
while(1) { asm("nop"); }
}
// analyze a command that the user entered into the serial terminal.
// this is still a work in progress.
void processLine() {
Serial.print("Got: ");
Serial.println(buff);
long value = atol(buff+1);
if ((buff[0] == 'I')||(buff[0] == 'i')) {
//Intensity
intensities[intensity_pos] = value*100;
} else if ((buff[0] == 'F')||(buff[0] == 'f')) {
//Frequency
frequencies[frequency_pos] = value;
} else if ((buff[0] == 'T')||(buff[0] == 't')) {
//Time
duration = value;
} else if ((buff[0] == 'S')||(buff[0] == 's')) {
//Store
char setting = buff[1];
char value = atoi(buff+2);
if ((setting == 'I')||(setting == 'i')) {
//intensities[value] = intensity;
} else if ((setting == 'F')||(setting == 'f')) {
//frequencies[value] = frequency;
} else if ((setting == 'T')||(setting == 't')) {
//durations[value] = duration;
}
} else if ((buff[0] == 'R')||(buff[0] == 'r')) {
//Recall
char setting = buff[1];
char value = atoi(buff+2);
if ((setting == 'I')||(setting == 'i')) {
//intensity = intensities[value];
} else if ((setting == 'F')||(setting == 'f')) {
//frequency = frequencies[value];
} else if ((setting == 'T')||(setting == 't')) {
//duration = durations[value];
}
}
else if ((buff[0] == 'L')||(buff[0] == 'l')) {
//LEDs
char led = atoi(buff+1);
char color = atoi(buff+2);
int value = atoi(buff+3);
char channel = 2+led+color;
if ((led == 2) && (color > 0)) {
channel += 3;
}
}
}
//Read a single line, terminated with a '\n' character
void readLine(){
memset(buff, 0x0, 200); //Clear the previous contents of the buffer first
char c = ' ';
int idx = 0;
while (c != '\n'){
c = Serial.read();
if ((c > 31) && (c != '\r') && (c != '\n')) { //Filter out unwanted non-text characters.
buff[idx++] = c; //Add the character to the next position in the 'line' array
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment