Trying to wrap my head around low power best practices


#1

I’ve got a handful of DK’s up and running and I’m trying to figure out what to do with them. I have a couple of Photons constantly monitoring my washing machine and my garage door and I suspect I’ll come up with some similar projects for the DK’s (checking a value every X seconds)

I went through the Bluz getting started and the pattern suggested there makes sense

void setup() {
    //setup code
}
void loop() {
    //this, and only this...
    System.sleep(SLEEP_MODE_CPU);
}

But I am curious what the best practice is for maintaining low power while still polling something regularly, like temperature for example?

I poked around the particle docs a bit and Software Timers seems promising. https://docs.particle.io/reference/firmware/photon/#software-timers

Is that the best way to do it?


#2

Software timers will indeed be best practice once we have them implemented. We couldn’t get that done in the first release, but it is planned for the next.

In the meantime, you can just do normal checks for time in the loop. The loop function will be run every 100mSec even when you use CPU sleep mode as you show in the example. We have a timer that wakes up the CPU every 100mSec to check on system status and the RGB LED and such. When that happens, loop() will get run and you can easily add checks in there for time without affecting battery life. It is a little different then the deep sleep mode on the Photon.

So adding a check in the loop() function to see if some interval of one has passed, and the acting on it, is perfectly fine. Really, the worst thing you could do in loop() is to use delay() as that just puts the CPU in spin mode, eating up power.

It’s a very good question and the documentation doesn’t explain this well enough I think, I will try and adjust it better.


#3

Thanks for the reply @eric. That makes sense.


#4

Not to beat a dead horse, but the clarification of the 100mSec aspect of loop() is huge from my perspective. I was scratching my head for the longest time and reading docs over and over trying to figure out how to write code with the assumption the sleep mode was like the Particle’s deep sleep mode. I strongly suggest you update the docs quickly to clarify this. I was avoiding making a purchase until I could understand this most basic concept. Perhaps I’m just a bit dull?


#5

I completely agree, the documentation needs to be much clearer around this point. I am moving this up on my list of to-do’s and will get it done quickly.


#6

@ctmorrison I updated the documentation to try and clarify this all better. I would really appreciate it if you could take a look and let me know if this makes things clearer for you. It is definitely one of the bigger differences between using bluz and the Photon, so I want to make it clear without making it too confusing for new users.

You can see the new section on the intro page here: http://docs.bluz.io/#one-more-thing

And also the System page of the Reference section was changed here: http://docs.bluz.io/reference/system/#systemsleep

Let me know what you think. Thanks!


#7

Great tweak! To me, this definitely clears things up for all readers and I’m about to order a starter kit! Thanks!


#8

That’s a fantastic improvement to the docs and I’m sure it will save many hours of confusion for new Bluz owners.


#9

So…my last CR2032 batteries lasted a whopping week. I’m going to have to get a night job to pay for batteries!

I must be doing something wrong, so I’m posting my code (a hack in work) to see if someone can point out what I should or should not be doing. The code all seems to work as expected, but I’m certainly hoping to get way more than a month out of these batteries.

In the meantime, I’ve switched to using 2 AA batteries, but have little hope they’ll last more than 10 weeks, since they only have 10 times the capacity of the CR2032’s.

int sensor = D1; //this is where we connect the input sensor
int powerOut = D0; //applies 3.3 v to sensor
int lastState = LOW; //last known state of the sensor
int inState; //sensor state as of most recent reading
double lastCheck = 0; //last time the sensor was read.  When starting, we assume it's closed
double now;
double checkPeriod = 5000;//polling period
double gsPeriod = 600000;//push to GS at least once every 5 minutes
double lastGSupdate = 0;//last time data was pushed to GS
char gPublishString[200]; //holds string to be sent to webhook
//char gDeviceID[25] = ""; //holds the unique device ID
char gGSAPIkey[50] = "xxxxxxxx"; //holds the Grovestreams PUT API key
char gComponentID[20] = "xxxx"; //holds the Grovestreams component ID
char gStream[] = "xxxi";
boolean gsUpdated = false; //indicates if we've ever successfully updated Grovestreams or need to do an update

void setup () {
  pinMode(sensor, INPUT_PULLDOWN);
  pinMode(powerOut, OUTPUT);
  digitalWrite(powerOut,HIGH);
  inState = digitalRead(sensor);
  digitalWrite(powerOut, LOW);
  Particle.variable("state",lastState);
}

void loop () {
  System.sleep(SLEEP_MODE_CPU);
  now = millis();
  if (now < lastCheck) {//handle the rollover
    lastCheck = 0;
  }
  if ((now-lastCheck)>checkPeriod) {
    lastCheck = now;
    digitalWrite(powerOut, HIGH);
    inState = digitalRead(sensor);
    digitalWrite(powerOut, LOW);
    if ((inState != lastState) || !gsUpdated) {
      delay(50);
      digitalWrite(powerOut, HIGH);
      inState = digitalRead(sensor);
      digitalWrite(powerOut, LOW);
      if ((inState != lastState) || !gsUpdated){
        lastState = inState;
        updateGS();
      }
    }
  }
  if (now<lastGSupdate) { //handle the rollover
    lastGSupdate = 0;
  }
  if ((now-lastGSupdate)>gsPeriod) {
    lastGSupdate = now;
    gsUpdated = false; // this will force an update in the 5 second cycle
  }
}

void updateGS() {
  sprintf(gPublishString,"{\"gGSAPIkey\":\"%s\",\"gCID\":\"%s\",\"gStream\":\"%s\",\"state\":\"%i\"}",gGSAPIkey,gComponentID,gStream,lastState);
  if(Particle.connected()){
    if (!RGB.controlled()) {
        RGB.control(true);
        RGB.color(0,0,0);
     }
    Particle.publish("bluzSingleDI",gPublishString,60,PRIVATE);
    gsUpdated = true;
    if (lastState==1){
      Particle.publish("right-door-closed",NULL, 60,PRIVATE);
    }
    else {
      Particle.publish("right-door-open",NULL,60,PRIVATE);
    }
  }
}

#10

Should I use RGB.brightness(0) to shut off the RGB LED or RGB.color(0,0,0)? Or are they the same? I’m wondering if this is what could be causing short battery life?


#11

That delay(50) could be your problem. Every 5 seconds the processor is running full bore for 50ms when it could be sleeping. Could you setup a bit of a state machine and wait for the next time loop() is called to run that logic. It would mean a delay of 100ms instead of 50ms but it would make your code more efficient.

EDIT: I just studied the code a bit more and I see it should only do the delay when the state of your sensor changes. Is the door opening/closing a lot?

EDIT 2: If Particle.Connected() ever is false for a long time, it is going to call that delay every 5s because gsUpdated never gets set to true. Could you be disconnected from particle for long stretches of time?


#12

Thanks for the feedback. As you determined, the status is checked every 5 seconds but the 50 msec only comes into play if the status has changed. The door is not opening/closing that often–a few times a day.

I’ll look some more at the Particle.connected() situation. The system seems to run very deterministically until the batteries die. For giggles, I may put a counter next to the 50 msec delay just to see how often it is being called. I’ll publish that variable.

The way I force the Grovestreams periodic update was a bit of a last minute thought and I certainly need to clean that up a bit.

Again, thanks for your feedback!


#13

I don’t see anything glaringly wrong with that code. How many batteries have you tried? One week is very low, I know we have seen problems in the past with counterfeit and half-drained batteries (I once got a pack that was dead in the packaging), so I am not sure if that is playing an issue here.

Once we get these gateways out the door, my focus will switch fully back to 1.2 and adding some power saving options to it. If you are using a gateway/gateway shield and can get it relatively close, we can turn down the transmit power and turn up the connection interval, that would get you some significant increases.


#14

The four batteries I’ve gone through (2 for each of my 2 BluzDK’s) were new Duracell with dates well into the future. Last night, I switched to using 2 AA batteries for each, hoping this would eliminate the battery issue for now. However, the option of running via coin cells is very appealing, especially if I could get the battery shield sans header to facilitate very small packaging. Actually, I’d like to see the battery shield supplied with the headers, but not soldered to the shield.

I am using a gateway shield and believe the code is the most recent (via another topic). The shield is about 25’ away from the two BluzDK’s. If I have to get it closer, then I might as well switch back to Photon’s and power adapter. I am hoping for some lawn/flower management applications that decent battery life and distance are not mutually exclusive.

I have tweaked the code a bit based upon @pnoyes feedback, reducing the 50 msec to 25 and getting the periodic GS publish separated out from that loop.

I have high hopes for BluzDK’s if I can get past the battery life issue while not sacrificing range.


#15

I maintain that real world power consumption over time is not where we expect it to be, even when we understand and factor in the constraints of the current firmware.

I have not had time to spend on this (too busy with $DAYJOB and $FAMILY) - but am hopeful that I’ll be able to revisit this soon.

So I believe you @ctmorrison, your experience is similar to mine - and I’m confident that we can get to the bottom of this and realize the full low power potential of the bluz hardware and the particle platform.


#16

My hope is that I can utilize the BluzDK platform in lieu of digging into Z-wave or Zigbee for battery-powered applications that require a range of ~100’ and coin cell (or dual AA/AAA) battery life of at least a year. Is this realistic in the not-too-distant future or am I being quite naive?


#17

No - I’m pretty sure we can get there, that is the entire reason for this product.
I’m not bluz, just another user like you - I think they’ve been buried in logistics getting stuff out the door, and the ensuing support overhead - I’m hoping they can get back into fleshing out features RSN.


#18

Hi, was this finally the right way to turn off the RGB led?
(maybe @eric would know also)
thanks!
Gustavo.


#19

Yes, that will turn it off.