Open kjm1102 opened 3 years ago
Hi @kjm1102 thanks for having a look at the code.
I've had a quick look at this, I couldn't find anything for micopython around this after an initial look ( for waking from the real time clock, see the last part of this post for GPIO wakeup), that's not to say it hasn't been implemented. I did stumble upon this however:
https://github.com/adafruit/circuitpython/pull/4816/files
It appears that someone has done the work to get deep sleeping working for circuit python. It's fairly similar to micropython in that you just upload a uf2 file to your pico and then drop python files onto it, which it will then run for you.
Some other links around this are: https://circuitpython.readthedocs.io/en/latest/shared-bindings/alarm/index.html#alarm.exit_and_deep_sleep_until_alarms
I tried with this code:
import alarm
import board
import time
import digitalio
print("Waking up")
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
# Flash the led
led.value = True
time.sleep(2)
led.value = False
# Set an alarm for 60 seconds from now.
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 60)
print("Sleeping")
# Deep sleep until the alarm goes off. Then restart the program.
alarm.exit_and_deep_sleep_until_alarms(time_alarm)
You can then upload this as code.py
onto the pico board and it will run it. At least for me it seems to work. However I have not measured the power consumption. Certainly from a brief look at the PR above they are using dormant mode to sleep when no alarm is set (wake from GPIO), and a light sleep mode otherwise. With the example above I expect it should use around 1.3mA ("light" sleep mode).
Unfortunately there is no main loop here, so each time the program restarts. However if you want to accomplish some work and then sleep, after which the program wakes up again from the beginning, this is certainly a way to do that.
I also found this https://github.com/tomjorquera/pico-micropython-lowpower-workaround
Which appears to be a way for the pico to sleep and be woken from a GPIO pin using micropython. Very similar to this post https://ghubcoder.github.io/posts/waking-the-pico-external-trigger/
So by using an external RTC you could wake the pico by listening for a pin to go high. I haven't tested this however.
@kjm1102
I've had a look at recompiling micropython to support this, please see here
Thanks for this. I've been trying to follow up your Oct 18th email with the circuit python example but Windows 10 is giving me grief (refusing to recognise the pico when I plug it in with the boot button held down, reckons the USB device has malfunctioned). I've tried picos from different manufacturers & different USB cables & ports but no joy so must be a Windows 10 problem I'm guessing. Haven't been able to find a fix online.
On Thu, 28 Oct 2021 at 05:42, ghubcoder @.***> wrote:
@kjm1102 https://github.com/kjm1102
I've had a look at recompiling micropython to support this, please see here https://ghubcoder.github.io/posts/deep-sleeping-the-pico-micropython/
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ghubcoder/PicoSleepDemo/issues/2#issuecomment-953210201, or unsubscribe https://github.com/notifications/unsubscribe-auth/AV562M53F524MG5JYFY2GZ3UJBI4HANCNFSM5FNN3XPQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
Nothing like seeing someone with talent (such as your good self) make a move on a topic to get a plodder (like me) motivated. Found a Windows machine that's happy to talk to the pico, results below. 1) Ran your circuit python example on a Rpi pico board with adafruit-circuitpython-raspberry_pi_pico-en_GB-7.0.0.uf2 via Thonny with the circuit python interpreter selected. Program blinked the led but the current stayed at 21mA, couldn't get your 7mA sleep current. 2) Ran import picosleep; picosleep.seconds(60) with your fork via Thonny with the Micropython (Rpi pico) interpreter. What a joy! 1.3mA sleep current that rises to 18mA when it wakes up. Intrigued by the lower idle current now (pico used to idle at 21mA). A brilliant effort! It's rare for github illuminati to reply to an email let alone act so spectacularly on a suggestion. I'm genuinely awed.
On Thu, 28 Oct 2021 at 05:42, ghubcoder @.***> wrote:
@kjm1102 https://github.com/kjm1102
I've had a look at recompiling micropython to support this, please see here https://ghubcoder.github.io/posts/deep-sleeping-the-pico-micropython/
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ghubcoder/PicoSleepDemo/issues/2#issuecomment-953210201, or unsubscribe https://github.com/notifications/unsubscribe-auth/AV562M53F524MG5JYFY2GZ3UJBI4HANCNFSM5FNN3XPQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
I can't get this to cycle chief. Initially, when the repl prompt failed to return after the deepsleep, I figured it was just Windows failing to rediscover the device port after pico wakeup. So I tried running
import picosleep, time from machine import Pin; led=Pin(25, Pin.OUT) print('new cycle'); led(1); time.sleep(1) t=15; print ('dormant sleep(s)', t); led(0); picosleep.seconds(t)
as main.py stored on the pico, expecting it to flash the led every quarter minute or so but its not happening for me. I can see the pico deepsleep then wake up on the USB ammeter but main.py doesn't restart. Any thoughts how to move picosleep beyond being a one shot?
On Thu, 28 Oct 2021 at 05:42, ghubcoder @.***> wrote:
@kjm1102 https://github.com/kjm1102
I've had a look at recompiling micropython to support this, please see here https://ghubcoder.github.io/posts/deep-sleeping-the-pico-micropython/
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ghubcoder/PicoSleepDemo/issues/2#issuecomment-953210201, or unsubscribe https://github.com/notifications/unsubscribe-auth/AV562M53F524MG5JYFY2GZ3UJBI4HANCNFSM5FNN3XPQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
I normally code python on an esp32 in Atom. After a machine.deepsleep() the esp32 wakes up, automatically reconnects to the repl & runs main.py again. On re-reading your 'Using picosleep from micropython' I see now dormant is different, I need to put it in a loop where execution resumes at the line after the picosleep. All good. Do you have an understanding of why the repl doesn't reconnect? It's no biggy I can just use a normal time.sleep during code development & change it to picosleep before deployment, just curious?
On Thu, 28 Oct 2021 at 05:42, ghubcoder @.***> wrote:
@kjm1102 https://github.com/kjm1102
I've had a look at recompiling micropython to support this, please see here https://ghubcoder.github.io/posts/deep-sleeping-the-pico-micropython/
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ghubcoder/PicoSleepDemo/issues/2#issuecomment-953210201, or unsubscribe https://github.com/notifications/unsubscribe-auth/AV562M53F524MG5JYFY2GZ3UJBI4HANCNFSM5FNN3XPQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
Hi @kjm1102 glad you got it working!
Yep as you found with this you need to put it inside a loop. machine.deepsleep()
as the docs state must reset the device after sleeping, causing it to then restart and once again running your main.py
file. With this you have more control of that main event loop. Perhaps though, it should be written to behave in the same way.
There might be some consequences with regards to the python environment on the pico that manifest after deep sleeping and waking up. I've only tested it with simple scripts up until this point though and it seems to work ok.
With regards to the repl, whilst the machine is deep sleeping the repl prompt won't respond, another downside of how this works, but it does show the device is indeed in a deep sleep state.
If I run this script however as main.py
:
from machine import Pin
import picosleep
import time
# I can connect via the repl from this point
led = Pin(25, Pin.OUT)
led.toggle()
time.sleep(5)
led.toggle()
# until this point.
# Cannot connect from this point
picosleep.seconds(5)
# Once this point is reached, the script exits, the pico does not
# restart and I can connect to the repl once more.
I can connect at the points described above. Also it's interesting to note that if I connect during the first time.sleep(5)
, (with Thonny I should add, with the stop/restart button), this will stop the script from executing preventing it from going into the deepsleep. It's useful to put that step at the start of you program to allow you to connect to your device without it getting into a loop where it's unreachable.
If it does get into that state, there is a factory reset uf2 file you can download onto the pico by holding the bootsel button at power on. File is at the bottom of this page.
Interesting that you seen different current draw with circuitpython. Might need to give that another test.
Happy coding! 🙂
One advantage of picosleep in a loop is we loose the reboot overhead. With a 50k micropython file on the ESP32 it takes 5-6s for the program to run again after a machine.deepsleep, a significant penalty when the program execution (read a few sensors, upload to a server) is only 10-15s long. This idea of going dormant then picking up where we left off in the loop should eliminate that reboot time each cycle.
I still don't understand why atom/thonny repl doesn't resume displaying print statements once the dormancy is finished?
Hi and thanks for creating this code. I ran it to execute calls to a Cricket Wifi module and it certainly slashes the power consumption on the Pico. I did find though that if the timer interval is set to values above 1024 the picosleep call seems to lose the expected interval and begins to cycle at shorter intervals than expected. For example using an interval of 3600 seconds starts a sleep that appears to last about 20 seconds. I wonder if there might be an overflow condition occurring on the input parameter?
@capnf which code are you getting that with? Is that with the micropython uf2 file?
Hello,
I downloaded a uf2 file - micropython-pico-deepsleep-1-0.uf2 - and loaded that onto the Pico.
David F
From: ghubcoder @.> Sent: 06 November 2021 12:13 To: ghubcoder/PicoSleepDemo @.> Cc: capnf @.>; Mention @.> Subject: Re: [ghubcoder/PicoSleepDemo] micropython (#2)
@capnf https://github.com/capnf which code are you getting that with? Is that with the micropython uf2 file?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ghubcoder/PicoSleepDemo/issues/2#issuecomment-962442658 , or unsubscribe https://github.com/notifications/unsubscribe-auth/AWL2F3OU6442SNKEXONXNWTUKULT3ANCNFSM5FNN3XPQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub . https://github.com/notifications/beacon/AWL2F3MAGAMJB467QBEYK6LUKULT3A5CNFSM5FNN3XP2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOHFO3LIQ.gif
Great thanks @capnf I will take a look at what might be going on there.
@capnf I've uploaded a new release which should fix that here
If you could give it a test when you get a moment that would be much appreciated.
That's terrific - thank you. I've downloaded and am testing now. Will get back with results later today.
I've run tests at various timer intervals from 60 seconds up to 1500 secs and all worked correctly, whereas previously it began to misbehave at the upper end of that range. Obviously it will take longer to check what happens when much longer intervals are selected, but I'll keep running the tests and let you know. Looks as though it's fixed though - good work and super fast response :)
It works at 3600secs also, so looks like the fix is good.
Brilliant, thanks for checking.
You were right with your hunch, it was overflowing. The type used was int8_t
, max value 127.
Now it's using uint32_t
, max value 4,294,967,295 which I hope should be plenty.
haha!. Debugging by hunch 👍 - I was 35 years in the IT business, and have never coded in C or Python, but some issues smell the same no matter what the language. I calculate that your new counter will be good for intervals up to the year 2157, and as I've already been around quite a lot of years I doubt I'll have to worry about what happens from then on :)
I'm planning to use your new module in a remote monitoring system for my boat. I want to rig up a system that sleeps on minimum power, then fires up a mifi router periodically, sniffs the temperature and the service battery voltages, and then reports back via a Cricket wifi module and a Pipedream workflow, before going to sleep again. I can't use the main service batteries to power this because the boatyards insist that everything is disconnected in Winter due to the fire risk. Your code will, I hope, let me use a pretty small standalone battery that will last all winter - I wouldn't have been able to do this without your help.
That's really good to hear you know 🙂 I'm glad this little experiment has been useful for someone else and will be put to good use.
Thanks for raising the issue!
I'm a tad off topic with this question but I'm going to ask it anyway because, after a month of trying stuff, I've run out of ideas. Your uf2 works great & my test program with picosleep.seconds(300) works the same as time.sleep(300), the only difference being the greatly reduced power consumption during the 300s of dormancy in the case of the former.
So next I connected the pico to to a simcom7020 4G modem via uart0. Yes, this is the off topic part. This modem 4G network attaches automatically, but you have to tell it to 4G network connect. Here's the rub! If the pico has slept I can command the modem to connect no probs, but if the pico has been dormant no dice. It's bizarre, like the modem somehow knows which sleep mode I've used. The only way I can get the modem to connect is if I do a machine.reset() after the dormancy is finished. This is not the end of the world but it bugs me because I loose all my variables in RAM. Initially I thought the dormancy must have subtly effected uart0 but the modem responds to all other AT cmds normally, just won't 4g connect.
Since the 2 uart0 lines & 0v are the only 3 wires the modem & pico share I can't figure out how the modem knows when I've used dormancy Vs sleep. From your understanding of what happens to the various timers when dormant do you have any thoughts on what machine.reset() is fixing that allows the modem to carry out the connect command.
I finally got to the bottom of this. The dormancy leaves the pico with a uart unable to send or receive more than 32 chrs. So when the modem returns b'at+cgcontrdp\r\r\n+CGCONTRDP: 1,5,"telstra.internet","10.108.59.111.255.255.255.0",,"101.168.244.106","10.4.130.164",,,,,1500 for the connect cmd the pico sees only b'at+cgcontrdp\r\r\n+CGCONTRDP: 1,5,
Very strange @kjm1102.
I've tested with two picos connected together via UART 0. Pico 1 sends a long string every 5 seconds. Pico 2 runs the micropython deep sleep uf2 with the following:
import machine
uart = machine.UART(0)
from machine import Pin
led = Pin(25, Pin.OUT)
import time
import picosleep
def flash(times,sleep):
num = 0
while num < times:
led.toggle()
time.sleep_ms(sleep)
num=num+1
while True:
try:
if uart.any():
rxData = uart.read()
string = rxData.decode("ascii")
if "UART0 with debug with very very long string to see if it is cut" in string:
flash(4,500)
picosleep.seconds(2)
except UnicodeError as e:
continue
I added in the UnicodeError exception handler as sometimes when it starts up it fails with that error.
I also had to remove all print()
statements as this seems to hang the pico after a while when sleeping. This is whilst connected via usb. There are definitely issues with usb communication after deep sleeping, I haven't been able to get to the bottom of that. Another issue was raised here https://github.com/ghubcoder/PicoSleepDemo/issues/1
However with the above code it will loop and flash indicating it has received the long string that is being sent after sleeping. That is more than 32 chars.......
Seems like there was someone else with your issue though a while back: https://forum.micropython.org/viewtopic.php?t=9913
On Mon, 3 Jan 2022 at 00:40, ghubcoder @.***> wrote:
Very strange @kjm1102 https://github.com/kjm1102.
I've tested with two picos connected together via UART 0. Pico 1 sends a long string every 5 seconds. Pico 2 runs the micropython deep sleep uf2 with the following:
import machine uart = machine.UART(0) from machine import Pin led = Pin(25, Pin.OUT) import time import picosleep
def flash(times,sleep): num = 0 while num < times: led.toggle() time.sleep_ms(sleep) num=num+1
while True: try: if uart.any(): rxData = uart.read() string = rxData.decode("ascii") if "UART0 with debug with very very long string to see if it is cut" in string: flash(4,500) picosleep.seconds(2)
except UnicodeError as e: print(e) continue
Interesting squire! I ran a tweaked version of your code where I query the modem for a response that has more than 32 chrs (looks like 'at+cclk?\r\r\n+CCLK: 22/01/03,11:07:33+44\r\n\r\nOK\r\n')
while True: try: f=open('log', 'w'); f.close() uart.write('at+cclk?\r\n') if uart.any(): rxData = uart.read() string = rxData.decode("ascii") if "OK" in string: flash(4,500) picosleep.seconds(2) time.sleep(2) else: f=open('log', 'a'); f.write(repr(string)+'\n'); f.close() except UnicodeError as e: print(e) continue
So you'd expect the modem response to get the same post dormant treatment as your pico2 reply? But no, when I check the log file for 'OK'less responses I get just the 1st 32 chars of the reply.
'at+cclk?\r\r\n+CCLK: 22/01/03,11:27' 'at+cclk?\r\r\n+CCLK: 22/01/03,11:27' 'at+cclk?\r\r\n+CCLK: 22/01/03,11:27' 'at+cclk?\r\r\n+CCLK: 22/01/03,11:27'
Are you using the uart0 on the default GP0/GP1 pins same as me? Maybe my writing to uart0 post dormancy is the problem? Do you still get all of your pico2 reply if you've previously written to the pico1 uart?
Seems like there was someone else with your issue though a while back: https://forum.micropython.org/viewtopic.php?t=9913
Unreal, hemberg is using a modem from the same manufacturer. It seems from that thread that the 32 chr uart brick wall was fixed in the April 2021 UF2. So why it's back for me & not for you is baffling.Message ID: @.***>
I rewrote my code so that pico2 would send data to pico1, and then pico1 would reply.
Using your code I was able to reproduce the issue where it would only ever receive 32 chars after sleeping, by checking the contents of the log file.
The fix for me is to reinitialise uart0 immediately once we wake up:
picosleep.seconds(2)
uart = machine.UART(0, 115200)
Full code looks like:
import machine
uart = machine.UART(0)
from machine import Pin
led = Pin(25, Pin.OUT)
import time
import picosleep
def flash(times,sleep):
num = 0
while num < times:
led.toggle()
time.sleep_ms(sleep)
num=num+1
while True:
time.sleep(2)
uart.write('send\n')
try:
while uart.any():
rxData = uart.read()
string = rxData.decode("ascii")
if "UART0 with debug with very very long string to see if it is cut" in string:
flash(4,500)
picosleep.seconds(2)
uart = machine.UART(0, 115200)
else:
f=open('log', 'a'); f.write(repr(string)+'\n'); f.close()
except UnicodeError as e:
continue
This isn't great, sleeping must reset something which is breaking this. But for me this does appear to work around the issue.
Hopefully this will work for you as well 🤞
Nice fix, works for me too. Mercifully, dormancy seems to leave I2C unscathed so I'm using an oled for diagnostics instead of print over USB to the repl.
There must be a uart that handles the usb too right, uart-1? If we reset it too after dormancy do you think print over USB to the repl might keep working?
On Mon, 3 Jan 2022 at 20:54, ghubcoder @.***> wrote:
I rewrote my code so that pico2 would send data to pico1, and then pico1 would reply.
Using your code I was able to reproduce the issue where it would only ever receive 32 chars after sleeping, by checking the contents of the log file.
The fix for me is to reinitialise uart0 immediately once we wake up:
picosleep.seconds(2)
uart = machine.UART(0, 115200)
Full code looks like:
import machine
uart = machine.UART(0)
from machine import Pin
led = Pin(25, Pin.OUT)
import time
import picosleep
def flash(times,sleep):
num = 0 while num < times: led.toggle() time.sleep_ms(sleep) num=num+1
while True:
time.sleep(2) uart.write('send\n') try: while uart.any(): rxData = uart.read() string = rxData.decode("ascii") #print(string) if "UART0 with debug with very very long string to see if it is cut" in string: flash(4,500) picosleep.seconds(2) uart = machine.UART(0, 115200) else: f=open('log', 'a'); f.write(repr(string)+'\n'); f.close() except UnicodeError as e: continue
This isn't great, sleeping must reset something which is breaking this. But for me this does appear to work around the issue.
Hopefully this will work for you as well 🤞
— Reply to this email directly, view it on GitHub https://github.com/ghubcoder/PicoSleepDemo/issues/2#issuecomment-1003978394, or unsubscribe https://github.com/notifications/unsubscribe-auth/AV562MYRCNVYFZRJAMT7BYTUUFW7BANCNFSM5FNN3XPQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
You are receiving this because you were mentioned.Message ID: @.***>
I was trying to use the deep sleep function. I uploaded the new picosleep firmware to my board. I have a dc motor which is being powered with the help of a transistor. My objective is to toggle the motor at an interval of 5 seconds. In between the intervals the pico should go in deepsleep mode. The problem is that nothing happens. This is my code
from machine import Pin
import picosleep
motor = Pin(1, Pin.OUT)
while True:
motor.value(1)
picosleep.seconds(5)
motor.value(0)
picosleep.seconds(5)
If I replace the picosleep.seconds()
with the utime.sleep()
(and of course import the utime module as well) then everything works as expected. What is the issue here?
Define 'nothing happens', the motor fails to start before the first dormancy? Have you tried; 1) Using the pico led instead of gpio1 so you can eyeball what's happening 2) putting a utime.sleep(.1) before the picosleep in case the picosleep fires before the motor gpio has finished toggling?
I did exactly what you suggested and the led indeed toggled at an interval of 5 seconds. After this I disconnected my pico, attached a multimeter and reconnected it, I couldn't however, measure the current consumption as the pico wouldn't connect. Everytime I connected it with the IDE, It logs this error in loop
Unable to connect to COM8: Cannot configure port, something went wrong. Original message: PermissionError(13, 'A device attached to the system is not functioning.', None, 31)
If you have serial connection to the device from another program, then disconnect it there first.
Process ended with exit code 1.
I noticed that this error went away when I boot the pico and upload the standard micropython uf2 firmware. But it comes back again after reuploading the pico sleep micropython uf2 firmware. Any possible solution to fix this? And thank you for helping me out so far
Okay so I uploaded a flash nuke to the pico which I got online while looking for a solution for this error that is mentioned above. I found out that I need to do this everytime I save the code in pico. It temporarily fixes the issue.
As I said earlier that this code (given below) works fine
from machine import Pin
import picosleep
import utime
led= Pin(25, Pin.OUT)
while True:
led.value(1)
utime.sleep(1)
picosleep.seconds(5)
led.value(0)
picosleep.seconds(5)
However, as soon as I change the pin number to GPIO 1, To which the motor is connected, things start to go south. The motor starts but it stops running after 1-2 seconds. And from then nothing happens. No motor movement or anything.
from machine import Pin
import picosleep
import utime
motor = Pin(1, Pin.OUT)
while True:
motor.value(1)
utime.sleep(1)
# Motor stops whenever pico reaches the next line, and from then onwards nothing happens, no motor movement at all
picosleep.seconds(5)
motor.value(0) # Motor should stop at this line after the 5 second deep sleep
picosleep.seconds(5)
ghubcoder did well to get dormancy working but it's fragile, print statements break it & it seems to have issues with the uart0 pins (gpio 0 & 1). gpio2 seems to work OK as a motor driver with picosleep.
Not sure what's wrong with your IDE, I've found Atom & Thonny work OK but are sensitive to other programs that might be running on your PC, Cura for example will hog the serial ports on a Windows machine & stop them connecting to the pico.
Maybe try a 'for i in range (5)' instead of 'while True' while testing so the program can quit with the IDE connection still working.
I have fixed the issue with the help of some workarounds. Thank you for your help
My guess is that, like me, the majority of RP2040 users are coding newbies who've had a splash in the micropython pool but haven't ventured into the murky water that is C++.
A micropython version of a 1.3mA sleep mode that wakes itself from an internal timer would broaden the scope & appeal of this ingenious bit of detective work considerably.