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
    #2

    Guys

    Making progress, mainly on sketch-side of it.

    Now on the hardware side of it : i would like to power the clock via a single 18650 cell (2000mah from old laptop)

    So i need a regulation unit, as LiIon cell will run from 4.2V down to 2.3V (cut-off from protection board)

    NRF, arduino nano and 5110 screen need 3.3V supply
    NRF and arduino will draw up to 30mA when transmitting
    5110 screen backlight will draw 50mA, logic seems to draw nearly nothing...
    So that would be a grand total of less than 100mA

    That converter would need to step-down as well as step-up depending on "petrol level" of the cell...
    Does it even exist ??

    @NeverDie suggested this elsewhere on the forum... http://www.ti.com/product/TPS82740B/description&lpos=Middle_Container&lid=Alternative_Devices

    Or shall i simply connect the LiIon cell straight to arduino board (raw pin) and let it power the screen and radio (3.3 pin) ?? As it seems the on-board regulator can handle upto 150mA... smell of burning chips... ? :laughing:

    Would it fit the bill ? Any suggestions ?

    Thanks a lot !

    1 Reply Last reply
    1
    • 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