Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. My Project
  3. Alarm Clock

Alarm Clock

Scheduled Pinned Locked Moved My Project
13 Posts 4 Posters 4.0k Views 5 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • ben999B Offline
    ben999B Offline
    ben999
    wrote on last edited by ben999
    #3

    And here is my code up-to now :

    What it does on node's side

    • displays time, date, and alarm if set (if not, displays "alarm Off")
    • updates itself once a week by asking controller
    • list menus to :
      • set alarm on or off
      • set alarm time (hour and minute)
      • change screen contrast

    What is does on openHab

    • turns on and off alarm via a switch (item #1 - lock sensor)

    • displays time of alarm and when it has been updated (item #0 - text)

    I have ordered a WTV020-SD-16P module so that my little guys can wake-up to the sound of "the Final Countdown" !
    http://www.ebay.com/itm/U-disk-audio-player-TF-SD-card-voice-module-MP3-Sound-WTV020-SD-16P-Arduino/310629013078?ssPageName=STRK%3AMEBIDX%3AIT&_trksid=p2057872.m2749.l2649

    And last on the list, being able to change alarm time via paperUI and iPhone app

    Thanks for your comments (be gentle on arduino sketch ;) )

    /*******Interrupt-based Rotary Encoder Sketch*******
    by Simon Merrett, based on insight from Oleg Mazurov, Nick Gammon, rt, Steve Spence */
    
    //#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>
    
    #define ALARM_ID 0                      // Id of TimeOfAlarm value
    #define ACTIV_ID 1                      // Id of ActivateAlarm switch
    
    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);
    
    //Declare the Menus you need.
    char menu[][10] = {"Activer", "Regler", "Ecran", "Volume", "Retour"};
    byte itemCount = 4;
    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 contrast = 50;
    int backlightFlag = 0;
    int backlightCountdown = 10000;   // switch off backlight after XX millisec
    int loopTime = 0;
    int prevMillis = 0;
    
    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", "08.07.2017");
      present(ALARM_ID, S_INFO);
      present(ACTIV_ID, S_LOCK);
    }
    
    void setup() {
      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() {
      /*** 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.setTextSize(1);
      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(2);
            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, 26);
              display.print(encoderPos);
              if (encoderPos == 0)  {
                sprintf(dataBuffer, "OFF");
              }
              else  {
                sprintf(dataBuffer, "ON");
              }
              display.setCursor(45, 26);
              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(2);
            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, 26);       //HOUR LOCATION
              display.print(encoderPos);
              if (encoderPos < 0)
                encoderPos = 23;
              if  (encoderPos > 23)
                encoderPos = 0;
              display.setCursor(35, 26);       // ":" LOCATION
              display.print(":");
              display.setCursor(50, 26);       //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, 26);       //HOUR LOCATION
              display.print(alarmHour);
              display.setCursor(35, 26);       //":" LOCATION
              display.print(":");
              display.setCursor(50, 26);       //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(2);
            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;
          default: break;
        }
      }
    }
    
    
    /*** 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);      // Change relay state
      }
    }
    
    /*** receive a new time value ***/
    void receiveTime(unsigned long controllerTime) {
      setTime(controllerTime);
    }
    
    /*** 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
    }```
    1 Reply Last reply
    2
    • ben999B Offline
      ben999B Offline
      ben999
      wrote on last edited by ben999
      #4

      Guys

      Funny discovery tonight :
      "alarm time" value in PaperUI can be set by clicking on it !! It is not a read-only value, it can also BE SET !!!

      0_1500492214317_Capture du 2017-07-19 21-23-03.png

      Now the big question is: how can my node retreive that new value ?

      21:17:04.816 [DEBUG] [rsAbstractConnection$MySensorsReader] - 50;255;3;0;21;0
      21:17:20.291 [INFO ] [smarthome.event.ItemCommandEvent    ] - Item 'bedAlarm01' received command 21:12
      21:17:20.296 [DEBUG] [rs.internal.gateway.MySensorsGateway] - Node 50 found in gateway
      21:17:20.298 [DEBUG] [rs.internal.gateway.MySensorsGateway] - Child 0 found in node 50
      21:17:20.299 [DEBUG] [rsAbstractConnection$MySensorsWriter] - Sending to MySensors: 50;0;1;0;47;21:12
      

      Looks like gateway sends a text message (47) towards node... so sketch needs to make a difference between "lock" messages and "alarm time" messages, right ?

      /*** receive alarme state and time from controller ***/
      void receive(const MyMessage & message) {
        if (message.type == V_LOCK_STATUS) {
          setAlarmState(message.getBool(), false);
        }
        if (message.type == V_TEXT) {
          do (
               use strtok_r(message.data, ";", &p) to extract payload
               somehow extract hour and minute from that payload
               store them in int alarmHour int and alarmMin
        }
      }
      

      A clue to get me on the way ?

      Thanks a lot for your input :)

      1 Reply Last reply
      0
      • ben999B Offline
        ben999B Offline
        ben999
        wrote on last edited by
        #5
        This post is deleted!
        1 Reply Last reply
        0
        • ben999B Offline
          ben999B Offline
          ben999
          wrote on last edited by
          #6

          Guys,

          I need some help por favor :)

          I am fighting against payload! The picture (a few posts above) shows that i can modify two values (seen under "Heure de réveil")

          As i validate the new value, the gateway sends a message to the node

          Sending to MySensors: 50;0;1;0;47;21:12
          

          Which is great, that's a new feature to me :)

          Now on the node side : I have included

          void receive(const MyMessage & message) {
            if (message.type == V_TEXT)
              Serial.print(message.data);
             // or Serial.print(message.getString());  // works fine too
          }
          

          Serial monitor of node shows

          21:12
          

          Lovely jobly sounds good

          I am now facing two major problems to me as a noob:

          • what is the format of "message.data" ? string? whenever i try to play with it i get some "invalid conversion" from IDE...
          • how to split that "message.data" into two integers (alarmHour and alarmMin) ?

          Thanks for your help ! That should be the final shout for help as this project is nearly achieved...

          1 Reply Last reply
          0
          • ben999B Offline
            ben999B Offline
            ben999
            wrote on last edited by ben999
            #7

            Can someone please comment on this DIY mess (it's a mix of bits and pieces found here and there)

            void receive(const MyMessage & message) {
              if (message.type == V_LOCK_STATUS)
                setAlarmState(message.getBool(), false);
              if (message.type == V_TEXT) {
                char *alarmTime =  message.data;
                char* token = strtok(alarmTime, ":");
                alarmHour = atoi(token);
                token= strtok(NULL, "");
                alarmMin = atoi(token);
              }
            

            It works the way I need it but i guess it's a dirty way of getting there... your eyes might start bleeding :D i get this from compiler :

            warning: invalid conversion from 'const char*' to 'char*' [-fpermissive]
                 char *alarmTime =  message.data;
            

            Any suggestion to make it neater?

            mfalkviddM 1 Reply Last reply
            0
            • ben999B ben999

              Can someone please comment on this DIY mess (it's a mix of bits and pieces found here and there)

              void receive(const MyMessage & message) {
                if (message.type == V_LOCK_STATUS)
                  setAlarmState(message.getBool(), false);
                if (message.type == V_TEXT) {
                  char *alarmTime =  message.data;
                  char* token = strtok(alarmTime, ":");
                  alarmHour = atoi(token);
                  token= strtok(NULL, "");
                  alarmMin = atoi(token);
                }
              

              It works the way I need it but i guess it's a dirty way of getting there... your eyes might start bleeding :D i get this from compiler :

              warning: invalid conversion from 'const char*' to 'char*' [-fpermissive]
                   char *alarmTime =  message.data;
              

              Any suggestion to make it neater?

              mfalkviddM Offline
              mfalkviddM Offline
              mfalkvidd
              Mod
              wrote on last edited by mfalkvidd
              #8

              @ben999 message.data is const char* which means its contents can not (or at least should not) be modified.
              Then you set the alarmTime pointer to point to the same message.data, which means that if you modify the contents of alarmTime you will also modify the contents of message.data (they are not two copies of the same thing, they are the same thing)
              The definition of strtok is

              char *strtok(char *restrict source, const char *restrict delimiters);
              

              which means the first parameter must allow modification (no const), while the second does not need modification.

              The man page of strtok also says this (my emphasis):

              The strtok function returns a pointer to the beginning of each subsequent token in the string, after replacing the separator character itself with a null character. When no more tokens remain, a null pointer is returned.

              which explains why the parameter must allow modification.
              That's why you can't do

              const char *alarmTime =  message.data;
              

              I am not a c expert, but I think you should do this instead:

              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);
                }
              }
              

              MAX_MESSAGE_LENGTH is provided by the MySensors library.

              ben999B 1 Reply Last reply
              2
              • mfalkviddM mfalkvidd

                @ben999 message.data is const char* which means its contents can not (or at least should not) be modified.
                Then you set the alarmTime pointer to point to the same message.data, which means that if you modify the contents of alarmTime you will also modify the contents of message.data (they are not two copies of the same thing, they are the same thing)
                The definition of strtok is

                char *strtok(char *restrict source, const char *restrict delimiters);
                

                which means the first parameter must allow modification (no const), while the second does not need modification.

                The man page of strtok also says this (my emphasis):

                The strtok function returns a pointer to the beginning of each subsequent token in the string, after replacing the separator character itself with a null character. When no more tokens remain, a null pointer is returned.

                which explains why the parameter must allow modification.
                That's why you can't do

                const char *alarmTime =  message.data;
                

                I am not a c expert, but I think you should do this instead:

                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);
                  }
                }
                

                MAX_MESSAGE_LENGTH is provided by the MySensors library.

                ben999B Offline
                ben999B Offline
                ben999
                wrote on last edited by ben999
                #9

                @mfalkvidd thank you very much for your detailed explanation !

                It does make a lot of sense

                And it compiles perfectly

                Thanks again for taking the time to correct and explain :)

                ben999B 1 Reply Last reply
                1
                • ben999B ben999

                  @mfalkvidd thank you very much for your detailed explanation !

                  It does make a lot of sense

                  And it compiles perfectly

                  Thanks again for taking the time to correct and explain :)

                  ben999B Offline
                  ben999B Offline
                  ben999
                  wrote on last edited by ben999
                  #10

                  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
                  }```
                  1 Reply Last reply
                  2
                  • N Offline
                    N Offline
                    Newzwaver
                    wrote on last edited by
                    #11

                    Nice project would love to see the finished product as I want to try it as well. Can you post all the details once you get it do Easy?

                    Thanks and good luck

                    1 Reply Last reply
                    0
                    • N Offline
                      N Offline
                      Newzwaver
                      wrote on last edited by
                      #12

                      Hi I need your help, I created this clock and complied the sketch, looked like it worked but when adding it to very I get the error message that D_LcdText1.xml is not available. Where do I find that and how do i add it to the Vera Plus?

                      Excellent project and can't wait to have it up and running.

                      korttomaK 1 Reply Last reply
                      0
                      • N Newzwaver

                        Hi I need your help, I created this clock and complied the sketch, looked like it worked but when adding it to very I get the error message that D_LcdText1.xml is not available. Where do I find that and how do i add it to the Vera Plus?

                        Excellent project and can't wait to have it up and running.

                        korttomaK Offline
                        korttomaK Offline
                        korttoma
                        Hero Member
                        wrote on last edited by
                        #13

                        @Newzwaver you can find the needed files here

                        • Tomas
                        1 Reply Last reply
                        1
                        Reply
                        • Reply as topic
                        Log in to reply
                        • Oldest to Newest
                        • Newest to Oldest
                        • Most Votes


                        20

                        Online

                        11.7k

                        Users

                        11.2k

                        Topics

                        113.1k

                        Posts


                        Copyright 2025 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
                        • Login

                        • Don't have an account? Register

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • MySensors
                        • OpenHardware.io
                        • Categories
                        • Recent
                        • Tags
                        • Popular