Sleep and Wake for battery longevity

sleepwake

When the boolean is true, the board will go to into full sleep waiting for the condition to be false and wake up.

Inputs

  • COND (boolean, default=0) — True is asleep, False is awake.

Outputs


I am a beginner. I’d like to accomplish sleep and wake, they are ying and yang, two halves of the same thing. I’ve found the sleepydog node, but seems incomplete and hard to understand without extensive background knowedge. I’m not a coder, that’s why I’m here using XOD, which is absolutely fantastic, albeit still missing some powerful nodes.

Context: I’m making an automatic chicken door opener that opens and closes with daylight. It works.
Now I need to conserve the battery because the board only needs to be awake twice a day for door movement.

The best nodes I’ve seen in XOD have been approached with an elegant simplicity for Pete, even if under the hood it’s a complex action.

I have an incline that because it’s a asleep, the patch doesn’t run, so it has to wake up through a pin going high? In which case (although a problem for waking through an LDR wether day or night), this node could receive:

Inputs

  • PULSE (pulse, default=0) — Pulse that sleeps the board.
  • PORT (port, default=2) — Pin on high that wakes the board.

Surely a sleep/wake node is a big deal for projects running on battery?

1 Like

The sleepydog node is really a timer to wake up but we don’t have the same thing based on an interupt pin. e.g. pulse to sleep, and when it gets light or a PIR detects movement, wake up on that pin change.

Regarding the latter two inputs above, I’m trying to adapt this following code into this XOD node: https://github.com/RalphBacon/Arduino-Deep-Sleep/blob/master/Sleep_ATMEGA328P.ino#L1

Anyone want to help please?

Ok so I’ve tried to do this myself anyway for the benefit of anyone who cares.

This throws up errors I don’t understand.
I’m not sure how to handle the wakeISR interupt event when it happens, should this be in it’s own void wakeISR function here, or should that be in another node?

This code is taken from the github link in my question.


{{#global}}
#include <avr/sleep.h>
{{/global}}


struct State {
};

{{ GENERATED_CODE }}

void evaluate(Context ctx) {
         if (isInputDirty<input_SLEEP>(ctx)){

             static byte prevADCSRA = ADCSRA;
             ADCSRA = 0;
             set_sleep_mode (SLEEP_MODE_PWR_DOWN);
             sleep_enable();
             MCUCR = bit (BODS) | bit (BODSE);
             MCUCR = bit (BODS);
             noInterrupts();
             attachInterrupt(digitalPinToInterrupt(<input_WAKE>(ctx)), wakeISR, CHANGE);
             interrupts();
             sleep_cpu();
//is asleep now, waiting for input WAKE port to change, wakeISR should run these two lines:
//           sleep_disable();
//           detachInterrupt(digitalPinToInterrupt(wakePin));
             ADCSRA = prevADCSRA;
             }
}

I was going to suggest you look at intigrating an existing arduino library such as this one:

or one of the others available via google search.

probably trying to follow some of the existing examples such as this:
https://xod.io/docs/guide/wrapping-arduino-libraries/

I’ve only been using XOD a week, and have had my own issues attempting to integrate what I thought was a “simple” bit of code into XOD and failing … https://github.com/NicksonYap/digitalWriteFast

1 Like

Thanks @robertspark. So many have seen this, my first post and not said hello or tried to help :frowning:

Regarding my previous code, how do you think calling another function should be done in xod’s void evaluate(Context ctx){} function?

attachInterrupt(digitalPinToInterrupt(<input_WAKE>(ctx)), wakeISR, CHANGE);

Oh, so sorry for being hostile. Hello and welcome! :hugs:

Well, I think you are very close to success. The only thing left to do is inserting the ISR routing. Unfortunatelly, C++ don’t have so-called function closures / partially applied functions out of the box. The best thing you have is a parameterless global function. Which should be enough to solve the problem though.

Haven’t checked it but here’s outline of how it can look like:

{{#global}}
#include <avr/sleep.h>
{{/global}}

struct State { };

{{ GENERATED_CODE }}

// To transfer the context to the ISR function, we have to keep the
// interrupt number global. Will set it in `evaluate`
uint8_t g_interruptNum = -1;

// Also store the ADC state to bring it back
uint8_t g_prevADCSRA;

void sleepISR() {
  // Prevent sleep mode, so we don't enter it again, except deliberately, by code
  sleep_disable();

  // Detach the interrupt that brought us out of sleep
  detachInterrupt(g_interruptNum);
  g_interruptNum = -1;

  // Restore ADC state
  ADCSRA = g_prevADCSRA;
}

void evaluate(Context ctx) {
  if (!isInputDirty<input_SLEEP>(ctx))
    return;

  auto wakePort = getValue<input_WAKE>(ctx);
  g_interruptNum = digitalPinToInterrupt(wakePort); // save globally
  g_prevADCSRA = ADCSRA;
  ADCSRA = 0;
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS);
  noInterrupts();
  attachInterrupt(g_interruptNum, wakeISR, CHANGE);
  interrupts();
  sleep_cpu();
}

2 Likes

hi, it is a bad habit, sometimes I do not understand I do let another user with knowledge say. But beware that there are also users who never thank …:smile:

1 Like

Thanks @nkrkv ! I see what you’re doing there. That would of taken me weeks to work out, since this is not my background or strength.
I changed the void sleepISR to void wakeISR and it runs and uploads, but it is not going to sleep.

Should any of the global settings go inside:

void evaluate(Context ctx) {
    if (isSettingUp()) {

or into the struct State {}; ?

And does the g_interruptNum refer to the port number? in which case why is it -1 ?

OK. I’ve checked it live and published a library: https://xod.io/libs/nkrkv/avr-sleep/

The g_interruptNum keeps the interrupt number. Port numbers and interrupt numbers are not the same. For example, D2 has interrupt number 0 and D3 has 1. -1 is just a placeholder for invalid value, nothing.

While trying the node yourself, please, keep in mind that you have to use the WAKE port capable of handling external interrupts (D2 and D3 or Uno and Nano).

1 Like

That is superb, works a treat! I see you’ve done a few good things to make it a proper node. Like a done pulse and a condition to turn off the interupt pin when not in sleep.
Curious question: the sleep_bod_disable(); before sleep. Is this supposed to be enabled on wake? And does that line replace the MCUCR ?
Thanks @nkrkv !

Glad it solves the problem.

Yes, it’s instead of MCUCR manipulation: this marco function just makes code a bit more readable. And no, you haven’t to enable the brownout detection when awake, it happens automatically. But you have to call it again right before the next sleep (which is done by the node). See: https://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html#gabf889562cc5ea768ee80cfc8a5bb0312

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.