Low power sleep on Boron LTE

Hi,

Forgive me if I may have missed something obvious, but I think I have read all the threads I could find about this and don’t think I’ve been able to find a conclusive answer.

Here’s my simple app requirement:
-Wake up on wakeup pin (D8?), rising edge is probably fine (so DEEP_SLEEP should work)
-Push some data to the cloud
-Go back to sleep
-Consume <=~1mA in sleep mode

From what I saw on previous threads, this should be do-able. I saw that there were lots of quirks regarding disconnecting first before trying to turn off the cellular radio in order to hit low power consumption.

For my test setup, I am powering the Boron via Li+ @ 3.8V via benchtop supply. What I get is that sometimes when it goes to sleep I see <1mA consumed. However, other times it will be stuck at ~8mA after sleeping and never drop. I haven’t found any consistency to why sometimes it sleeps in lower power modes than other times.

For software, I am currently using 1.3.0-rc.1 and have some super simple test code cobbled together from what I saw others successfully doing:

SYSTEM_MODE(SEMI_AUTOMATIC);                       
SYSTEM_THREAD(ENABLED);

bool disconnectFromParticle() {
  Particle.disconnect();
  waitFor(notConnected, 15000);                                     
  Cellular.off();
  delay(5000);                                                    
  return true;
}

bool notConnected() {
  return !Particle.connected();                             
}

bool Connected() {
  return Particle.connected();                             
}

void setup() {
}

void loop() {

  Cellular.on();
  Particle.connect();
  waitFor(Connected, 15000);
  Particle.publish("state", "open", 7200, PUBLIC);
  delay(5000);

  disconnectFromParticle();
  System.sleep(SLEEP_MODE_DEEP, 0);
}

Any suggestions?

Thanks,
Eric

Hum.

Using the same code to get the Boron LTE into System.Sleep for 1.1mA and it’s working fine.

I haven’t tried testing sleep currents after powering the modem back on after multiple wake ups.

I’m just connecting to sync the time on the first run and then putting the modem sleep and keeping it there and just using System.sleep(); only after that and from what I have seen it goes back into System.sleep() mode with 1.1mA power consumption as expected.

I would try adding 5 more seconds to the Cellular.off(); time and see if that extra time makes a difference in getting it to turn off and drop down to the 0.6mA deep sleep current your looking for.

15 seconds for establishing a cellular and cloud connection may be a bit short and you should check the outcome of waitFor() whether or not you actually were able to establish a connection.

This should only be done when you know you actually got a connection.
TTL is not implemented with Particle.publish() so whether you pass 7200 or 0 there is no difference in outcome.
And do you really neet to publish PUBLICly?
Especially with a common term like state you may get other people's public state events and/or interfere with their subscriptions. Either way it's not considered good style to use common terms for public events.

You should actually not pass a sleep period for deep sleep on Gen3 devices.

1 Like

Sure, but I'm not sure this is related to the issue, as I can tell from the LEDs and the activity in the cloud that my events are being published.

publish() is still non-blocking and doesn't tell me when it has completed, correct?

Regarding the name of the event, I took some liberty to change the names from my actual code, mainly just to not distract from the actual issue at hand. The real name is a bit more specific. I hadn't thought too much about the possibility of global conflicts with event names. It was intentional that the event is public, as this particular application is meant to be open source and replicated, and generate public data that anyone could subscribe to, regardless of whether they have access to the builder's personal particle account. Presumably people using public events do some sort of sanity-checking on subscribed events to ensure that it is of the correct format. In my case, particle devices would only publish data, not subscribe to them, so I don't have to worry about the node having to deal with unexpected events.

Regarding the TTL, I do understand that the current backend ignores it. However, as it is in the API signature, I have matched the intent of how I desire it to be used, and if the backend ever honors it, it will have the expected behavior I desire.

Ok, but it is my understanding that this is simply ignored, correct?

Can you tell me which firmware/OS version you are using? Since I was developing a new app and saw that there was lots of quirkiness with sleep in past versions, I figured I'd go bleeding edge...

I'll try increasing the delay after Cellular.off().

I’m using the latest 1.3.0-RC1.

Experience tells that that's not always the case and we had several instances where people got "ghost" triggers of their subscriptions which were in fact public events issued by others.

For quite some time now Particle.publish() returns a "future bool" which means when you catch the return value as bool it will be blocking, if not, then not.

That's a fair point. I just mentioned it so that you don't get confused if you were to expect the even living for 7200 sec but in fact didn't.

Unfortunately I have no answer to your immediate issue about inconsistent power draw during sleep mode. I'll leave that for @rickkas7 to address :blush:

While I sympathize with people subscribing to public events and getting unexpected events from other people, that sort of goes with the territory of how the API is designed and expected to be used. I suppose you could add something like a concept of using GUIDs to define versioned interfaces for public events to reduce the likelyhood of accidental collisions, but that’s not what exists today. I don’t see any easy way to publish events to third parties without an intermediate broker without using PUBLIC, right?

Oooh, didn’t realize publish() returns an std:future—cool!

Unfortunately none of the changes suggested have helped. I tried increasing the delay between turning off cellular and going to sleep, as well as using the version of sleep without the wakeup parameter passed.

I see at least three distinct power states that my device will randomly settle to on going to DEEP_SLEEP:
-<1mA (occasionally)
-8mA constant (very often)
-8mA but bouncing to 12 on a regular basis (rare)

1 Like

Bump. Anyone have any further thoughts? Any simple test code that consistently demonstrates the ~1mA or below sleep current?

I just updated a test Boron LTE to 1.3.0-rc1 and consistently get 1.39 mA with a Li-Po voltage of 3.97V using the following Code:

SYSTEM_MODE(MANUAL);
SYSTEM_THREAD(ENABLED);

int sleepTime =         1 * 60    ;     // (# Minutes * 60 seconds)
int connectionFail =    5 * 60000 ;     // (# Minutes * 60,000 ms ) During the Connection Process, Boron will "Give-Up" after this amount of time and Sleep if un-successful.

inline void softDelay(uint32_t t) {
  for (uint32_t ms = millis(); millis() - ms < t; Particle.process());  //  safer than a delay()
}

void setup()  {
}

void loop()   {
  softDelay(2000);
  if ( !Particle.connected() ) {
    Cellular.on();
    softDelay(2000);
    Particle.connect();
    softDelay(2000);   //
  }

  //  Limit the time spent for the Connection attempt to preserve Battery
  if (waitFor(Particle.connected, connectionFail)) {    // Will continue once connected, or bail-out after "connectionFail" time-limit
    softDelay(5000);
    Particle.publish("DeBug", "Boron Awake", PRIVATE, NO_ACK);
    softDelay(5000);
  }

  goToSleep();

} // End LOOP()

void goToSleep() {
  Particle.disconnect();
  softDelay(2000);
  Cellular.off();
  softDelay(3000);  // Step through the process safely to ensure the lowest Modem Power.
  System.sleep( {}, {}, (sleepTime) );  //
  softDelay(5000);

}

Stepping through the connection processes individually isn't required (or the softDelay's) ..... but I feel safer doing it that way.

~1.4 mA is higher than my previous firmware version testing, but that was also likely at higher Supply Voltages.

Thanks! I’ll have to give this a try the next time I can pull the system out of deployment.

The few differences I can see from the code I am running are:
-I am using delay() instead of your softDelay() that interleaves process()
-I have a waitForDisconnect method instead of a fixed sleep after disconnect

Curious to see if this helps
Eric

Arg! Still no luck!!

At first I tried integrating the changes to better match yours. On one boot I saw ~1mA, but never could repeat it on subsequent sleep/wake cycles.

Just to prove I’m not doing something dumb, I copied and pasted your code exactly as-is. I compiled it locally and flashed it over USB. Similar result: ~12mA when PC USB also connected, ~8mA if only power is applied (presumably the charge circuitry/LED is consuming the 4mA?).

As far as hardware connections:

  • Benchtop supply powering Li+/GND at 3.97V to match your setup exactly.
  • D6 connected to 3.3V via 10k pull-up resistor (connected to ground if my reed switch is closed). Probably could go with a higher value resistor to limit consumption in the closed case, but it was what I had handy, and shouldn’t consume anywhere close to the ~10+mA delta. In any case, even before I had this sensor/pull-up connected, I saw the same issues. I’d try removing this completely, but to reach the small size I simply soldered the pull-up directly.

Interestingly, I noticed that with your code, when waking up each sleep cycle, the LED briefly goes white, and for that second or two, I do reach the mythical 1mA current draw. Just not when it is sleeping!

Any more ideas? Any chance my Boron is a newer silicon rev or has different modem firmware than yours? It is only a few weeks old from Amazon.

Thanks,
Eric

Think I may have found the solution!

Increasing the softDelay() times in your goToSleep() fixed it:

void goToSleep() {
fuelGauge.sleep();
Particle.disconnect();
softDelay(5000);
Cellular.off();
softDelay(5000); // Step through the process safely to ensure the lowest Modem Power.
System.sleep(doorSensor, CHANGE, 7200);
softDelay(5000);
}

Magic…

2 Likes

Congrats !
So is my Boron smarter than yours......or vice-versa ? J/K.

Maybe the Cellular Connection Strength has an impact on the required softDelay() times?

We might improve on this strategy by using waitFor(..., timeout) for the
Particle.disconnect() & Cellular.off() commands to complete, instead of the hard coded 5 seconds each ?

Hi there,
So how would one write the line to put the boron to deep sleep based on time? is there an alternative you’d recommend?
Will the classic code work on my boron just like it does on my electron anyways?

Could you elaborate on the use case you have in mind?
However, timed wake from deep sleep on a Boron is out of question as the timers won't be running in that state. Hence the common way around this is to use an external RTC.

on my electron, I use sleep deep with a timer for an hour when battery power is very low. I recently got a boron on which I wanted to do the same. I was reading the Boron docs on the sleep, and couldn’t decide on whether the ultra_low_power or the hibernate mode would be an appropriate equivalent to sleep deep for me to use the timer just the same way as in the electron.

The Electron features a proper RTC which keeps running even when in deep sleep.
The Boron has not - AFAICT neither in ultra-low-power nor in hibernate mode.
Only in stop-mode the timers keep counting on Gen3 devices.

1 Like

That’s a bummer, I guess I’ll use the stop mode then but with the network off and hopefully obtain the low sleep power consumption I am looking for. Thank you very much for your time.

@grimm ,

You can use ULTRA_LOW_POWER which is has lower power consumption than STOP mode and can do time-based wakeup on the Boron.

Two lines of code to sleep:

config.mode(SystemSleepMode::ULTRA_LOW_POWER)
      .gpio(userSwitch,CHANGE)
      .gpio(intPin,RISING)
      .duration(wakeInSeconds * 1000);
SystemSleepResult result = System.sleep(config);                   // Put the device to sleep

Then, you can see what woke the device with lines like this:

if (result.wakeupPin() == intPin) {                                // Executions starts here after sleep - time or sensor interrupt?
  stayAwakeTimeStamp = millis();
}
else if (result.wakeupPin() == userSwitch) setLowPowerMode("0");

Hope this helps

Chip

3 Likes