Close

Low Power - I Stuffed Up!

A project log for Bin Monitored

Has your bin been blown over in the wind? Did the delivery person leave a parcel in it? These and more questions answered with Bin Monitored

stephen-harrisonStephen Harrison 10/01/2018 at 02:220 Comments

The ESP8266 features a very nice deep sleep mode where the entire module sleeps, consuming only nano-amps until woken, either by the on-board RTC or some external factor, this is ideal for Bin Monitored, it could sleep all day long, waking only occasionally to send an "Im still alive" signal and how full the bin is, or be woken by the accelerometer if something interesting happens.

This was my first time trying to use the ESP12 in low power mode and I got a few things wrong. Quite a few things...

I connect GPIO16 to the reset pin to allow the RTC to wake the ESP when in deep sleep mode as per the datasheet, this works well....

So I figured I'd do the same with the accelerometer. I could program INT2 to go low and reset the ESP, so waking it when an interesting event occurred...

Yep, you guessed the obvious problem, the MMA8452 latches the interrupt output, it's not pulsed and is only cleared when you read the appropriate register, so if INT2 ever gets asserted it will keep the ESP in a reset state. Not so great. 

I had also planned to route this via a 0R resistor link so I could remove it if my plan failed. I forgot that! Fortunately as long as the firmware never configures INT2 it doesn't affect the processor, but I'm only ever one bad firmware deployment away from making a brick, one square inch in size :-)

Going forward, I can either modify the circuit so the asserted interrupt causes only a pulse on the reset line, or try and use the enable pin (pin 3 - CH_PD) on the ESP to wake the device.

Mostly Sleeping...

However for now I have no way to wake the ESP from deep sleep when an interesting even occurs, so instead I sleep the ESP for a short time (1 minute in this case), when it wakes the startup routine checks the ACCEL_INT pin (GPIO13), if it is asserted then the accelerometer has registered an event (lid opened) and further investigation is needed.

The accelerometer is configured so that an interrupt is generated if motion is detected in the Y axis. When the lid is opened the accelerometer is orientated such that acceleration due to gravity is through the Y axis (it's normally through the Z giving -1G). This "motion" caused by gravity is enough to tell the lid has been opened. Additionally the sample rate of the accelerometer is slowed down to save on power.

Detecting if the bin has fallen over is a little more tricky so I decided to do this detection only on occasional intervals and keep the interrupt strictly for the lid being opened.

The initial part of setup code for the ESP is below. It checks to see if the accelerometer has interrupted, then it checks the time to see if the bin level / environment should be checked and then updates the fake RTC to track time. If no action is required the ESP goes back into a deep sleep as fast as possible. This takes a few hundred milliseconds and still contains (as you can see) a lot of debugging code!

void setup() {   
  pinMode(ACCEL_INT, INPUT_PULLUP);
    
  pinMode(BIN_DAY_LED, OUTPUT);
  digitalWrite(BIN_DAY_LED, LOW);

  pinMode(STATUS_LED, OUTPUT);
  digitalWrite(STATUS_LED, LOW);

  // TODO: Only use serial during debugging.
  Serial.begin(115200);
  Serial.println("");
  Serial.println("====================================================");
  
  bool shouldSleep = true;

  Wire.setClock(400000);
  Wire.begin();

  // Read the configuration early.
  readConfiguration();
  
  // Check accelerometer interrupt to see
  // if something interesting has happened.
  if (hasAccelerometerInterrupt()) {
    // Handle accelerometer event...
    // most likely lid opened.
    // Motion setting lost when accelerometer is initialised.
    shouldSleep = false;
    lidOpenedCount++;
    // Cap at 100 to keep the number within a byte
    // so we don't overflow to 0.
    if (lidOpenedCount>100) {
      lidOpenedCount = 100;
    }
    // Only connect/send on the first open.
    shouldConnect = lidOpenedCount == 1;
    lidOpened = true;
  } else {
    // Lid closed, reset the counter.
    lidOpenedCount = 0;
  }

  // read the RTC value from EEPROM, update it based on wake interval
  // then write it back to keep track of time in a really
  // not very good way...
  readRtc();
  updateRtc();
  writeRtc();

  // Check to see if the bin status should be checked.
  // this happens on a periodic interval.
  if (shouldCheckBin()) {
    shouldSleep = false;
  }

  // Check if the bin status should be sent
  // again, this happens periodically.
  if (shouldUploadStatus()) {
    shouldSleep = false;
    shouldConnect = true;
  }

  // If nothing to do put the micro back to sleep 
  // as soon as possible to 
  if (shouldSleep) {
    Serial.println("Nothing to do. Sleeping.");
    deepSleep();
    // ESP will be reset to wake up so nothing after this happens.
  }

The ESP is woken from deep sleep once per minute for two reasons, the first is that Bin Monitored features an LED to indicate if it is bin collection day (In Cambridge different bins are collected weekly, one week the black bin, the next the blue and green bins). So to make the LED reasonably responsive (if you can call 1 minute delay responsive!), the ESP is woken every minute to detect of the lid is opened and notify me if it is bin day. As it turns out the enclosure obscures the LED so it's of little use.

The second reason is to keep an on-board fake RTC running, every time the ESP is woken an on-board counter is incremented and stored in EEPROM to track time. This is used to determine if the ESP should check the bin contents and to track if the day is bin day.

To try and avoid wearing out the EEPROM I've used a range of addresses for hour and minute storage based on the day/hour and minute, rather unusually this RTC uses a day value of 0 for bin day, and then counts only to the bin collection interval, in this case 14, when it resets back to 0. This saves tricky days per month calculation and tracks bin collection days rather than day of the month.

WiFi connectivity is kept to a minimum, I've used MQTT as transport connecting to the Tinamous MQTT server to reduce the overhead at the transport layer, perhaps the biggest gain in reducing the WiFi time was when I improved my home WiFi access point.

The blue series is time for the WiFi to connect, orange is RSSI. You can clearly see when I replaced my WiFi access point, connection time is typically 250ms now (which is currently the minimum time the device can connect in due to a 250ms delay in my code!).

Battery life is still an issue, despite the cheap CR123A's claiming 2200mAH, they (oddly enough) only store about the same as the expensive CR123As, about 650mAH, which in sleep mode should be enough for over 3000 hours, or about 4 months. Ideally I would have liked years rather than months, but for now the sleep power consumption is to high. 

The chart below shows the battery voltage (approx) Blue Bin Bin Monitored, it had the battery replaced on the 22 September, so far it's made 9 days so far, however it is sending data every 10 minutes rather than every day which doesn't help!

Deep sleep power consumption is about 10x what I had aimed for. More work to be done!...

Discussions