Nano IOT saving power with low power mode

our solution design uses a Nano IOT as the BLE advertising system.

the product placement does not reliably have wall power nearby, so battery power is critical.

looking thru the reports here and our measurements, running 100% active won't work, so some sort of cyclic reduction in power consumption is required.

these measurements have been done with 9v and 2s Lipo at 7.4v.

with the Radio on for advertising I see approx 60ma
with radio off, approx 12ma (same as just empty loop())

so I have been investigating using one of the low power approaches
the Adafruit SleepyDog library or the Arduino Lowpower library

advertise for 5 seconds every 25, and sleep for 20 seconds. (not finally tuned)
i have also customized our app to advertise and have the arduino never advertise
until the app is nearby. Measurements says this is not helpful, as the power consumption goes to 60ma, as soon as we scan or advertise.

when asleep, power consumption drops to just over 3ma.

but.. it appears in both cases the millisecond clock is turned off, and not corrected on return from sleep

and my code timing loops no longer function reliably.

running on USB without sleep the sketch works reliably and start/stops BLE as expected

on battery with sleep, we get erratic behavior as if the millis() return value is not consistent.

loop(){ 
   now = millis()
   if(not Advertising){
      if(now >startedScanning+timeScanning){(5 seconds)
          scanning = false;
          lastScanning=now
          stopScanning(), then sleep(20 seconds)
               amperage drops to 3.3ma here
      }
    

//  after a few cycles (10), there is a period where power draw is 12ma, for 20 seconds
//  then high power for 5 seconds (scanning started)
//  then low power (3.3ma) so scanning stopped
// after 15-18 cycles, power is 60ma, so the radio is on all the time
// I have commented out the later code that toggle advertising, and still get stuck

      // once we wake up, this time limit should be expired
      // as last scanning was saved BEFORE sleep
      if(now >lastScanning+SleepTime){ (
         startScanning()
               scanning=true
               startedScanning=now
               amperage climbs to 60ma here
      }
   }

   // commented out for testing
   if(scanning){
       if a peripheral beacon arrives
           and its our client {
           startedAdvertising=now
           startAdvertsing()
       }
   
      if(now > startedAdvertising+AdvertisingLimit){
                stopAdvertising()
      }
   }  // end if scanning
}  // end loop

as a next step, I'm considering the power off approach with the external rtc.
are there any projections on how many power cycles the iot can take before failing.
this is seconds between, not minutes.
lets say on/off every 7 seconds, 9 times a minute ~ 13000 times a day
hard turn off every 7 seconds..

so, I have moved over to timer2 for millisecond timings

and the code runs for hours, not using Low Power library
while usb connected to my mac dev machine

but using battery power and the LowPower lib, it runs great for 30-45 minutes,
toggle between 3.3ma power usage(15 seconds) and 64ma power usage(5 seconds)
then gets stuck in full power mode (64ma)
maybe the code crashed?
I have commented out everything but the primary loop, and there is only one decision

has it been 5000ms since we started BLE operations?
if so, go to sleep, 15 seconds
wake up, start BLE operations
reset start time, for use in compare above

I haven't found a way to debug it..
I connected a USB to serial adapter on my linux box and opened the port,
and have tx/rx setup properly usb tx-> nano rx, usb rx-> nano tx

the sketch does a 30 second loop while waiting for an IR sensor to settle after power on,
this uses delay(1000) to wait and each cycle prints a dot.
i connected a serial port monitor on my linux box to the usb port and i see 1 dot. then nothing

if I do Serial.begin(112500); while(!Serial);
it hangs at the while !..
it doesn't hang on usb connected

all the doc on Serial says it 'just works'
also tried setting to 9600 bps.. no difference

what am I doing wrong, this should be so easy.

The Arduino Nano 33 Iot uses native USB. The baudrate setting in the function is just for sketch compatibility and is ignored.

That is intentional. This is designed to ensure you can see all messages in the Serial Monitor. It is a simple way to wait for USB to be enumerated and the user to open the Serial Monitor. On old Arduinos this is not necessary because the COM port can stay connected during reset because USB is handled by a separate chip.

The low power device should be the peripheral. Advertisement is lower power than scanning. This seems counter intuitive because you seem to need more energy for transmission. The reason is transmission can be fire and forget. The receiver/central needs to be active long enough to catch the transmission and then hit a small window where the peripheral listens to connect.

maybe the BLE firmware needs settle time? (the doc doesn't say) I've added 100ms delays after BLE operations.. with no change

this time it ran great for 8 minutes... normally 20-25...

thats not what I see

if I setup to listen for beacons (peripheral) with BL.scan(); I see 64ma power consumption,
if I setup to advertise(after detecting a peripheral), I see 64ma power consumption, BE.poll() to keep this alive.

here is the core loop

#define useSleep
boolean active=false;
boolean scanning=true;
unsigned long start=0;
#define scantime 5000
#define asleeptime 15000
setup(){
   BLE.begin();   // start scanning
   start=millis();    // record when 
}
void loop() {
  now =getMillis();  // gets our ulong ms counter from timer2 interrupt

  if (!active) {     // we haven't seen our partner app yet
    if (scanning) {
      if (now  > (start + scantime)) {
        Serial.println("go to sleep, save power");
        scanning = false;
        BLE.stopScan();  // stop scanning
        delay(100); 
        BLE.end();           // end ble operations, turn off radios etc
        delay(100);
        int limit=asleeptime;               
        #ifdef useSleep                 
          #ifdef useSleepy      
            Serial.println("going to low power mode via SleepyDog"); 
            while(limit>0){    
              // sleep as long as we can           
              int sleepMS = Watchdog.sleep(limit);
              // sleepMS says how long we DID sleep for
              // reduce the remaining time by how much we have slept already
              // supposedly 8 seconds is most..
              limit-=sleepMS;                    
            }              
          #else
            Serial.println("going to low power mode via LowPower");
            // power consumption drops to 3.3ma
            LowPower.deepSleep(limit);
          #endif
          #if defined(USBCON) && !defined(USE_TINYUSB)
               USBDevice.attach();
          #endif     
        #else
          Serial.println("not going to low power mode, just looping");
          unsigned long no = getMillis();
          while(limit>0 && now<(no+asleeptime)){
            now=getMillis();
          }          
        #endif             
        Serial.println("wake up and scan");
        scanning = true;
        start = now;
        // power consumption rises to 64ma
        BLE.begin();   // start ble operations again
        delay(100);
        BLE.scan();    // start scanning
        delay(100);
      } else
        BLE.poll();    // scanning  
        delay(100);
    } 
  }
}

maybe the ArduinoBLE library has some dependency on timer 0?

this runs for hours and hours when usb connected and using looping for sleep

As long as you can measure any current for your peripheral device you know you do not have a BLE device. BLE was designed to run on coin cells for month on the peripheral side. I do not know how low the Arduino Nano 33 IoT can go because the radio is not a BLE only radio and the dual processor system with 240MHz clock is not helping ether.

I would not expect the ArduinoBLE library to use timers on the SAMD21.

i have it now recreate-able on USB power.

after 15 minutes or so

Serial.println("before available()"); // this line is printed
BLEDevice peripheral=BLE.available(); // this call never returns
Serial.println("after available()"); // this line is not printed

I have moved the BLEDevice peripheral off of the loop stack, no change
I don't know if the code crashed , there is no useful try/catch

currently running tests without calling BLE.available()
hangs at BLE.stopScan() // removed, still hangs later
hangs at BLE.begin(); // after sleep wakeup

According to the chart here GitHub - ostaquet/Arduino-Nano-33-IoT-Ultimate-Guide: Arduino Nano 33 IoT - Ultimate guide
i'm not far off from this table
I am testing with 7.4v (lipo 2s) and 9v sources
3.3 ma in sleep
11.4 ma in spin loop
34 ma in advertise only, and loop() with 3 compares, no BLE calls from sketch loop()
64 ma in ble scan, and loop with 3 compares, no BLE calls

BLE.begin() is 31ma
BLE.advertise() is 34ma

i have it now recreate-able on USB power.

after 15 minutes or so

reported on github

well, there are at least a few lines of code in ArduinoBLE that relay on timer 0.

./utility/HCIUartTransport.cpp:  for (unsigned long start = millis(); (millis() - start) < timeout;) {
./utility/ATT.cpp:  for (unsigned long start = millis(); (millis() - start) < _timeout;) {
./utility/ATT.cpp:  for (unsigned long start = millis(); (millis() - start) < _timeout;) {
./utility/ATT.cpp:  for (unsigned long start = millis(); (millis() - start) < _timeout;) {
./utility/HCI.cpp:  for (unsigned long start = millis(); _cmdCompleteOpcode != opcode && millis() < (start + 1000);) {

You are right. I missed these. It would be nice if Arduino would provide some basic documentation about resources used by libraries.

1 Like