// BUG: demo state should start as mode 0 after intro
// NICE TO HAVE: When alarm is cancelled, the alarm still remains set for next day.
//Add the following libraries to the respective folder for you operating system. See http://arduino.cc/en/Guide/Environment
#include <FastLED.h> // FastSPI Library from http://code.google.com/p/fastspi/
#include <Wire.h> //This is to communicate via I2C. On arduino Uno & Nano use pins A4 for SDA (yellow/orange) and A5 for SCL (green). For other boards ee http://arduino.cc/en/Reference/Wire
#include <RTClib.h> // Include the RTClib library to enable communication with the real time clock.
#include <EEPROM.h> // Include the EEPROM library to enable the storing and retrevel of settings.
#include <Bounce.h> // Include the Bounce library for de-bouncing issues with push buttons.
#include <Encoder.h> // Include the Encoder library to read the out puts of the rotary encoders
RTC_DS1307 RTC; // Establishes the chipset of the Real Time Clock
#define LEDStripPin A0 // Pin used for the data to the LED strip
#define menuPin A3 // Pin used for the menu button (green stripe)
#define numLEDs 60 // Number of LEDs in strip
// Setting up the LED strip
struct CRGB leds[numLEDs];
Encoder rotary1(2, 3); // Setting up the Rotary Encoder
DateTime old; // Variable to compare new and old time, to see if it has moved on.
int rotary1Pos = 0;
int subSeconds; // 60th's of a second
int secondBrightness;
int secondBrightness2;
int breathBrightness;
long newSecTime; // Variable to record when a new second starts, allowing to create milli seconds
long oldSecTime;
long flashTime; //
long breathCycleTime;
#define holdTime 1500
int cyclesPerSec;
float cyclesPerSecFloat; // So can be used as a float in calcs
float fracOfSec;
float breathFracOfSec;
boolean demo;
#define demoTime 12 // seconds
long previousDemoTime;
long currentDemoTime;
boolean swingBack = false;
int timeHour;
int timeMin;
int timeSec;
int alarmMin; // The minute of the alarm
int alarmHour; // The hour of the alarm 0-23
int alarmDay = 0; // The day of the alarm
boolean alarmSet; // Whether the alarm is set or not
int modeAddress = 0; // Address of where mode is stored in the EEPROM
int alarmMinAddress = 1; // Address of where alarm minute is stored in the EEPROM
int alarmHourAddress = 2; // Address of where alarm hour is stored in the EEPROM
int alarmSetAddress = 3; // Address of where alarm state is stored in the EEPROM
int alarmModeAddress = 4; // Address of where the alarm mode is stored in the EEPROM
boolean alarmTrig = false; // Whether the alarm has been triggered or not
long alarmTrigTime; // Milli seconds since the alarm was triggered
boolean countDown = false;
long countDownTime = 0;
long currentCountDown = 0;
long startCountDown;
int countDownMin;
int countDownSec;
int countDownFlash;
int demoIntro = 0;
int j = 0;
long timeInterval = 5;
long currentMillis;
long previousMillis = 0;
float LEDBrightness = 0;
float fadeTime;
float brightFadeRad;
int state = 0; // Variable of the state of the clock, with the following defined states
#define clockState 0
#define alarmState 1
#define setAlarmHourState 2
#define setAlarmMinState 3
#define setClockHourState 4
#define setClockMinState 5
#define setClockSecState 6
#define countDownState 7
#define demoState 8
int mode; // Variable of the display mode of the clock
int modeMax = 6; // Change this when new modes are added. This is so selecting modes can go back beyond.
int alarmMode; // Variable of the alarm display mode
int alarmModeMax = 3;
Bounce menuBouncer = Bounce(menuPin,20); // Instantiate a Bounce object with a 50 millisecond debounce time for the menu button
boolean menuButton = false;
boolean menuPressed = false;
boolean menuReleased = false;
int advanceMove = 0;
boolean countTime = false;
long menuTimePressed;
long lastRotary;
int rotaryTime = 1000;
int LEDPosition;
int reverseLEDPosition;
int pendulumPos;
int fiveMins;
int odd;
int LEDOffset = 30;
void setup()
{
// Set up all pins
pinMode(menuPin, INPUT_PULLUP); // Uses the internal 20k pull up resistor. Pre Arduino_v.1.0.1 need to be "digitalWrite(menuPin,HIGH);pinMode(menuPin,INPUT);"
// Start LEDs
LEDS.addLeds<WS2811, LEDStripPin, GRB>(leds, numLEDs); // Structure of the LED data. I have changed to from rgb to grb, as using an alternative LED strip. Test & change these if you're getting different colours.
// Start RTC
Wire.begin(); // Starts the Wire library allows I2C communication to the Real Time Clock
RTC.begin(); // Starts communications to the RTC
Serial.begin(9600); // Starts the serial communications
// Uncomment to reset all the EEPROM addresses. You will have to comment again and reload, otherwise it will not save anything each time power is cycled
// write a 0 to all 512 bytes of the EEPROM
// for (int i = 0; i < 512; i++)
// {EEPROM.write(i, 0);}
// Load any saved setting since power off, such as mode & alarm time
mode = EEPROM.read(modeAddress); // The mode will be stored in the address "0" of the EEPROM
alarmMin = EEPROM.read(alarmMinAddress); // The mode will be stored in the address "1" of the EEPROM
alarmHour = EEPROM.read(alarmHourAddress); // The mode will be stored in the address "2" of the EEPROM
alarmSet = EEPROM.read(alarmSetAddress); // The mode will be stored in the address "2" of the EEPROM
alarmMode = EEPROM.read(alarmModeAddress);
// Prints all the saved EEPROM data to Serial
Serial.print("Mode is ");Serial.println(mode);
Serial.print("Alarm Hour is ");Serial.println(alarmHour);
Serial.print("Alarm Min is ");Serial.println(alarmMin);
Serial.print("Alarm is set ");Serial.println(alarmSet);
Serial.print("Alarm Mode is ");Serial.println(alarmMode);
// create a loop that calcuated the number of counted milliseconds between each second.
if (alarmSet == true && alarmDay != now.day()) // The alarmDay statement ensures it is a newly set alarm or repeat from previous day, not within the minute of an alarm cancel.
if (menuBouncer.fallingEdge()) // Checks if a button is pressed, if so sets countTime to true
{
countTime = true;
Serial.println("rising edge");
}
if (menuBouncer.risingEdge()) // Checks if a button is released,
{
countTime = false;
Serial.println("rising edge");
} // if so sets countTime to false. Now the ...TimePressed will not be updated when enters the buttonCheck,
if (countTime) // otherwise will menuBouncer.duration will
{
menuTimePressed = menuBouncer.duration();
if (menuTimePressed >= (holdTime - 100) && menuTimePressed <= holdTime)
{
clearLEDs();
LEDS.show();
delay(100);
}
}
menuReleased = menuBouncer.risingEdge();
if (menuPressed == true) {Serial.println("Menu Button Pressed");}
if (menuReleased == true) {Serial.println("Menu Button Released");}
Serial.print("Menu Bounce Duration ");
Serial.println(menuTimePressed);
if (alarmTrig == true)
{
alarmTrig = false;
alarmDay = now.day(); // When the alarm is cancelled it will not display until next day. As without it, it would start again if within a minute, or completely turn off the alarm.
delay(300); // I added this 300ms delay, so there is time for the button to be released
return; // This return exits the buttonCheck function, so no actions are performs
}
switch (state)
{
case clockState: // State 0
if (advanceMove == -1 && mode == 0)
{
mode = modeMax;
advanceMove = 0;
}
else if(advanceMove != 0) //if displaying the clock, advance button is pressed & released, then mode will change
{
mode = mode + advanceMove;
EEPROM.write(modeAddress,mode);
advanceMove = 0;
}
else if(menuReleased == true)
{
if (menuTimePressed <= holdTime) {state = alarmState; newSecTime = millis();}// if displaying the clock, menu button is pressed & released, then Alarm is displayed
else {state = setClockHourState;} // if displaying the clock, menu button is held & released, then clock hour can be set
}
break;
case alarmState: // State 1
if (advanceMove == -1 && alarmMode <= 0)
{
alarmMode = alarmModeMax;
alarmSet = 1;
}
else if (advanceMove == 1 && alarmMode >= alarmModeMax)
{
alarmMode = 0;
alarmSet = 0;
}
else if (advanceMove != 0)
{
alarmMode = alarmMode + advanceMove;
if (alarmMode == 0) {alarmSet = 0;}
else {alarmSet = 1;}
}
Serial.print("alarmState is ");
Serial.println(alarmState);
Serial.print("alarmMode is ");
Serial.println(alarmMode);
EEPROM.write(alarmSetAddress,alarmSet);
EEPROM.write(alarmModeAddress,alarmMode);
advanceMove = 0;
alarmTrig = false;
if (menuReleased == true)
{
if (menuTimePressed <= holdTime) {state = countDownState; j = 0;}// if displaying the alarm time, menu button is pressed & released, then clock is displayed
else {state = setAlarmHourState;} // if displaying the alarm time, menu button is held & released, then alarm hour can be set
}
break;
case setAlarmHourState: // State 2
if (menuReleased == true) {state = setAlarmMinState;}
else {state = demoState; demoIntro = 1; j = 0;}// if displaying the count down, menu button is pressed & released, then demo State is displayed
}
else {countDown = false; countDownTime = 0; currentCountDown = 0; j = 0;} // if displaying the clock, menu button is held & released, then the count down is reset
}
else if (advanceMove == -1 && currentCountDown <= 0)
{
countDown = false;
countDownTime = 0;
currentCountDown = 0;
demoIntro = 0;
}
else if (advanceMove == 1 && currentCountDown >= 3600)
{
countDown = false;
countDownTime = 3600;
}
else if (advanceMove != 0) //if displaying the count down, rotary encoder is turned then will change accordingley
{
countDown = false;
countDownTime = currentCountDown - currentCountDown%60 + advanceMove*60; // This rounds the count down minute up to the next minute
}
advanceMove = 0;
break;
case demoState: // State 8
if(menuReleased == true) {state = clockState; mode = EEPROM.read(modeAddress);} // if displaying the demo, menu button pressed then the clock will display and restore to the mode before demo started
break;
}
if (menuReleased || advanceMove !=0) {countTime = false;}
Serial.print("Mode is ");
Serial.println(mode);
Serial.print("State is ");
Serial.println(state);
}
void setAlarmDisplay()
{
for (int i = 0; i < numLEDs; i++)
{
fiveMins = i%5;
if (fiveMins == 0)
{
leds[i].r = 100;
leds[i].g = 100;
leds[i].b = 100;
}
}
if (alarmSet == 0)
{
for (int i = 0; i < numLEDs; i++) // Sets background to red, to state that alarm IS NOT set
{
fiveMins = i%5;
if (fiveMins == 0)
{
leds[i].r = 20;
leds[i].g = 0;
leds[i].b = 0;
}
}
}
else
{
for (int i = 0; i < numLEDs; i++) // Sets background to green, to state that alarm IS set