I assumed v4.4.1 would be valid due to SEMVER versioning and the README for the esp32 port specifically mentions supporting v4.4
I tried the exact same setup with v4.4 and the result is completely different:
I added some info for those interested, but I have not put time and effort in diagnosing this issue yet.
I was just hoping that "hopping from one weird error into the next" will tip one of you off in determining what might be wrong with my setup.
I will try to find the exact error line in the lib where the panic occurs tomorrow.
Code: Select all
# pcf8523.py
import ucollections
import utime
import micropython
from machine import Pin
from time import sleep
DateTimeTuple = ucollections.namedtuple("DateTimeTuple", ["year", "month",
"day", "weekday", "hour", "minute", "second", "millisecond"])
def datetime_tuple(year=None, month=None, day=None, weekday=None, hour=None,
minute=None, second=None, millisecond=None):
return DateTimeTuple(year, month, day, weekday, hour, minute,
second, millisecond)
def _bcd2bin(value):
return (value or 0) - 6 * ((value or 0) >> 4)
def _bin2bcd(value):
return (value or 0) + 6 * ((value or 0) // 10)
def tuple2seconds(datetime):
return utime.mktime((datetime.year, datetime.month, datetime.day,
datetime.hour, datetime.minute, datetime.second, datetime.weekday, 0))
def seconds2tuple(seconds):
(year, month, day, hour, minute,
second, weekday, _yday) = utime.localtime(seconds)
return DateTimeTuple(year, month, day, weekday, hour, minute, second, 0)
class _BaseRTC:
_SWAP_DAY_WEEKDAY = False
def __init__(self, i2c, address=0x68):
self.i2c = i2c
self.address = address
def _register(self, register, buffer=None):
if buffer is None:
return self.i2c.readfrom_mem(self.address, register, 1)[0]
self.i2c.writeto_mem(self.address, register, buffer)
def _flag(self, register, mask, value=None):
data = self._register(register)
if value is None:
return bool(data & mask)
if value:
data |= mask
else:
data &= ~mask
self._register(register, bytearray((data,)))
def datetime(self, datetime=None):
if datetime is None:
buffer = self.i2c.readfrom_mem(self.address,
self._DATETIME_REGISTER, 7)
if self._SWAP_DAY_WEEKDAY:
day = buffer[3]
weekday = buffer[4]
else:
day = buffer[4]
weekday = buffer[3]
return datetime_tuple(
year=_bcd2bin(buffer[6]) + 2000,
month=_bcd2bin(buffer[5]),
day=_bcd2bin(day),
weekday=_bcd2bin(weekday),
hour=_bcd2bin(buffer[2]),
minute=_bcd2bin(buffer[1]),
second=_bcd2bin(buffer[0]),
)
datetime = datetime_tuple(*datetime)
buffer = bytearray(7)
buffer[0] = _bin2bcd(datetime.second)
buffer[1] = _bin2bcd(datetime.minute)
buffer[2] = _bin2bcd(datetime.hour)
if self._SWAP_DAY_WEEKDAY:
buffer[4] = _bin2bcd(datetime.weekday)
buffer[3] = _bin2bcd(datetime.day)
else:
buffer[3] = _bin2bcd(datetime.weekday)
buffer[4] = _bin2bcd(datetime.day)
buffer[5] = _bin2bcd(datetime.month)
buffer[6] = _bin2bcd(datetime.year - 2000)
self._register(self._DATETIME_REGISTER, buffer)
def now(self):
return self.datetime()
class PCF8523(_BaseRTC):
_CONTROL1_REGISTER = 0x00
_CONTROL2_REGISTER = 0x01
_CONTROL3_REGISTER = 0x02
_DATETIME_REGISTER = 0x03
_ALARM_REGISTER = 0x0a
_SQUARE_WAVE_REGISTER = 0x0f
_SWAP_DAY_WEEKDAY = True
_alarm_time = None
irq_handler = None
def isr_handler(self, pin):
micropython.schedule(self.irq_callback_handler, None)
def __init__(self, i2c, address=0x68, irq_pin=None):
self._irq_pin = irq_pin
super().__init__(i2c, address)
self.init()
def init(self):
# Enable battery switchover and low-battery detection.
self._flag(self._CONTROL3_REGISTER, 0b11111111, False)
self._flag(self._CONTROL2_REGISTER, 0b00000000, True)
# Disable CLK out:
self._flag(self._SQUARE_WAVE_REGISTER, 0b00111000, True)
sleep(0.050)
if self._irq_pin is not None:
self._irq_pin.init(mode=Pin.IN)
self._irq_pin.irq(trigger=Pin.IRQ_FALLING, handler=self.isr_handler)
def deinit(self):
self.reset()
def reset(self):
self._flag(self._CONTROL1_REGISTER, 0x58, True)
self.init()
def lost_power(self, value=None):
return self._flag(self._CONTROL3_REGISTER, 0b00010000, value)
def stop(self, value=None):
return self._flag(self._CONTROL1_REGISTER, 0b00010000, value)
def battery_low(self):
return self._flag(self._CONTROL3_REGISTER, 0b00000100)
def alarm_irq_enable(self, value=None):
return self._flag(self._CONTROL1_REGISTER, 0b00000010, value)
def alarm_triggered(self, value=None):
return self._flag(self._CONTROL2_REGISTER, 0b00001000, value)
def datetime(self, datetime=None):
if datetime is not None:
self.lost_power(False) # clear the battery switchover flag
return super().datetime(datetime)
def alarm(self, datetime=None):
if datetime is None:
buffer = self.i2c.readfrom_mem(self.address,
self._ALARM_REGISTER, 4)
return datetime_tuple(
weekday=_bcd2bin(buffer[3] &
0x7f) if not buffer[3] & 0x80 else None,
day=_bcd2bin(buffer[2] &
0x7f) if not buffer[2] & 0x80 else None,
hour=_bcd2bin(buffer[1] &
0x7f) if not buffer[1] & 0x80 else None,
minute=_bcd2bin(buffer[0] &
0x7f) if not buffer[0] & 0x80 else None,
)
datetime = datetime_tuple(*datetime)
self._alarm_time = datetime
buffer = bytearray(4)
buffer[0] = (_bin2bcd(datetime.minute)
if datetime.minute is not None else 0x80)
buffer[1] = (_bin2bcd(datetime.hour)
if datetime.hour is not None else 0x80)
buffer[2] = (_bin2bcd(datetime.day)
if datetime.day is not None else 0x80)
buffer[3] = (_bin2bcd(datetime.weekday) | 0b01000000
if datetime.weekday is not None else 0x80)
self._register(self._ALARM_REGISTER, buffer)
# clear alarm interrupt
self.alarm_triggered(value=False)
def alarm_left(self):
raise NotImplementedError
return
# check if alarm is activated
if (self.alarm_irq_enable() is True):
print("alarm time = " + str(self._alarm_time))
if (self._alarm_time is not None):
now = self.now()
self._alarm_time.year = now.year
self._alarm_time.month = now.month
self._alarm_time.second = now.second
self._alarm_time.millisecond = now.millisecond
return tuple2seconds(self._alarm_time) - tuple2seconds(self.now())
return None
def cancel(self):
self.alarm_irq_enable(value=False)
self.alarm_time(datetime=None)
def irq_callback_handler(self, *args):
# clear alarm interrupt
self.alarm_triggered(value=False)
if (self.irq_handler is not None):
self.irq_handler()
def alarm_interrupt(self, handler=None):
# assign interrupt callback
self.irq_handler = handler
if (self.irq_handler is None):
self.alarm_irq_enable(value=False)
else:
self.alarm_irq_enable(value=True)