esp-idf v4.4.1 oddity

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
ChrisO
Posts: 48
Joined: Mon Apr 06, 2020 6:16 pm

esp-idf v4.4.1 oddity

Post by ChrisO » Tue May 17, 2022 2:58 pm

Hi,

Based on a comment from @bulletmark I tried updating my dev environment.
I've checked out micropython latest, and did the following to get a matching ESP-IDF installation:

Code: Select all

source micropython/tools/ci.sh
ci_esp32_setup_helper v4.4.1
source esp-idf/export.sh
make -C micropython/mpy-cross
make -C micropython/ports/esp32 clean all
I have a custom board:

Code: Select all

# 8 MB flash

CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_ESPTOOLPY_FLASHSIZE="8MB"
# Fast flash

CONFIG_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESP32_REV_MIN_1=y

# OTA

CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB-ota.csv"

# BLE
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1

# Network name

CONFIG_LWIP_LOCAL_HOSTNAME="Custom - SC"
The firmware resulting from this does not allow me to open a socket.
It results in red text (assuming this is from the C-stack):
E (20370) esp.emac: no mem for receive buffer
E (20440) esp.emac: no mem for receive buffer
E (20700) esp.emac: no mem for receive buffer
E (20700) esp.emac: no mem for receive buffer
E (20700) esp.emac: no mem for receive buffer
E (20790) esp.emac: no mem for receive buffer

The exact same procedure works for v4.2.3

I tried google, but found nobody with similar issues.
So, I guess I'm doing something odd here :?
Now I'm not in desperate need to update to v4.4.1 but I surely would like to get a better grip on Micropython development...
does anyone have a clue whether I might have found a bug, or whether I am doing something incorrectly :?:
Chris

ChrisO
Posts: 48
Joined: Mon Apr 06, 2020 6:16 pm

Re: esp-idf v4.4.1 oddity

Post by ChrisO » Tue May 17, 2022 3:15 pm

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.

for now I have to go.

Code: Select all

rtc = PCF8523(peripheral_bus, irq_pin=Pin(sfc.RTCC_INT))
initialising this external RTC fails with a kernel panic:

Code: Select all

Guru Meditation Error: Core  1 panic'ed (StoreProhibited). Exception was unhandled.

Core  1 register dump:
PC      : 0x4008c878  PS      : 0x00060031  A0      : 0x8008ca0d  A1      : 0x3ffbeed0  
A2      : 0x3ffbc774  A3      : 0x00000000  A4      : 0x00000000  A5      : 0x00000800  
A6      : 0x00000003  A7      : 0x00060f23  A8      : 0x3ffbc980  A9      : 0x00000901  
A10     : 0x00000003  A11     : 0x00060023  A12     : 0x00000001  A13     : 0x3ffbc984  
A14     : 0x00000000  A15     : 0x3ff53000  SAR     : 0x0000001d  EXCCAUSE: 0x0000001d  
EXCVADDR: 0x00000008  LBEG    : 0x00000000  LEND    : 0x00000000  LCOUNT  : 0x00000000  


Backtrace:0x4008c875:0x3ffbeed00x4008ca0a:0x3ffbef10 0x40082c39:0x3ffbef40 0x4014b491:0x3ffca4b0 0x400963e8:0x3ffca4d0 

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)
Chris

ChrisO
Posts: 48
Joined: Mon Apr 06, 2020 6:16 pm

Re: esp-idf v4.4.1 oddity

Post by ChrisO » Thu Jun 09, 2022 8:28 am

turns out I had a build/caching issue.
The bug was part of v4.4 and was fixed in v4.4.1.
See viewtopic.php?f=18&t=11330
Chris

Post Reply