Full sketch
Some mods to come:
- stop alarm while ringing
- snoze
//#define MY_DEBUG // Enable debug prints
#define MY_RADIO_NRF24 // Enable and select radio type attached
#define MY_PARENT_ID 0
#define MY_NODE_ID 50
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <MySensors.h>
#include <Time.h>
#include <Wtv020sd16p.h>
#define ALARM_ID 0 // Id of TimeOfAlarm value
#define ACTIV_ID 1 // Id of ActivateAlarm switch
int resetPin = 5; // The pin number of the reset pin.
int clockPin = 6; // The pin number of the clock pin.
int dataPin = 7; // The pin number of the data pin.
int busyPin = 8; // The pin number of the busy pin.
static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
static int enSW = 4; // Rotary switch push-button
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile uint16_t encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile uint16_t oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
// Software SPI (slower updates, more flexible pin options):
// pin 7 - Serial clock out (SCLK)
// pin 6 - Serial data out (DIN)
// pin 5 - Data/Command select (D/C)
// pin 4 - LCD chip select (CS)
// pin 8 - LCD reset (RST)
Adafruit_PCD8544 display = Adafruit_PCD8544(A4, A3, A2, A1, A0);
// Create an instance of the Wtv020sd16p class.
// 1st parameter: Reset pin number.
// 2nd parameter: Clock pin number.
// 3rd parameter: Data pin number.
// 4th parameter: Busy pin number.
Wtv020sd16p wtv020sd16p(resetPin, clockPin, dataPin, busyPin);
//Declare the Menus you need.
char menu[][20] = {"ActiverAlarme", "Regler Alarme" , "Contra-ste", "Volume",
"MusiqueAlarme", "EcouterMusique", "Retour"
};
byte itemCount = 6;
int itemSelected;
byte runOnceAWeek = 1; //allow for time update once a week
char dataBuffer[20]; // for sprintf function
bool alarmStatus; // Alarm set/unset status
int alarmHour = 0;
int alarmMin = 0;
int alarmTune = 1;
unsigned int alarmVolume = 5;
int contrast = 50;
int backlightFlag = 0;
int backlightCountdown = 10000; // switch off backlight after XX millisec
int loopTime = 0;
int prevMillis = 0;
byte smallFont = 1;
byte bigFont = 2;
MyMessage textMsg(ALARM_ID, V_TEXT); // Initialize clock messages
MyMessage alarmMsg(ACTIV_ID, V_LOCK_STATUS); // Initialize switch messages
void presentation() {
sendSketchInfo("Loulou's Clock", "25.07.2017");
present(ALARM_ID, S_INFO);
present(ACTIV_ID, S_LOCK);
}
void setup() {
wtv020sd16p.reset(); //MP3 player
delay(1000);
wtv020sd16p.setVolume(alarmVolume);
pinMode(A5, OUTPUT); //5110 screen backlight
digitalWrite(A5, LOW);
display.begin();
pinMode(enSW, INPUT_PULLUP);
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
attachInterrupt(0, PinA, RISING);
attachInterrupt(1, PinB, RISING);
Serial.begin(115200);
display.setContrast(contrast);
display.display();
display.clearDisplay();
display.setTextColor(BLACK);
int clockCounter = 0;
while (timeStatus() == timeNotSet && clockCounter < 60) {
requestTime();
clockCounter++;
wait(1000);
/*
Serial.print("รฉtat reception:");
Serial.println(timeStatus());
*/
if (clockCounter > 16) {
/*
Serial.print(F("**Failed Clock**"));
Serial.print(F("*Syncronization*"));
*/
break;
}
}
alarmStatus = loadState(0); // Read last lock status from eeprom
setAlarmState(alarmStatus, true); // Now set the last known state and send it to controller
sendTime();
}
void loop() {
/*** ALARM !!! ***/
while (hour() + 2 == alarmHour && minute() == alarmMin) {
digitalWrite(A5, LOW); // switch backlight ON when entering menu
display.clearDisplay();
display.setTextSize(1);
display.setCursor(2, 2);
display.print("Debout Axou");
display.display();
display.setCursor(2, 20);
display.print("Faut y aller");
display.display();
display.setCursor(2, 40);
sprintf(dataBuffer, "Il est %02u:%02u ", hour() + 2, minute());
display.print(dataBuffer);
display.display();
wtv020sd16p.playVoice(alarmTune - 1);
}
/*** synchronise clock node with controller from time to time... ***/
if (weekday() == 5 && runOnceAWeek == 1) {
requestTime();
runOnceAWeek = 0;
}
if (weekday() != 5)
runOnceAWeek = 1;
/*** switch off backlight function ***/
loopTime = millis() - prevMillis;
prevMillis = millis();
backlightFlag = backlightFlag + loopTime;
if (backlightFlag > backlightCountdown) {
digitalWrite(A5, HIGH);
backlightFlag = backlightCountdown + 1; // to avoid overflow
}
else
digitalWrite(A5, LOW);
/*** Show time on the default Screen ***/
display.clearDisplay();
display.setTextSize(2);
display.setCursor(14, 2);
sprintf(dataBuffer, "%02u:%02u ", hour() + 2, minute());
display.print(dataBuffer);
//display.display();
display.setTextSize(1);
display.setCursor(14, 22);
sprintf(dataBuffer, "%02u-%02u-%04u ", day(), month(), year());
display.print(dataBuffer);
display.display();
if (alarmStatus == 0) { //Show whether alarm is on or off
display.setCursor(14, 35);
sprintf(dataBuffer, "Alarme OFF");
}
else {
display.setCursor(7, 35);
char alarmText[7] = "Alarme";
sprintf(dataBuffer, "%s %02u:%02u", alarmText, alarmHour, alarmMin);
}
display.print(dataBuffer);
display.display();
/*** Enter the settings menu if select Switch is pressed ***/
if (digitalRead(enSW) == 0) {
while (digitalRead(enSW) == 0); //wait till switch is released.
encoderPos = 0;
digitalWrite(A5, LOW); // switch backlight ON when entering menu
switch (encoderPos) { //Enter main program
case 0: display.clearDisplay();
display.setTextSize(2);
display.setCursor(0, 10);
display.println(menu[encoderPos]);
display.display();
while (digitalRead(enSW)) {
encoderPos = constrain(encoderPos, 0, itemCount);
display.clearDisplay();
display.setCursor(0, 0);
display.println(menu[encoderPos]);
if (encoderPos < 0)
encoderPos = itemCount;
if (encoderPos > itemCount)
encoderPos = 0;
display.display();
}
while (digitalRead(enSW) == 0);
itemSelected = encoderPos;
default: break;
}
switch (itemSelected) {
case 0: display.clearDisplay(); //ACTIVATE ALARME
display.setTextSize(bigFont);
display.setCursor(0, 10);
display.println(menu[itemSelected]);
display.display();
encoderPos = alarmStatus;
while (digitalRead(enSW)) {
encoderPos = constrain(encoderPos, 0, 1);
display.clearDisplay();
display.setCursor(0, 0);
display.print(menu[itemSelected]);
display.setCursor(16, 34);
display.print(encoderPos);
if (encoderPos == 0) {
sprintf(dataBuffer, "OFF");
}
else {
sprintf(dataBuffer, "ON");
}
display.setCursor(45, 34);
display.print(dataBuffer);
display.display();
}
if (encoderPos != alarmStatus) {
alarmStatus = encoderPos;
send(alarmMsg.set(alarmStatus));
}
while (digitalRead(enSW) == 0);
break;
case 1: display.clearDisplay(); //SET ALARME TIME
display.setTextSize(bigFont);
display.setCursor(0, 10);
display.print(menu[itemSelected]);
display.display();
encoderPos = alarmHour;
while (digitalRead(enSW)) { //SET ALARME HOUR
encoderPos = constrain(encoderPos, 00, 23);
display.clearDisplay();
display.setCursor(0, 0);
display.print(menu[itemSelected]);
display.setCursor(10, 34); //HOUR LOCATION
display.print(encoderPos);
if (encoderPos < 0)
encoderPos = 23;
if (encoderPos > 23)
encoderPos = 0;
display.setCursor(35, 34); // ":" LOCATION
display.print(":");
display.setCursor(50, 34); //MINUTE LOCATION
display.print(alarmMin);
display.display();
}
if (encoderPos != alarmHour) {
alarmHour = encoderPos;
sendTime();
}
encoderPos = alarmMin;
while (digitalRead(enSW)) { //SET ALARME MINUTE
encoderPos = constrain(encoderPos, 00, 59);
display.clearDisplay();
display.setCursor(0, 0);
display.print(menu[itemSelected]);
display.setCursor(10, 34); //HOUR LOCATION
display.print(alarmHour);
display.setCursor(35, 34); //":" LOCATION
display.print(":");
display.setCursor(50, 34); //MINUTE LOCATION
display.print(encoderPos);
if (encoderPos < 0)
encoderPos = 59;
if (encoderPos > 59)
encoderPos = 0;
display.display();
}
if (encoderPos != alarmMin) {
alarmMin = encoderPos;
sendTime();
}
while (digitalRead(enSW) == 0);
break;
case 2: display.clearDisplay(); //CONTRAST
display.setTextSize(bigFont);
display.setCursor(0, 10);
display.print(menu[itemSelected]);
display.display();
encoderPos = contrast;
while (digitalRead(enSW)) {
encoderPos = constrain(encoderPos, 0, 100);
display.clearDisplay();
display.setCursor(0, 0);
display.print(menu[itemSelected]);
display.setCursor(16, 16);
display.print(encoderPos);
if (encoderPos < 0)
encoderPos = 100;
if (encoderPos > 100)
encoderPos = 0;
display.setContrast(encoderPos);
display.display();
}
if (encoderPos != contrast)
contrast = encoderPos;
while (digitalRead(enSW) == 0);
break;
case 3: display.clearDisplay(); //VOLUME
display.setTextSize(smallFont);
display.setCursor(0, 10);
display.print(menu[itemSelected]);
display.display();
encoderPos = alarmVolume;
while (digitalRead(enSW)) {
encoderPos = constrain(encoderPos, 0, 7);
display.clearDisplay();
display.setCursor(0, 0);
display.print(menu[itemSelected]);
display.setCursor(16, 34);
display.print(encoderPos);
if (encoderPos < 0)
encoderPos = 7;
if (encoderPos > 7)
encoderPos = 0;
display.display();
wtv020sd16p.setVolume(encoderPos);
}
alarmVolume = encoderPos;
while (digitalRead(enSW) == 0);
break;
case 4: display.clearDisplay(); //ALARM TUNE
display.setTextSize(bigFont);
display.setCursor(0, 10);
display.print(menu[itemSelected]);
display.display();
encoderPos = alarmTune;
while (digitalRead(enSW)) {
encoderPos = constrain(encoderPos, 1, 10);
display.clearDisplay();
display.setCursor(0, 0);
display.print(menu[itemSelected]);
display.setCursor(16, 34);
display.print(encoderPos);
if (encoderPos < 1)
encoderPos = 10;
if (encoderPos > 10)
encoderPos = 0;
display.display();
}
alarmTune = encoderPos;
while (digitalRead(enSW) == 0);
break;
case 5: display.clearDisplay(); //PLAYER
display.setTextSize(bigFont);
display.setCursor(0, 10);
display.print(menu[itemSelected]);
display.display();
encoderPos = 0;
while (digitalRead(enSW)) {
encoderPos = constrain(encoderPos, 0, 10);
display.clearDisplay();
display.setCursor(0, 0);
display.print(menu[itemSelected]);
display.setCursor(16, 34);
display.print(encoderPos);
if (encoderPos < 0)
encoderPos = 10;
if (encoderPos > 10)
encoderPos = 0;
display.display();
}
if (encoderPos == 0)
wtv020sd16p.stopVoice();
else
wtv020sd16p.playVoice(encoderPos - 1);
while (digitalRead(enSW) == 0);
break;
default: break;
}
}
}
/*** receive a new time value ***/
void receiveTime(unsigned long controllerTime) {
setTime(controllerTime);
}
/*** send alarm time to controller ****/
void sendTime() {
sprintf(dataBuffer, "%02u:%02u ", alarmHour, alarmMin);
send(textMsg.set(dataBuffer));
wait(1000);
}
/*** Activate alarm ***/
void setAlarmState(bool state, bool doSend) {
if (doSend)
send(alarmMsg.set(state));
saveState(0, state);
alarmStatus = state;
}
/*** receive alarme state from controller ***/
void receive(const MyMessage & message) {
if (message.type == V_LOCK_STATUS)
setAlarmState(message.getBool(), false);
if (message.type == V_TEXT) {
char alarmTime[MAX_MESSAGE_LENGTH];
strncpy(alarmTime, message.data, MAX_MESSAGE_LENGTH);
char* token = strtok(alarmTime, ":");
alarmHour = atoi(token);
token = strtok(NULL, "");
alarmMin = atoi(token);
}
}
/*** rotary encoder interrupt function ***/
void PinA() {
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
if (reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos --; //decrement the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
backlightFlag = 0;
}
else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinB() {
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos ++; //increment the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
backlightFlag = 0;
}
else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}```