Go Down

Topic: Nano 33 BLE low power operation (Read 394 times) previous topic - next topic

d_green

I have a Nano 33 BLE which I plan to use in a remote sensor. Low power operation is very important - I'd like it to go many months between battery changes. I should be able to accomplish this by having the board in a deep-sleep state for much of the time, waking up periodically to send data. I made the following simple sketch to test the low-power performance of this board:
Code: [Select]
// Low power blink

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
 
  pinMode(D2, INPUT_PULLUP);
  digitalWrite(LED_PWR,LOW);    // keep Power LED off
 
  pinMode(PIN_ENABLE_SENSORS_3V3, OUTPUT);
  pinMode(PIN_ENABLE_I2C_PULLUP, OUTPUT);
  digitalWrite(PIN_ENABLE_SENSORS_3V3, LOW); // turn off sensors
  digitalWrite(PIN_ENABLE_I2C_PULLUP, LOW);
}

void loop() {

// flash LED
  digitalWrite(LED_BUILTIN, HIGH);
  delay(20);
  digitalWrite(LED_BUILTIN, LOW);
  delay(2000);
}


This sketch just turns the built-in LED on for 20ms, then off for 2s, repeating this cycle. I find that during the off phase, current draw is only 3 microamps, which is great - just what I'm looking for. It proves that during a delay(), the chip can go into a true deep-sleep mode. Note that I cut the jumper which makes it possible to power the board from an external 3.3v supply, and that's what I'm doing. I also disconnected the USB and pressed reset before the current measurement.

But, what I really need is to be able to do this from a sketch that has BLE code and functionality. Consider this sketch:
Code: [Select]
#include <ArduinoBLE.h>

// BLE Service
BLEService Service1("BA45889A-FC12-2298-33D4-2EE6554A90B0");

// BLE Characteristics
BLEByteCharacteristic count("BA45889A-FC12-2298-33D4-2EE6554A90BC", BLERead | BLENotify);   
BLEBooleanCharacteristic ccbit("BA45889A-FC12-2298-33D4-2EE6554A90BD", BLERead | BLENotify);   
BLEIntCharacteristic ca1in("BA45889A-FC12-2298-33D4-2EE6554A90BE", BLERead | BLENotify);   

byte cntr = 0;
boolean cbit = 0;
int a1in = 0;
boolean f1 = 0;


void setup() {

  pinMode(LED_BUILTIN, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected
  pinMode(D2, INPUT_PULLUP);
  digitalWrite(LED_PWR,LOW);    // keep Power LED off
  pinMode(PIN_ENABLE_SENSORS_3V3, OUTPUT);
  pinMode(PIN_ENABLE_I2C_PULLUP, OUTPUT);
  digitalWrite(PIN_ENABLE_SENSORS_3V3, LOW); // turn off sensors
  digitalWrite(PIN_ENABLE_I2C_PULLUP, LOW);
}


void loop() {
 
    // begin initialization
  digitalWrite(LED_BUILTIN, HIGH);   
  if (!BLE.begin()) {
   while (1);
  }
 
  // Set a local name for the BLE device
  BLE.setLocalName("Nano33BLE1");
  BLE.setAdvertisedService(Service1); // add the service UUID
  Service1.addCharacteristic(count);
  Service1.addCharacteristic(ccbit);
  Service1.addCharacteristic(ca1in);
  BLE.addService(Service1); // Add the service

  // start advertising
  digitalWrite(LED_BUILTIN, LOW);   
  BLE.advertise();
 
  // wait for connection
  f1 = 1;
  while (f1) {
    BLEDevice central = BLE.central();  // believe this is a declaration of local variable "central"
      // BLE.central() means query the central BLE device connected. Returns BLE device representing the central.
    if (central) {    // if central now connected
     
      // do variable checks as long as central is connected
      while (central.connected()) {    // while central connected
        // bleDevice.connected means query if a BLE device is connected. Returns true if connected.
        digitalWrite(LED_BUILTIN, HIGH);
        delay(20);
        digitalWrite(LED_BUILTIN, LOW);   // flash LED
        cntr = cntr + 1;
        cbit = digitalRead(D2);
        a1in = analogRead(A1);
        count.writeValue(cntr);
        ccbit.writeValue(cbit);
        ca1in.writeValue(a1in);
        delay(2000);
        }
   
      // when central disconnects
      BLE.end();
      digitalWrite(LED_BUILTIN, LOW);
      digitalWrite(PIN_ENABLE_SENSORS_3V3, LOW); // turn off sensors
      digitalWrite(PIN_ENABLE_I2C_PULLUP, LOW);
      delay(15000);
      f1 = 0;
    }
  }
}     


Looking near the bottom, under "when central disconnects", notice the BLE.end() line. When this wasn't included, I got current draw of about 1.4mA during the 15s wait caused by delay(15000). I hoped that BLE.end() would shut off BLE and make deep sleep possible, but current draw with that line included was 0.6mA, more than I want. Another problem is that after that, it should loop back to the top of loop() and again execute BLE.begin. BLE should restart and advertising should start again. Instead, BLE.begin fails. I know that because the LED is on solid while it loops because of the while(1). It's as if BLE.begin can't be called more than once in a program. Is that true?

In other sketches, I've tried doing NRF_POWER->SYSTEMOFF = 1. That also puts the board in a very low-power state. A problem is that there apparently isn't a way to wake from this using timing. Another is that if I wanted to wake from this state using an I/O pin transition, I'd need to know exactly how to specify what pin I plan to use  and whether it happens on the rising or falling edge. I'd also need to know where in my sketch to put this code.

So, my overall question is how to go into a deep sleep state from a sketch that includes working BLE code, and how to wake from that using timing or a pin transition.

Thanks

mbgrubman

#1
Feb 23, 2021, 10:04 pm Last Edit: Feb 23, 2021, 10:25 pm by mbgrubman
Hey

I made a iSpindle like device (floats and senses tilt for specific gravity during fermentation or for aquarium salinity) using the BLE 33 and Bluetooth rather than the the IP communication.

It has been a while but it did reduce my sleep current significantly.   Powered from a Lion battery with a 3.3V step up converter and can go about a month at the fastest setting and much longer if I do every 30 mins or something. I believe I also had to cut a pre-joiner on the board to get rid of the voltage regulator and run on the stepped up 3.3V.
I have an external Real Time Clock that wakes the Nano up at the configurable settings (every 1 min, 2 min, 5min,... etc).  I also have an OLED display powered from a Nano Discrete Pin that I turn on after power up (if required).  I also have an external EEPROM and RTC powered off of another pin.
When the RTC clock is powered off the small coin battery will still wake up the Nano - which then powers on the OLED (if needed), powers on the RTC and EEPROM and then starts the I2C bus (bus does like to be started until items are up and stable).  It will read its config from the EEPROM, do its measurements and calcs, broadcast via iBeacon the parameters,  setup the RTC for the next wakeup, and go back to sleep.  It also uses the Magnetic field sensors to drive a menu/config system (senses this while floating in the sealed tube on top of the water) for the OLED and if changed save the values to EEPROM.  The mag sensor senses blips and holds on the left/right/and top - all done with a magnetic screwdriver.

I should note that my code only enters loop if it senses a mag field and enters config mode.  Otherwise it does what I described above in the setup section.  In loop (config mode) it will sense inaction and again set the RTC up for the next wakeup and do an order shutdown.

I used a 3231 RTC board with coin cell.  I also used a single 8-pin DIP 24C32 EEPROM to save config externally (this is what the BLEs should have built in - pretty much gets added to any project I do).

Here are my interrupt items

// Interrupt Pin used
static const byte wakeUpPin = 4;

// Interupts when On
pinMode(wakeUpPin, INPUT_PULLUP);       // set RTC Interupt as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
// set an interrupt on RTC WakeUP, looking for a falling edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(digitalPinToInterrupt(wakeUpPin),PinRTCint,FALLING);

// Setup Nano to Wake on Low Voltage from RTC Alarm Output
nrf_gpio_cfg_sense_input(digitalPinToInterrupt(wakeUpPin), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);

Here is my Power Down steps.

// Kill RTC/EEPROM before shutdown
rtcenb = kill_RTC();

// Turn Off Built In Connection LED
digitalWrite(LED_BUILTIN, LOW);

// turn Off power LED
digitalWrite(LED_PWR, LOW);

// Turn Off Sensors
digitalWrite(PIN_ENABLE_SENSORS_3V3, LOW);

// Turn Off Pullups
digitalWrite(PIN_ENABLE_I2C_PULLUP, LOW);

//  Force Nano to Sleep
NRF_POWER->SYSTEMOFF=1;

Go Up