Close

Turbines to speed!

A project log for RemuterMCS

Remote control for muting and unmuting the microphone, camera, and speakers

wjcarpenterWJCarpenter 03/14/2021 at 18:240 Comments

I wanted to find out how long the M5StickC can run on its battery under various conditions. That would give me some idea of the usability of RemotMCS as a ... remote. How to measure?

The simplest thing I thought of was just having it "Serial.println()" something every once in a while. The Arduino IDE serial console would timestamp them so I could know when the last one came in. The problem with that is that the M5StickC charges its battery and runs off USB power when it's plugged in. I can predict in advance that the answer would be something like infinity.

What about doing the equivalent over Bluetooth? Paired to a PC, I could just leave an editor window open and have the M5StickC "type" some time information periodically. RemuterMCS doesn't know time of day, but it does know the amount of time since the ESP32 startup. This scheme would probably work, but it has a couple of drawbacks. My primary concern is that I don't know how much the Bluetooth interaction would affect power consumption, so I wasn't sure I could get a baseline of "while doing nothing". Second, I know from past experience that I have a good shot at goofing up the recording now and then. If the battery life is 4-5 hours, then it's an overnight wasted on a bungled test. Finally, I have a preference for something that is simpler for someone else to replicate so they could perform the measurements on their own specific M5StickC devices. In fact, I have a few of these and might want to measure them all to see if there is variation in available power.

The solution that I adopted was to periodically store timing information on the M5StickC itself. On start-up, I read it back out and report it. I report it with "Serial.println()" and, in the early stages of the code, on the device display. I don't know if I'll show it on the device display when the UI is complete.

With true Arduinos (without SD cards or other storage devices), that's done using a mechanism called PROGMEM. On an ESP32, PROGMEM is a no-op. Instead, the ESP32 Arduino libraries provide a mechanism called "Preferences", which is a straightforward key-value store. It operates on some part of the device's flash memory. I didn't chase down the details of the flash partition scheme and where the preferences values live; I just tried it and it worked as advertised.

Now, of course, I don't know how much power it takes for the ESP32 to write those values to flash via the Preferences API. I'm just hoping it's not much. I write the time since startup every 10 minutes. A 10 minute (rounded down) granularity for battery life should be fine unless I get into some really detailed fine-tuning, which I don't expect to do. It also means I have 10 minutes of false starts and shenanigans before the value gets overwritten after a device restart.

The mechanism is enabled with a single define. I won't quote all of the code here, but this is the gist (snapshot in time; check the git repo in case it changes, which it surely will).

#define LONGEVITY 1

#if LONGEVITY
#include <Preferences.h>
#endif

#if LONGEVITY
const char *key_naptime = "naptime";
const char *key_longevity = "longevity";
unsigned long lastSaveMillis = 0;
unsigned long saveEveryMillis = 600000;
unsigned long storedLongevity = 0;
unsigned long storedNaptime = 0;
Preferences preferences;
#endif

void setup() {
// ...
#if LONGEVITY
  preferences.begin("RemuterMCS");
  storedLongevity = preferences.getULong64(key_longevity);
  storedNaptime = preferences.getULong64(key_naptime);
  preferences.end();
  Serial.printf("Stored longevity = %ld, naptime micros = %ld\n", storedLongevity, storedNaptime);
#endif
// ...

void loop() {
// ...
#if LONGEVITY
  unsigned int now = millis();
  if ((now - lastSaveMillis) > saveEveryMillis) {
    lastSaveMillis = now;
    preferences.begin("RemuterMCS");
    preferences.putULong64(key_longevity, now);
    preferences.putULong64(key_naptime, napDurationMillis);
    preferences.end();
  }
#endif
// ...
}

The "naptime" referenced in the code is because I anticipate doing some kind of low power sleep in most iterations of the loop to conserve power. But that's a topic of its own.

Discussions