Open ThomasAtBBTF opened 3 years ago
For further information on this topic.
For testing, I deinitialized all 12 pins and passed them together with a TimeAlarm to light_sleep_until_alarms()
Here my code:
braillekeyboard.deinitpins()
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 60*60)
alarms = [time_alarm]
for p in braillekeyboard.pins:
p_alarm = alarm.pin.PinAlarm(pin=p.pinid, value=False, pull=True)
alarms.append(p_alarm)
print("going to sleep")
alarm.light_sleep_until_alarms(*alarms)
print("woken up")
braillekeyboard.initpins()
This sometimes works. Other times not all of the 12 pins are waking up from sleep and also sometimes I run into a hard fault. Is there a limit how many alarm sources may be specified? Did anyone else observe the instability of the system after waking up?
Note: The problems are occurring regardless if connected to USB or not.
The pins used for the braillekeyboard are:
self.B1 = PinKey(board.A4, "B1")
self.B2 = PinKey(board.A5, "B2")
self.B3 = PinKey(board.MISO, "B3")
self.B7 = PinKey(board.SCK, "B7")
self.B4 = PinKey(board.A0, "B4")
self.B5 = PinKey(board.A1, "B5")
self.B6 = PinKey(board.A3, "B6")
self.B8 = PinKey(board.A2, "B8")
self.BL = PinKey(board.D12, "BL")
self.B9 = PinKey(board.D9, "B9")
self.B0 = PinKey(board.D7, "B0")
self.BR = PinKey(board.D13, "BR")
These "PinKeys" are added to braillekeyboard.pins by:
self.addpin(self.B1)
self.addpin(self.B2)
self.addpin(self.B3)
self.addpin(self.B7)
self.addpin(self.B4)
self.addpin(self.B5)
self.addpin(self.B6)
self.addpin(self.B8)
self.addpin(self.BL)
self.addpin(self.B9)
self.addpin(self.B0)
self.addpin(self.BR)
Where addpin is:
def addpin(self, pin):
self.pins.append(pin)
And braillekeyboard.initpins() is;
def initpins(self):
for p in self.pins:
p.initpin()
And braillekeyboard.deinitpins() is;
for p in self.pins:
p.deinitpin()
And parts of class PinKey is:
def __init__(self, pinid, name):
self.pinid = pinid
self.downval = False
self.name = name
self.waitforup = False
self.initpin()
def initpin(self):
self.waitforup = False
self.pin = DigitalInOut(self.pinid)
self.pin.direction = Direction.INPUT
self.pin.pull = Pull.UP
def deinitpin(self):
self.waitforup = False
self.pin.deinit()
self.pin = None
Also, I am very happy with the reduction of power consumption in "light sleep"! While active the project board is using 9 mA when running from a fully charged 3.7v LIPO battery During sleep, the current is only 1.5 mA ! I am not sure about the exact mA values because I have no real high accuracy measuring equipment for so little currents. But the power consumption reduction is very promising for long battery life!
We recommend the PPK2 for measuring power: https://www.adafruit.com/product/5048
I wouldn't be surprised if there are bugs in the sleep implementation. It is very new. I'd suggest trying to narrow down the code that causes issues and then posting an issue so we can try and reproduce it.
We can leave this issue open for the sharing issue. I understand the problem but am not sure the best way to solve it. I'm happy you are working to the deinit and then pin alarm case because I'm curious if it actually misses presses.
Is this problem as simple as removing the "claim pin" effect in PinAlarm? I did not foresee a use case like this so I think I specifically put it in there for all ports. That said we should think about whether there will be ill effects to allowing this form of overlap - initializing a PinAlarm will do things like change the pull settings and potentially the internal mux settings, depending on the port, and so it may become useless for the original purpose after being constructed for the alarm.
I have a somewhat same use case, of trying to use an alarm pin, while awake.
I'm also trying to make sure that i don't miss any events, just on a single alarm pin. The code does something along the lines of
1: Wake up from alarm pin 2: Log timestamp 3: Do some processing 4: Go to sleep and wait for next alarm pin event
In order to catch any pin events happening while at step 3, i'm trying to use countio on the same alarm pin.
import alarm
import board
import countio
pin_alarm = alarm.pin.PinAlarm(pin=board.A3, value=False, edge=True, pull=True)
pulse = countio.Counter(board.A3)
The above code will run, and the pulse object will increment.
After running
alarm.light_sleep_until_alarms(pin_alarm)
and waking up, the pulse object will stop incrementing however.
It seems the claim pin code does not work correctly with either the alarm or countio module.
Interestingly, swapping line 4 and 5 will make the code error, with
ValueError: A3 in use
Using rp2040 on 7.0.0a5
We recommend the PPK2 for measuring power: https://www.adafruit.com/product/5048
I wouldn't be surprised if there are bugs in the sleep implementation. It is very new. I'd suggest trying to narrow down the code that causes issues and then posting an issue so we can try and reproduce it.
We can leave this issue open for the sharing issue. I understand the problem but am not sure the best way to solve it. I'm happy you are working to the deinit and then pin alarm case because I'm curious if it actually misses presses.
Thank you for the information about the Nordic Power Profiler, I will buy one of these. And, actually, for the moment I can not observe any missed presses. But for production code, the sleep implementation is currently too buggy. I will try to make a simplified version of the code which can run on a CLUE and will see if I will observe the same instabilities. If so, I will post a bug here.
@mrdalgaard That's definitely a bug. :-) You shouldn't be able to use a pin twice.
What are you using it for? Key/button scanning as well?
Maybe this would be best served by making the new keypad
module sleep compatible. I think that'd work for @ThomasAtBBTF's keyboard.
@tannewt Actually its for a remote anemometer on an airfield. It will generate 0-30 pulses/sec. The pulseio module is hence not usable, and using just countio makes the resolution bad at low speeds. (need to measure time between pulses, not the number of pulses) When calculating gusts, average wind directions over different durations, and sending the data via the RFM95 module, pulses would potentially get lost.
For now i've worked around it, by connecting the input to two pins, and using countio on one to catch missing pulses, and alarm on the other one to keep power usage down between pulses. This is obviously not useful for @ThomasAtBBTF - Just wanted to mention another use case of the same pin being used in both run and sleep.
@mrdalgaard If keypad
had simple tick timestamps, I wonder if that would work for you. Or if countio kept some kind of simple timestamps (e.g. supervisor.tick_ms()
values) of the previous n
ticks, maybe that would be useful. But that wouldn't help your lower-power sleep quest.
Some simple external hardware with a counter that you poll often might work.
@mrdalgaard If
keypad
had simple tick timestamps, I wonder if that would work for you. Or if countio kept some kind of simple timestamps (e.g.supervisor.tick_ms()
values) of the previousn
ticks, maybe that would be useful. But that wouldn't help your lower-power sleep quest.Some simple external hardware with a counter that you poll often might work.
Any of those modules, with logging of simple timestamps would definitely work. pulseio could also work, as it does log time intervals, but it does not currently allow the longer intervals between pulses.
Switching the project to the nRF has provided big powersavings, so the use of the alarm module is really more for its use as a primitive interrupt system.
I wish i could contribute with improving any of these modules, but while the python coding is hard enough for me, the c code in CP is just way above my skill level
If
keypad
had simple tick timestamps, I wonder if that would work for you. Or if countio kept some kind of simple timestamps (e.g.supervisor.tick_ms()
values) of the previousn
ticks, maybe that would be useful. But that wouldn't help your lower-power sleep quest.
As of https://github.com/adafruit/circuitpython/commit/72bfd39a1754ca5b6c6a3c6d6b03444fe92ae49e, keypad
events do capture timestamps (using supervisor.ticks_ms()
), but they're based on polling so I don't know how accurate they get. countio
does not yet have this functionality for timing interrupts.
I am working on a project where 12 processor pins are used on an nRF52840 and I am running into the problem that these pins can not be used as pins for ending light sleep. It would be helpful if already initialized / in use pins could be added to
alarm.light_sleep_until_alarms()
. I fear, deinitializing all 12 pins (which I use to detect user input) before "going to sleep" and reinitializing them after "wake up" takes too long and will result in losing user input events.Or, can I initialize the PinAlarm pin-instances globally and use the values of these pins "while not sleeping" in my regular code?
PS: A hardware alternative would be to use a M74HC133 to couple all the pins and connect the output to another "wakeup-pin". I hope that this can be avoided by software.