Sleep3 class to be used with many interrupts



  • I have been struggling with the MySensors sleep method. My PIR sensor is not using one of the two supported interrupts.
    I have started writing some code, but that is just a beginning. Is anybody interested in this subject or should I just write it for my own use?
    Any help would be welcome, it is not unlikely that I am missing something important in this mechanism.
    The actual (partial) code can be found at
    https://sourceforge.net/p/easypirmultisensorsbox/code/ci/cvdenzen/tree/easyPIRmultisensorsBox2_155/easyPIRmultisensorsBox2_155/

    I named the new class Sleep3.

    Advantages of current (december 2021) sleep implementation:

    • it works
    • it is simple to use
    • there is a good description how to implement it
      Problems with current (december 2021) sleep implementation:
    • it only handles two interrupt sources
    • it captures these two interrupts, but these interrupts don't belong to the sleep mechanism
    • it only works for ATMega
    • it is not scalable to many interrupt sources
    • the return value is not safe: it is possible to miss interrupts if two interrupts happen simultaneously

    Trying to make a better sleep (class Sleep3) that improves some of the shortcomings:

    • only an interrupt service routine for the watchdog timer, the other interrupts are not of our (Sleep3) concern
    • Implement the Observer pattern
    • Use the Observer pattern to register methods that can veto a sleep call (see note 1)
    • turn off adc only once (not every iteration of sub-sleep)
    • turn off network only once (not every iteration of sub-sleep)
    • Two public methods: "sleep()" (forever), and "sleep(unsigned long sleep_time)". They return a struct
      with a few members: int errno (e.g. re-entry not allowed), bool wokeUpByWDT (if false, some other interrupt triggered wake-up),
      unsigned long requestedSleepMS, unsigned long sleepRemainingMS.
      requestedSleepMS-sleepRemainingMS can be used to adjust the millis() counter ?? it will not be accurate (can be 8 seconds off in avr).

    Note 1:
    If there is a non-processed interrupt, the cpu must not go to sleep. That happens e.g. in this scenario: the cpu is executing the loop() code and is almost at the point to call sleep(). A PIR sensor triggers an interrupt, the ISR is run, it sets a flag that is to be checked in the loop(). At that point the cpu would go to sleep. The flag would only be seen after the sleep call!
    Solution: check whether sleeping is allowed, same scenario extended with the check:
    the cpu is executing the loop() code and is almost at the point to call sleep(). A PIR sensor triggers an interrupt, the
    ISR is run in which a flag is set to be checked in the loop(). New: at that point the cpu disables interrupts, calls the
    callback methods of the observers and will be signaled by the PIR observer that it should not go to sleep(), but continue its processing. The loop is re-entered immediately and the PIR data can be sent to the gateway.

    The Observer method will be called in a locked (interrupts disabled) state just before the hardware sleep is entered.
    If the Observer notices that there is a pending interrupt on behalf of its subject, it should return false, otherwise
    it should return true (as in: allright, go to sleep).

    // The Sleep3 code that calls the observers will look like this:
    boolean sleepIsAllowed=true;
    noInterrupts();
    for (Observer observer:observers) { if (!observer()) {sleepIsAllowed=false;break}}
    if (sleepIsAllowed) {interrups();sleep;} else interrupts();
    
    // An example application looks like this:
    //
    Sleep3 sleep3; // Sleep3 is the proposed sleep class
    before() {
      sleep3.attach(sleepObserver);
      .. enable interrupts etc.
    }
    ISR (PCINT2_vect) {
      boolean PIRPinValue = digitalRead(PIR_PIN);
      queue.add(PIRPinValue);
    }
    // This method is entered with interrupts disabled
    boolean sleepObserver() {
      if (!queue.isEmpty()) return false; else return true;
    }
    void loop() {
      noInterrups(); // to lock the queue
      if (!queue.isEmpty()) {
        boolean pir=queue.remove();
        interrupts();
        // do something with pir
      } else {
        interrupts();
      }
      sleep3.sleep(SLEEP_TIME);
    }
    


  • @cvdenzen
    I for one am interested, I have raised issues I have had with sleep, and interrupts in other forum entries.

    Entry here



  • @Nigel31 Thank you, I will try to write some code and test it. It can take a few weeks as I have to do it in my spare time (like most of us).


Log in to reply
 

Suggested Topics

  • 1
  • 5
  • 2
  • 1
  • 2
  • 198

15
Online

11.2k
Users

11.1k
Topics

112.5k
Posts