Help needed sleeping the Atmega4809 Arduino Nano Every

My project is a soil moisture sensor, powered by a battery, that is charged with a solar panel, sending data over ethernet-over-nRF24l01 radio (parts under $25, without housing $?) to an MQTT broker (RPi). I have this running on an older Nano and radio.

Now I'd like to use the Nano Every. My question is pretty much the same closed question at Sleep Mode Atmega4809 Arduino Nano Every

LowPower.h provides a simple way to power down the non-mega CPUs. Granted the max sleep time is 8s, but you can string those 8s together with only being powered up for a few uSecs between them.

The "LowPower.h" library doesn't work on the Every. The closed topic "Arduino Nano Every" links to "avr-libc: <avr/sleep.h>: Power Management and Sleep Modes". The information on that page is inadequate and makes the useless reference "Refer to the datasheet for the details relating to the device you are using"

The documentation I have found has a lot of "if you screw this up you can brick your device" and a lot of language referring to CPU registers, a place the inexperienced should avoid. This last link talks about turning off various timers and interrupts, but doesn't tell one if they get turned back on after returning from sleep.

I just need a little hand holding.

Thanks.

Sleep mode handling is completely different, and much more flexible, with the ATmega4809 on which the new Nano Every is based. This chip has an RTC peripheral and you have to configure this by register entries. Probably, the PIT (periodic Interrupt timer) is the best in your case if you need the Power Down sleep mode.

This may help: http://ww1.microchip.com/downloads/en/AppNotes/TB3213-Getting-Started-with-RTC-90003213A.pdf

If not, put together a very simple sketch for the old Nano which simply demonstrates forcing it to sleep and waking afterwards and I (or someone else) may give you some pointers to convert it for the Nano Every.

EDIT

Here is a simple example of Sleep mode. It performs a cycle of blinking 5 times, then sleeping for 8 seconds.

/*
  Nano Every: Cycle of Blink 5 times then Sleep for 8 seconds
  Configuration option: No ATmega328P Register emulation

*/

#include <avr/sleep.h>
uint8_t loopCount = 0 ;


void RTC_init(void)
{
  while (RTC.STATUS > 0) ;    /* Wait for all register to be synchronized */
 
  RTC.CLKSEL = RTC_CLKSEL_INT1K_gc;        // Run low power oscillator (OSCULP32K) at 1024Hz for long term sleep
  RTC.PITINTCTRL = RTC_PI_bm;              // PIT Interrupt: enabled */

  RTC.PITCTRLA = RTC_PERIOD_CYC8192_gc | RTC_PITEN_bm;     // Set period 8 seconds (see data sheet) and enable PIC                      
}

ISR(RTC_PIT_vect)
{
  RTC.PITINTFLAGS = RTC_PI_bm;          // Clear interrupt flag by writing '1' (required) 
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  RTC_init();   
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // Set sleep mode to POWER DOWN mode 
  sleep_enable();                       // Enable sleep mode, but not going to sleep yet 
}


void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(200);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(200);                       // wait for a second

  if( ++ loopCount == 5 ) {
    loopCount = 0 ;
    sleep_cpu();                     // Sleep the device and wait for an interrupt to continue
  }
}

@OldSurferDude Please edit your post and fix the link.

I don't have the Atmega4809 (yet) but it looks like you can do sleep periods well in the days interval compared to the 8 seconds you get from older AVRs

Thanks, 6v6gt! I'll be looking at the link. Here is a functional example:
SleepExample_old_.ino (1.6 KB)

Sorry, I, too, see that the link is dead and I can't find the post. This thread has some good information, while the former had no responses.

If you're looking for answers to the question of sleeping the Every, this is a good place.

If you're looking to support us who do not know how and have better information, this thread would also be a good place to post.

Thanks for alerting us that the link is dead.

OSD

Another Thanks, 6v6gt. Your code worked without modification (though I did put in some comments to jog my own memory :slight_smile:

And this got me to thinking, I'd like to be able to run the same code on either Nano. Is that possible? A compiler directive would work, but is there something that could be done at run time?

Also, in my readings I read some warnings about trying to download new code while the Nano Every is in sleep. Could you comment on that?

Thanks

OSD

Your only option to support both the original Nano and the Nano Every in one sketch is with compiler directives to exclude code for the architectures which do not match that of the board type selected at the time you compile. Registers for the other architecture would not be recognised.

Can you post a link to this . . . ?

I'll be looking for the link. But in the mean time, I put print statements before and after the sleep statement.
//-------------------------------------------------------------------------------loop
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(200); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(200); // wait for a second

if( ++ loopCount == 5 ) {
loopCount = 0 ;
Serial.print(F("Lulaby..."));
sleep_cpu(); // Sleep the device and wait for an interrupt to continue
Serial.println(F("Smell the coffee"));
}
}

Output:
⸮⸮⸮⸮⸮..Smell the coffee
⸮⸮⸮⸮⸮..Smell the coffee
⸮⸮⸮⸮⸮..Smell the coffee

same output when the F() function is removed.

Not what I would expect :grimacing:

OSD

fixed, thanks for pointing it out :slight_smile:

That behaviour is not new. The Serial buffer does not survive a sleep.

/*
  Nano Every: Cycle of Blink 5 times then Sleep for 8 seconds
  Configuration option: No ATmega328P Register emulation

*/

#include <avr/sleep.h>
uint8_t loopCount = 0 ;


void RTC_init(void)
{
  while (RTC.STATUS > 0) ;    /* Wait for all register to be synchronized */

  RTC.CLKSEL = RTC_CLKSEL_INT1K_gc;        // Run low power oscillator (OSCULP32K) at 1024Hz
  RTC.PITINTCTRL = RTC_PI_bm;              // PIT Interrupt: enabled */

  RTC.PITCTRLA = RTC_PERIOD_CYC8192_gc | RTC_PITEN_bm;     // Set period 8 seconds (see data sheet) and enable PIC
}

ISR(RTC_PIT_vect)
{
  RTC.PITINTFLAGS = RTC_PI_bm;          // Clear interrupt flag by writing '1' (required)
}

void setup() {
  Serial.begin( 9600 ) ;
  pinMode(LED_BUILTIN, OUTPUT);
  RTC_init();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // Set sleep mode to POWER DOWN mode
  sleep_enable();                       // Enable sleep mode, but not going to sleep yet
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
  delay(200); // wait for a second
  digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
  delay(200); // wait for a second

  if ( ++ loopCount == 5 ) {
    loopCount = 0 ;
    Serial.print(F("Lulaby..."));
    Serial.flush() ;
    delay( 10 ) ;
    sleep_cpu(); // Sleep the device and wait for an interrupt to continue
    Serial.println(F("Smell the coffee"));
  }
}

Notice now the flush() and delay(). There are probably better ways than the delay to ensure the buffer is clean before the sleep.

Thanks! I should have remembered that as I do delay before sleep in my project code (MQTT client over ethernet over nRF24l01)

I'm still looking for that page

found that link to the warning about downloading code while Nano Every is sleeping Section 2, subsection "Putting your device to sleep"

Warning

Be careful! If the microcontroller is put to Power-down sleep mode without first defining interrupt procedure, the microcontroller will not wake up. The only way to wake it up is by provide a signal to the RESET pin. As a result of this situation, if you try to reprogram the microcontroller in sleep mode, you will not be successful. The tool for uploading the code to the microcontroller will attempt, in case of failure, to upload the code again. Rebooting the microcontroller in this moment is a suitable solution. As a result, the microcontroller wakes up and, during the startup, the microcontroller code is recorded.

I'm getting an error from the compiler, on the sleep_cpu() lne:

error: expected ')' before '::' token
     sleep_cpu();
     ^

I can see where in sleep.h the error is coming from:

#define sleep_cpu() \
do { \
   __asm__ __volatile__ ( "sleep" "\n\t" :: ); \
} while (0)

What I don't understand is why this should be causing an error for me, when it seems it's OK for everbody else!

Any help gratefully received.

Have you defined your own macros, say:
#define “sleep”
or show some of the code which immediately proceeds it.

Thanks for replying, @6v6gt. No, I definitely haven't defined a sleep macro (or even a sleep_cpu()). I'd have said: that the sleep_cpu() macro in sleep.h is consistent with the error message argues against that.

My code fragment is as follows:

    // switch off external devices...
    sleep_enable ();
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    Serial.println ("Going to sleep");
    Serial.flush ();
    delay (10);
    sleep_cpu(); // <-- compile error here.
    Serial.println ("Just woke up!");

... meanwhile, in an interrupt service routine, far, far away (connected to a button, not RTC):

    sleep_disable(); 
    // restore devices

Then it is the "binary chop" strategy. Start with an absolute minimum sketch that does little more that contain the problem statement. If that is OK, keep dividing the main sketch in half and focus on the bad half.
If even the minimal sketch highlights the problem, look at things like switching off register emulation in the tools menu.
But, as you have pointed out, you'll not be the only person to use sleep on a Nano Every (or whatever it is) so maybe something unique in your environment.

It's taken a bit of tracking-down, but I've found the answer. Basically, sleep_cpu() needs to be inside loop(), otherwise nothing about it works properly.

Nearly everything in my program is being driven by interrupts (its a hard, real-time application), and the sleep logic was in a cpp file (which had #include "Arduino") in a routine that was triggered by a button-click interrupt. I found:

  • sleep_cpu() didn't compile (as noted above). It compiled only when it was inside loop() in main.ino;
  • __asm__ ("sleep") did compile, and it appeared to execute, but the device didn't seem to sleep. Furthermore, the sleep control register appeared to reset to zero as soon as I set it up. (In fact, it was sleeping, but something else was waking it up and clearing the register). When setting up the sleep inside loop(), everything worked fine.
  • Far from being necessary to enable sleeping, delay(10) just crashed the processor.

At some point, I'm going to look into the Arduino code to see what's inside the .ino bootstrap that would make this work, but for now, I've just put the sleep logic inside loop and everything seems to be fine.

Hope this helps somebody else.

I used this script to make measurements of power draw when the board is awake vs sleeping.
To my surprise I didn't see much difference.

27.8mA while awake and 22.3mA while sleeping. Not much of saving.

It doesn't even shut off the +5V pin while sleeping, so it even not suitable to turn off attached sensors if there are any.

Probably because the USB Cortex-M0 chip never turns off and continues to consume most of the energy?

Is there any room for further optimization?

@kostrse
OK. I am not sure exactly what power consumption improvement you can expect by using the sleep modes on a Nano Every. Usually, power sensitive applications are not run on a development board with all its power hungry peripherals, as you have already understood. The target environment would be closer to the raw ATmega4809 chip.

Anyway, sleep mode does not alter the status of the pins (High, Low or high impedance). It may change the oscillator, shut down internal peripherals like the ADC etc. to reduce power consumption.

For external peripherals, you shut these down by (a) their own shutdown pin (if available) or (b) by using a transistor/ mosfet to cut off power to these.