One-time init only



  • I've hooked up a DCF77 receiver to an Arduino Nano that I would like to use as a 'time master' in my house.

    I've looked into various ways of getting the time from this node to other nodes. Problem is that once a message fails (because of radio NACK), the whole sketch is re-run, including the startup of the DCF receiver.

    I've put initialising and starting the DCF reception in preHwInit(), but it seems this one is called before every radio init.

    Is there a function that is called only once? Is there a place I can patch the -master branch to get a function that is called only once? I would contribute back to -master, of course!

    Christ van Willegen


  • Mod

    There is function that can be called to get time from controller if you have one, if that's a possible solution


  • Hero Member

    @ChristW What about using saveState and loadState

    That would allow you to store a value in eeprom and you could check that to see if it is the first time run.

    As @gohan has said it is very easy for nodes to retrieve the time from a controller as well


  • Mod

    @ChristW setup() is only called once, or you could use a boolean to check if a function has run before.



  • Hi,

    thanks for the replies!

    I have no controller, just nodes and a gateway. I wanted to make the gateway respond to getTime() messages, but could not find if this was possible or not.

    In my logs, I see this:

    Waiting for DCF77 time ... 
    It will take at least 2 minutes until a first update can be processed.
    r;255;3;0;9;MCO:BGN:INIT GW,CP=RNNGA--,VER=2.1.1
    0;255;3;0;9;TSM:INIT
    0;255;3;0;9;TSF:WUR:MS=0
    0;255;3;0;9;TSM:INIT:TSP OK
    0;255;3;0;9;TSM:INIT:GW MODE
    0;255;3;0;9;TSM:READY:ID=0,PAR=0,DIS=0
    0;255;3;0;9;MCO:REG:NOT NEEDED
    0;255;3;0;14;Gateway startup complete.
    0;255;0;0;18;2.1.1
    0;1;0;0;23;
    0;2;0;0;3;
    0;255;3;0;9;MCO:BGN:STP
    0;255;3;0;9;MCO:BGN:INIT OK,TSP=1
    EoM
    00111010000EoM
    00110010000110000010110101010110010100100110111000111010000BF
    0time lag inconsistent
    0100101000110000010101101010110010100100110111000111010000BF
    0time lag consistent
    Time is updated
    Just obtained time. Send a message!
    0;255;3;0;9;!TSF:MSG:SEND,0-0-27-27,s=1,c=1,t=48,pt=5,l=4,sg=0,ft=0,st=NACK:1490363760
    
    Waiting for DCF77 time ... 
    It will take at least 2 minutes until a first update can be processed.
    0;255;3;0;9;MCO:BGN:INIT GW,CP=RNNGA--,VER=2.1.1
    0;255;3;0;9;TSM:INIT
    0;255;3;0;9;TSF:WUR:MS=0
    0;255;3;0;9;TSM:INIT:TSP OK
    0;255;3;0;9;TSM:INIT:GW MODE
    0;255;3;0;9;TSM:READY:ID=0,PAR=0,DIS=0
    0;255;3;0;9;MCO:REG:NOT NEEDED
    0;255;3;0;14;Gateway startup complete.
    0;255;0;0;18;2.1.1
    0;1;0;0;23;
    0;2;0;0;3;
    0;255;3;0;9;MCO:BGN:STP
    0;255;3;0;9;MCO:BGN:INIT OK,TSP=1
    001110111001100010111101011110010100100110111000111010000EoM
    00101011000000100010100011011110010100100110111000111010000BF
    0time lag inconsistent
    0011110111010100010110011010110010100100110111000111010000BF
    0time lag consistent
    Time is updated
    Just obtained time. Send a message!
    0;255;3;0;9;!TSF:MSG:SEND,0-0-27-27,s=1,c=1,t=48,pt=5,l=4,sg=0,ft=0,st=NACK:1490363940
    
    Waiting for DCF77 time ... 
    It will take at least 2 minutes until a first update can be processed.
    1;255;3;0;9;MCO:BGN:INIT GW,CP=RNNGA--,VER=2.1.1
    0;255;3;0;9;TSM:INIT
    0;255;3;0;9;TSF:WUR:MS=0
    0;255;3;0;9;TSM:INIT:TSP OK
    0;255;3;0;9;TSM:INIT:GW MODE
    0;255;3;0;9;TSM:READY:ID=0,PAR=0,DIS=0
    0;255;3;0;9;MCO:REG:NOT NEEDED
    0;255;3;0;14;Gateway startup complete.
    0;255;0;0;18;2.1.1
    0;1;0;0;23;
    0;2;0;0;3;
    0;255;3;0;9;MCO:BGN:STP
    0;255;3;0;9;MCO:BGN:INIT OK,TSP=1
    001111101011000010100000000001010000100110111000111010000EoM
    00010100000001100010110000001001010000100110111000111010000BF
    0time lag inconsistent
    1100010101111100010101000001001010000100110111000111010000BF
    0time lag consistent
    Time is updated
    Just obtained time. Send a message!
    0;255;3;0;9;!TSF:MSG:SEND,0-0-27-27,s=1,c=1,t=48,pt=5,l=4,sg=0,ft=0,st=NACK:1490364120
    
    Waiting for DCF77 time ... 
    It will take at least 2 minutes until a first update can be processed.
    1;255;3;0;9;MCO:BGN:INIT GW,CP=RNNGA--,VER=2.1.1
    0;255;3;0;9;TSM:INIT
    0;255;3;0;9;TSF:WUR:MS=0
    0;255;3;0;9;TSM:INIT:TSP OK
    0;255;3;0;9;TSM:INIT:GW MODE
    0;255;3;0;9;TSM:READY:ID=0,PAR=0,DIS=0
    0;255;3;0;9;MCO:REG:NOT NEEDED
    0;255;3;0;14;Gateway startup complete.
    0;255;0;0;18;2.1.1
    0;1;0;0;23;
    0;2;0;0;3;
    0;255;3;0;9;MCO:BGN:STP
    0;255;3;0;9;MCO:BGN:INIT OK,TSP=1
    100010110011000010111000000001010000100110111000111010000EoM
    00010110001110000010100100001001010000100110111000111010000BF
    0time lag inconsistent
    0011000111100000010110100000001010000100110111000111010000BF
    0time lag consistent
    Time is updated
    Just obtained time. Send a message!
    0;255;3;0;9;!TSF:MSG:SEND,0-0-27-27,s=1,c=1,t=48,pt=5,l=4,sg=0,ft=0,st=NACK:1490364300
    

    You can see that each time a !TSF:....st=NACK is logged, you will also see MCO:BGN:STP in the log. This is the setup() callback, which is called every time that radio is initted.

    I put a static boolean into my startDCF() function, but it still starts every time. It seems static variables are initialized every time.

    loadState() and saveState() would be possible, but that would look something like this in pseudocode:

    if (loadState(xxx) == 0) {
      startDCF()
      saveState(xxx, 1);
    }
    

    ...but where do I call saveState(xxx, 0) to indicate that my time master needs to start the DCF again? Ideally I would need to do this at startup, but then I can call startDCF() instead of that...

    Christ van Willegen


  • Mod

    Do you mind if I ask what's the point of not having a controller?



  • @gohan At the moment, I have a few 'weird' nodes that would like to talk to each other. I was pointed to this site because I wanted nodes that talked to each other 'over a certain distance', and MySensors seemed like the best fit, even without being attached to a controller...

    One of the nodes is attached to my washing machine (to indicate if it's ready or not) and dryer (same reason). One node is in our daughter's bedside lamp, that turns purple at night and green at 0700 (so that she knows she can get out of bed), and I have a node attached to an LED screen with 4x 4"-high 7-segments leds. This last node can either run a specific 'show' for my choir (we needed a time machine...) or show data that sensors bring in.

    Most of my nodes don't fit into a standard model really well, and I figured I didn't need to spend EUR100+ on a controller that I wasn't going to use...


  • Hero Member

    @ChristW I am not clear on exactly where you are having trouble, perhaps posting your sketch will help.

    to just run some code once at startup you can do as @Yveaux has said and place it in setup()

    If it needs to be run once from the loop() then you could try

    
    bool executeOnce = 1;
    
    void setup() {
     
    
    }
    
    void loop() {
      
      if (executeOnce) {
        // put code here to execute once at startup
        executeOnce = 0;
      }
    
    }
    

    As to distributing the time to the nodes you could push the time using node to node messaging if they are on all the time. See here for the basic message format

    Or you can have the nodes pull the time from your Time node using request()

    See here for a bit more info



  • Here are my sketches:

    The TimeMaster:

    #define CHILD_ID_TIME 1
    #define CHILD_ID_SIGNAL 2
    
    #define TARGET 27
    // #define TARGET 26
    
    MyMessage msgTime(CHILD_ID_TIME, V_CUSTOM);
    MyMessage msgSignal(CHILD_ID_SIGNAL, V_STATUS);
    
    #include "DCF77.h"
    #include "Time.h"
    
    #define DCF_PIN 2	         // Connection pin to DCF 77 device
    #define DCF_INTERRUPT digitalPinToInterrupt(DCF_PIN)		 // Interrupt number associated with pin
    
    time_t time;
    
    bool bPresented = false;
    
    DCF77 DCF = DCF77(DCF_PIN, DCF_INTERRUPT);
    
    void startDCF()
    {
      static bool bDCFStarted = false;
    
      if (!bDCFStarted) {
        DCF.Start();
      
        pinMode(DCF_PIN, INPUT_PULLUP);
        Serial.println("Waiting for DCF77 time ... ");
        Serial.println("It will take at least 2 minutes until a first update can be processed.");
        bDCFStarted = true;
      }
    }
    
    void preHwInit()
    {
      Serial.begin(MY_BAUD_RATE);
      Serial.println();
    
      startDCF();  
    }
    
    void setup()
    {
      msgSignal.setDestination(TARGET);
      msgTime.setDestination(TARGET);
    }
    
    void presentation()
    {
      present(CHILD_ID_TIME, S_CUSTOM);
      present(CHILD_ID_SIGNAL, S_BINARY);
    }
    
    bool bHaveTime = false;
    
    unsigned long lastSentTime = 0;
    
    void loop()
    {
      time_t DCFtime = DCF.getTime(); // Check if new DCF77 time is available
      if (DCFtime != 0)
      {
        Serial.println("Time is updated");
        setTime(DCFtime);
        bHaveTime = true;
        lastSentTime = minute();
      }
    
      if (bHaveTime) {
        if (DCFtime != 0) {
          Serial.println("Just obtained time. Send a message!");
          msgTime.set(DCFtime);
          send(msgTime);
        }
    
        if (minute() != lastSentTime) {
          Serial.println("A minute has passed. Send a message!");
          msgTime.set(now());
          send(msgTime);
          lastSentTime = minute();
        }
      }
    }
    

    The night light:

    #define MY_RADIO_NRF24
    #define MY_DEBUG
    // #define MY_NODE_ID 27
    // #define MY_GATEWAY_SERIAL
    
    
    #include <MySensors.h>
    #include <SPI.h>
    
    #include <Adafruit_NeoPixel.h>
    #include <avr/power.h>
    #include <EEPROM.h>
    
    #include <Time.h>
    
    #define colorNight(s) (s.Color(255,  20, 147))    // Purple
    #define colorDay(s)   (s.Color(  0,   0,   0))    // Black
    #define colorMorn(s)  (s.Color( 44, 201,  13))    // Green
    
    #define colorError(s) (s.Color(255,   0,   0))
    #define colorUp(s)    (s.Color(  0, 255,   0))
    #define colorDown(s)  (s.Color(  0,   0, 255))
    
    #define PIN 6
    Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, PIN, NEO_GRB + NEO_KHZ800);
    
    unsigned long colorPixel = 0;
    unsigned long colorOld = 0xffff;
    
    bool bHaveTime = false;
    bool bEmergencyMode = false;
    bool bEmergencyModeChecked = false;
    
    void setPixelColor(unsigned long color)
    {
      colorPixel = color;
    }
    
    void doSetPixelColor(long color)
    {
      if (!bEmergencyMode || color == colorMorn(strip) || color == colorNight(strip)) {
        strip.setPixelColor(0, color);
        strip.show();
      }
    }
    
    void doSetPixelColor()
    {
      if (colorOld != colorPixel) {
        doSetPixelColor(colorPixel);
        colorOld = colorPixel;
      }
    }
    
    void receive(const MyMessage &message)
    {
      if (!bHaveTime && message.type == V_STATUS) {
        setPixelColor(message.getBool() ? colorUp(strip) : colorDown(strip));
      } else if (message.type == V_CUSTOM) {
        time_t timeNow = message.getULong();
        setTime(timeNow);
        bHaveTime = true;
      }
    }
    
    void presentation()
    {
      setPixelColor(colorUp(strip));
      present(0, S_LIGHT);
    }
    
    void before()
    {
      strip.begin();
    
      doSetPixelColor(colorDown(strip));
      strip.show(); // Initialize all pixels to 'off'
    }
    
    void setup() {
      // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
    #if defined (__AVR_ATtiny85__)
      if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
    #endif
      // End of trinket special code
    
      Serial.begin(115200);
      EEPROM.write(300 + 2, EEPROM.read(300 + 1));
      EEPROM.write(300 + 1, EEPROM.read(300 + 0));
      EEPROM.write(300 + 0, 0xff);
    }
    
    void loop() {
      static unsigned long int lastLoop = -1;
    
      if (lastLoop == -1 || millis() < lastLoop || millis() - lastLoop > 60000) {
    //    request(0, V_CUSTOM, 42);
        lastLoop = millis();
      }
    
      if (!bEmergencyModeChecked && millis() < 5000) {
        bEmergencyMode = false;
        bEmergencyModeChecked = true;
        bool bNeedEmergencyMode = true;
    
        for (int t = 0; t <= 2; ++t) {
          if (EEPROM.read(300 + t) != 0xff) {
            bNeedEmergencyMode = false;
          }
        }
    
        bEmergencyMode = bNeedEmergencyMode;
    
        if (bEmergencyMode) {
          setPixelColor(colorNight(strip));
        }
      }
    
      if (bEmergencyModeChecked && millis() >= 10000) {
        EEPROM.write(300 + 0, 0);
        bEmergencyModeChecked = false;
      }
    
      if (bEmergencyMode && millis() > 12L * 60 * 60 * 1000) {
        setPixelColor(colorMorn(strip));
      }
    
      if (bHaveTime) {
        if (hour() >= 19 || hour() < 7) {
          setPixelColor(colorNight(strip));
        } else if (hour() >= 8) {
          setPixelColor(colorDay(strip));
        } else if (hour() >= 7) {
          setPixelColor(colorMorn(strip));
        }
      }
    
      doSetPixelColor();
    }
    

    Emergency mode on the night light is triggered by starting and stopping it three times within 10 seconds each time, so that if it can't find a time source, you can still get 12 hours of 'night light' followed by 'day light'.

    In the night light, I commented out the request() call (and I took out the receive() implementation in the TimeMaster) because I ran into the DCF time receiver starting again every time.

    Any more info you need right now? Just shout!


Log in to reply
 

Looks like your connection to MySensors Forum was lost, please wait while we try to reconnect.