I have started working with a new device: a newly purchased Adafruit Feather M0 WiFi (with headers, assembled!) which I got for the scandelously low price of $9.95.
I wanted another WiFi-capable device (that wasn't another ESP8266) to experiment with, and one that boasts incredibly low power consumption is just a bonus.
Previously, I had been working with a Wemos D1 Mini (an 8266-based MCU) and having a grand time with that. I managed to get OTA (Over-the-Air) updates working, integration with Adafruit IO and many other things. This board boasts WiFi integration with the ATWINC1500 from Atmel.
The first experiment for this board is to hook it up with the INA260, a breakout board from Adafruit that supports high- or low-side voltage, current, and power sensing. The initial goal is to be able to (breadboard-only) prototype a remote voltage-sensing device that can drive itself from the battery it is sensing (automotive/motorcycle/etc.). Therefore, it must be capable of very low power consumption. Why did I use an INA260 when I could just as easily use a voltage divider? I wanted to explore the benefit of using this as a "pass-through" device where I hook it up to the battery (and optionally also a charger) via the SAE 2-pin connectors that are very common in battery chargers and powersports.
These are the use cases I'm thinking of:
Regular access to WiFi, voltage only-sensing The intent would be to use this to periodically sense and emit the voltage of a vehicle that is parked near enough to WiFi.
Infreqeuent access to WiFi, but same as number 1. This would require persistent storage and additional logic to synchronize storage with something available over WiFi (a cloud service, a home server, a laptop, whatever).
Bonus points for:
In all cases, very low power consumption. I'd like to see an average power consumption under 20mA. Less is better.
I thought I'd start by hooking up the INA260. It's super easy to hook up thanks to Stemma-QT and it took less than a few minutes. I was up and running in under 10 minutes, code and all.
The next step was WiFi. Unlike the Wemos D1 mini, the WiFi for this board uses the "WiFi101" library,
so there was some learning for me. I don't understand how it works without a hook into the loop() function,
but it seems to work fine. After some initial stumbles, that works working fine as well.
Awesome.
Next Step: since I had experience with Adafruit IO, I thought I'd try Arduino Cloud. Arduino Cloud feels more advanced and considerably more powerful, but I really struggled with making it work. After an hour or two, I more or less gave up (temporarily!) and tried to add Adafruit IO. That also could have gone better, but overall it was fine.
Eventually, however, I wanted to focus on power consumption, so I altered the direction I was taking entirely.
I grabbed an Arduino Uno and 'borrowed' the INA260 (modifying the code that the Feather M0 WiFi runs to ignore the fact that the INA260 is missing) and wrote some code to very quickly grab and plot the voltage/power/current. It maintains an average current and power consumption since startup, emitting values once every 2 seconds but acquiring them as fast as possible.
Then I grabbed the MPM3610 5V Buck Converter Breakout - 21V In 5V Out at 1.2A I also purchased, soldered the headers on quick, and prepped it to power the Feather.
To do so, I 'modified' a micro-USB cable to plug into the Feather (per Adafruit's recommendation to NOT power the Feather with anything but USB or LiPo batteries) so that the 5V output of the buck converter can feed the Feather over USB.
Then I hooked things up through the INA260 so that the Arduino Uno could observe the power consumption of the Feather and it's components.
The Feather's code basically did this:
This is what I learned.
The Feather M0 can be very low power with WiFi turned off; hard to tell, but perhaps under 1mA!
I had a bunch of trouble turning the WiFi on and off, with no packets going out, etc... Some of thishttps://github.com/arduino-libraries/WiFi101/blob/master/docs/api.md#wifilowpowermode
was due to mistakes I made with the code. I have configured the WiFi with "max" power savings
(WiFi.maxLowPowerMode();) ref.
I experimented with variations on:
delay(1000);)delay(10000);)LowPower.idle and .sleep and .deepSleep with and without argumentsAs noted above, the 5V buck converter itself consumes about 8 mA. NOTE! Take these results with a grain of salt. Minor timing issues that might arise out of precisely in which order or how quickly I reset each device can make a big difference. For example, if I reset the Uno first and then the Feather, the Uno will catch initial startup and WiFi stuff which can be much more expensive. That's why the tests run for (exactly) 15 minutes. Now that I think about it, I should probably have had the Uno delay like... 10 seconds so that it catches the Feather after it's gone to sleep for the first time, but this is probably fine. The difference between 14 activations of WiFi and 15 activations of WiFi might be a very brief spike of 100 mA.
All tests were run for exactly 15 minutes. Current and Power are both averages.
The buck converter by itself appears to consume about 8mA.
| Delay (ms) | Current | Power | Difference (from baseline) |
|---|---|---|---|
| 0 | 26.51 mA | 242.33 mW | n/a |
| 1000 | 25.49 mA | 234.69 mW | -3% |
| 10000 | 25.74 mA | 236.77 mW | -2% |
| Function | Current | Power | Difference |
|---|---|---|---|
LowPower.idle() |
19.63 mA | 180.39 mW | -26% |
LowPower.sleep() |
n/a | n/a | n/a |
LowPower.deepSleep() |
n/a | n/a | n/a |
NOTE
LowPower.sleep() call appears to kill the WiFi.
LowPower.deepSleep() because I assume it would as well.¶Before we begin, a note: these tests (power, wifi, etc.) took several days; 15 minutes here, an hour there, etc. When I started writing this section it was a new day, and I ran into problems.
I disabled all of the neat stuff I got working last night (MQTT publishing to a local broker, Over-the-Air updates, use of a Voltage Divider, etc.) and ran another baseline that that I mistakenly believed would have parity with earlier results.
I was wrong. I was seeing a baseline of ~30mA (20-50% more than I expected).
After much wracking of my brain, I realized one thing: I upgraded the firmware from 19.5.2
(I think) to 19.6.1. (The latest is 19.7.6 but that's not available through the Arduino
WiFi Firmware Updater Thinger). Unless I can think of something else, that's the new baseline, I guess.
I have verified I'm using max power savings with the WiFi, LowPower.idle(), and the only networking
I'm doing is a single UDP packet every 60 seconds (confirmed with tcpdump).
I then tried a different Adafruit Feather M0 WiFi, one that had not had the firmware update. I confirmed the firmware's version was 19.5.2 - no change. I can't figure out what I did. I never did!
On to the testing:
There are three modes (reference to docs) for WiFi Power:
Up to this point, all tests have been run with WiFi.maxLowPowerMode().
I will be running a "ping" to the device IP during this test, so power consumption
levels will likely be a bit higher. I'm using ping -c 900. I start the ping a few seconds
after I see the WiFi light go on. ping times are in milliseconds.
Let's see what impacts these three modes have:
These will be run with LowPower.idle() as the idle mechanism.
Again, the baseline here with no "ping" is now ~28.29mA
| Method | Current (avg) | Ping Results (packet loss, rtt min/avg/max/mdev) |
|---|---|---|
WiFi.maxLowPowerMode() |
29.86 mA | 0.21%, 10.172/267.761/615.621/148.913 |
WiFi.lowPowerMode() |
29.65 mA | 0%, 10.430/267.516/603.632/151.045 |
WiFi.noLowPowerMode() |
61.44 mA | 0.22%, 2.102/3.037/20.302/1.597 |
What have we learned?
My initial attempts here left me without WiFi after the first packet was sent.
Eventually I realized I was doing it wrong (I was forgetting to reinit the WiFiUDP instance
with Udp.begin()).
This one is easy:
WiFi.end()WiFi.lowPowerMode() and a LowPower.idle()
(which doesn't seem to be working any more; not sure what's up with that).The results: 13.13 mA Wow. Take this with a grain of salt, obviously, since my use-case and test are rather contrived, but still. I did confirm the UDP packet (one-way chuck-it-over-the-wall approach) does make it.
That's enough of this for now.