I think that I got I working as I wanted, but as I was having some problems I will document it there if somebody would be interested.
At first I had to install machine
from upip. The Timer()
defined there (/home/pi/.micropython/lib/machine/timer.py
) is a little bit different to what is expected in lv_utils.py
. It looks like that:
import ffilib
import uctypes
import array
import uos
import os
import utime
from signal import *
libc = ffilib.libc()
librt = ffilib.open("librt")
CLOCK_REALTIME = 0
CLOCK_MONOTONIC = 1
SIGEV_SIGNAL = 0
sigval_t = {
"sival_int": uctypes.INT32 | 0,
"sival_ptr": (uctypes.PTR | 0, uctypes.UINT8),
}
sigevent_t = {
"sigev_value": (0, sigval_t),
"sigev_signo": uctypes.INT32 | 8,
"sigev_notify": uctypes.INT32 | 12,
}
timespec_t = {
"tv_sec": uctypes.INT32 | 0,
"tv_nsec": uctypes.INT64 | 8,
}
itimerspec_t = {
"it_interval": (0, timespec_t),
"it_value": (16, timespec_t),
}
__libc_current_sigrtmin = libc.func("i", "__libc_current_sigrtmin", "")
SIGRTMIN = __libc_current_sigrtmin()
timer_create_ = librt.func("i", "timer_create", "ipp")
timer_settime_ = librt.func("i", "timer_settime", "PiPp")
def new(sdesc):
buf = bytearray(uctypes.sizeof(sdesc))
s = uctypes.struct(uctypes.addressof(buf), sdesc, uctypes.NATIVE)
return s
def timer_create(sig_id):
sev = new(sigevent_t)
#print(sev)
sev.sigev_notify = SIGEV_SIGNAL
sev.sigev_signo = SIGRTMIN + sig_id
timerid = array.array('P', [0])
r = timer_create_(CLOCK_MONOTONIC, sev, timerid)
os.check_error(r)
#print("timerid", hex(timerid[0]))
return timerid[0]
def timer_settime(tid, hz):
period = 1000000000 // hz
new_val = new(itimerspec_t)
new_val.it_value.tv_nsec = period
new_val.it_interval.tv_nsec = period
#print("new_val:", bytes(new_val))
old_val = new(itimerspec_t)
#print(new_val, old_val)
r = timer_settime_(tid, 0, new_val, old_val)
os.check_error(r)
#print("old_val:", bytes(old_val))
#print("timer_settime", r)
class Timer:
def __init__(self, id, freq):
self.id = id
self.tid = timer_create(id)
self.freq = freq
def callback(self, cb):
self.cb = cb
timer_settime(self.tid, self.freq)
org_sig = signal(SIGRTMIN + self.id, self.handler)
#print("Sig %d: %s" % (SIGRTMIN + self.id, org_sig))
def handler(self, signum):
#print('Signal handler called with signal', signum)
self.cb(self)
So basically it takes two arguments during initialization (frequency beside id) and it does not have Timer.init()
method. I tried to adjust lv_utils.py
to this version of timer, but it crashes while initializing anyway:
Traceback (most recent call last):
File "main.py", line 81, in <module>
File "main.py", line 46, in main
File "/home/pi/display/lv_utils.py", line 89, in __init__
File "/home/pi/.micropython/lib/machine/timer.py", line 78, in __init__
File "/home/pi/.micropython/lib/machine/timer.py", line 56, in timer_create
File "/home/pi/.micropython/lib/os/__init__.py", line 68, in check_error
OSError: [Errno 22] EINVAL
I didn’t want to dig inside this because what’s being done there is far beyond my knowledge, so I tried with uasyncio driven loop:
Installed uasyncio with upip, but:
>>> import uasyncio
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: incompatible .mpy file
I found out in the Micropython forums that there is a problem with upip version of uasyncio for unix, so I just copied uasyncio from lv_micropython/extmod
to /home/pi/.micropython/lib
and it works fine.
So in my main context I added simple function:
def handle_exceptions(e):
sys.print_exception(e, sys.stderr)
sys.exit(1)
and defined my event loop like that:
lv_utils.event_loop(asynchronous=True, exception_sink=handle_exceptions)
Then I realized that it still doesn’t work cause exceptions are being raised inside lv_utils.async_refresh()
at line with lv.task_handler()
and they are not caught nor passed to exception_sink
.
So I changed it to:
async def async_refresh(self):
while True:
await self.refresh_event.wait()
self.refresh_event.clear()
try:
lv.task_handler()
except Exception as e:
if self.exception_sink:
self.exception_sink(e)
if self.refresh_cb: self.refresh_cb()
, to match what is being done inside lv_utils.task_handler()
It does what I want it to do. Basically if unhandled exception occurs in the event handlers context, the traceback is being printed to stderr and program exits (so it can be restarted by Supervisor).
As always, thank you @amirgon for your invaluable help! I would be glad if you could comment on my solution, as I expect that there is probably more elegant and safe way to do things.