Skip to content

Hard fault: memory access or instruction error. #9173

Open
@psitem

Description

@psitem

CircuitPython version

Adafruit CircuitPython 9.0.3 on 2024-04-04; Waveshare ESP32-S3-Zero with ESP32S3
Board ID:waveshare_esp32_s3_zero
UID:437BAD95F6CC

Libraries updated to adafruit-circuitpython-bundle-9.x-mpy-20240411.zip. Plus async_button library from https://github.com/furbrain/CircuitPython_async_button/releases/download/1.2.3/circuitpython-async-button-py-1.2.3.zip

Code/REPL

import time
import board
import busio
import supervisor
from digitalio import DigitalInOut, Direction, Pull
from pwmio import PWMOut
from adafruit_motor import motor as Motor

import gc
import rtc
import displayio
import adafruit_displayio_ssd1306
import adafruit_ina219
import wifi

import adafruit_ds3231
import adafruit_ntp
import socketpool
import asyncio
from async_button import Button, MultiButton

extended_debug = False
timeset = False

displayio.release_displays()
scl_pin = board.IO2
sda_pin = board.IO1
i2c = busio.I2C(scl_pin, sda_pin)

rtc_module = adafruit_ds3231.DS3231(i2c)

ina219 = adafruit_ina219.INA219(i2c)

ina219.bus_adc_resolution = adafruit_ina219.ADCResolution.ADCRES_12BIT_16S               
ina219.shunt_adc_resolution = adafruit_ina219.ADCResolution.ADCRES_12BIT_16S               

maxcurrent = 300
maxovercurrent = 4
mincurrent = 50
maxundercurrent = 10

light_state = False

if (True):
    display_bus = displayio.I2CDisplay(i2c, device_address=0x3C)
    display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=64)
    display.root_group[0].hidden = False
    display.root_group[1].hidden = True
    display.root_group[2].hidden = True
    try:
        display.root_group.remove(display.root_group[2])
        display.root_group.remove(display.root_group[1])
    except Exception as e:
        # print(e)
        e = None

    supervisor.reset_terminal(display.width, 84)
    display.root_group[0].y = 3
    display.root_group[0].x = 0


drv8833_ain1 = PWMOut(board.IO5, frequency=100)
drv8833_ain2 = PWMOut(board.IO4, frequency=100)

drv8833_enable = DigitalInOut(board.IO3)
drv8833_enable.direction = Direction.OUTPUT
drv8833_enable.value = True

# Note: Inverted, goes low for Fault conditions.
drv8833_fault = DigitalInOut(board.IO6)
drv8833_fault.direction = Direction.INPUT
drv8833_fault.pull = Pull.UP

motor_a = Motor.DCMotor(drv8833_ain1, drv8833_ain2)
motor_a.throttle = None

def print_timestamp():
    print("Time:       " + f'{time.localtime().tm_hour:02d} {time.localtime().tm_min:02d} {time.localtime().tm_sec:02d}')

CLICK_NAMES = {
    Button.SINGLE: "Single click",
    Button.DOUBLE: "Double click",
    Button.TRIPLE: "Triple click",
    Button.LONG: "Long click",
    Button.PRESSED: "Pressed",
    Button.RELEASED: "Released",
}

door_moving = False
door_direction = None
door_closed = True
door_opened = False

# 0 = stopped, positive = opening, negative = closing
door_throttle = 0 
lastDirection = 1

DOOR_STATES = {
    "Opened": 1,
    "Closed": 0,
}

async def click_watcher(button: MultiButton):
    global door_moving
    while True:
        button_name, click = await button.wait(BTN1=Button.ANY_CLICK , BTN2=Button.ANY_CLICK, BTN3=Button.ANY_CLICK)
        print(f"{button_name}: {CLICK_NAMES[click]}")

async def async_stop_btn(button: MultiButton):
    global door_throttle
    while True:
        await button.wait(BTN2=Button.SINGLE)
        print_timestamp()
        print("Stop.")
        door_throttle = 0
        await asyncio.sleep(0.10)
        print("Throttle: " + str(motor_a.throttle))

async def async_open_btn(button: MultiButton):
    global door_throttle
    while True:
        await button.wait(BTN3=Button.SINGLE)
        door_throttle = 1

async def async_close_btn(button: MultiButton):
    global door_throttle
    while True:
        await button.wait(BTN4=Button.SINGLE)
        door_throttle = -1

async def async_light_btn(button: MultiButton):
    global light_state
    while True:
        button_name, click = await button.wait(BTN1=Button.SINGLE)
        light_state = not light_state
        print("Light: " + str(light_state))
        asyncio.sleep(0.25)

async def async_kill_switch(button: MultiButton):
    global lastDirection 
    global motor_a
    global door_throttle

    while True:
        button_name, click = await button.wait(BTN5=Button.PRESSED)
        print_timestamp()
        print("Kill switch.")
        door_throttle = None
        asyncio.sleep(0.100)
        print("Opening door.")
        door_throttle = -1

async def async_open_close_btn(button: MultiButton):
    global lastDirection 
    global motor_a
    global door_throttle

    while True:
        button_name, click = await button.wait(BTN3=Button.SINGLE)
        print("Last: " + str(lastDirection))
        if ( (door_throttle is None) or (door_throttle == 0) ):
            print("Starting")
            door_throttle = -lastDirection
            lastDirection = door_throttle
        else:
            lastDirection = door_throttle
            door_throttle = 0

            print("Starting.")
            print("Throttle: " + str(motor_a.throttle))

        print("Now: " + str(lastDirection))

async def async_move_door(throttle: int):
    global motor_a
    global door_throttle

    motor_a.throttle = throttle

    while True:
        if (door_throttle == 0):
            motor_a.throttle = None
        else:
            motor_a.throttle = door_throttle
        await asyncio.sleep(0.05)


#async def async_monitor_current(duration: int):
async def async_monitor_current():
    global motor_a
    global ina219
    global door_throttle
    global lastDirection
    # print_timestamp()
    overcount = 0
    undercount = 0
    while (True):
        # now = time.monotonic()  # Time in seconds since power on
        # while (now + duration) > time.monotonic():
        while ( ( door_throttle != 0 ) ):
            while ( (overcount < maxovercurrent) and (undercount < maxundercurrent) ):
                await asyncio.sleep(0.005)
                current = ina219.current  # current in mA
                # print("Current: {:5.0f}  mA".format(current))

                if ( current > maxcurrent ):
                    overcount += 1
                    print("Current: {:5.0f}  mA".format(current))
                    print("Over-Current Count: {:1d}".format(overcount))
                elif ( ( door_throttle != 0 ) and ( current <mincurrent )):
                    pass
                    undercount += 1
                    print("Current: {:5.0f}  mA".format(current))
                    print("Under-Current Count: {:1d}".format(undercount))
                elif ( (current < maxcurrent) and (current > mincurrent) ):
                    undercount = 0
                    overcount = 0
            print("Stopping.")
            door_throttle = 0
            overcount = 0
            undercount = 0
            print_timestamp()
            await asyncio.sleep(0.10)
            print("Throttle: " + str(motor_a.throttle))
        await asyncio.sleep(0.05)

async def async_dump_throttle():
    global motor_a
    while (True):
        # print("Throttle: " + str(motor_a.throttle))
        # print("Enable:   " + str(drv8833_enable.value))
        # print("Fault:    " + str(not drv8833_fault.value))
        # print("Memory free: " + f'{gc.mem_free():>7}')
        await asyncio.sleep(1)

async def async_monitor_fault():
    global drv8833_fault
    
    while (True):
        # Pin is inverted
        if not drv8833_fault:
            print_timestamp()
            print("Fault:    " + str(not drv8833_fault.value))
            print("Stopping due to Fault.")
            door_throttle = 0

        await asyncio.sleep(0.005)


async def async_ntp():
    global ntp
    global pool
    global timeset
    global rtc_module
    while(False):
        loopcount = 0
        while ( (not timeset) and (loopcount < 5) ):
            if ( extended_debug ):
                print("Entering NTP loop.")
                print("loopcount: " + str(loopcount))
            try:
                loopcount += 1
                rtc.RTC().datetime = ntp.datetime
                rtc_module.datetime = ntp.datetime
                timeset = True
            except Exception as e:
                pass
        await asyncio.sleep(4 * 60 * 60) # sleep 4 hours
        
async def main():
    global door_throttle
    
    # Yellow / Light
    BTN1 = Button(
        board.IO10,
        value_when_pressed=False,
        long_click_enable=True,
        long_click_min_duration=0.7,
        triple_click_enable=False,
        double_click_enable=False,
    )

    # Red / Stop
    BTN2 = Button(
        board.IO9,
        value_when_pressed=False,
        long_click_enable=True,
        long_click_min_duration=0.7,
        triple_click_enable=False,
        double_click_enable=False,
    )

    # Green / Go
    BTN3 = Button(
        board.IO8,
        value_when_pressed=False,
        long_click_enable=True,
        long_click_min_duration=0.7,
        triple_click_enable=False,
        double_click_enable=False,
    )

    # Door Kill Switch
    BTN5 = Button(
        board.IO11,
        value_when_pressed=False,
        triple_click_enable=False,
        double_click_enable=False,
    )

    multibutton = MultiButton(BTN1=BTN1, BTN2=BTN2, BTN3=BTN3, BTN5=BTN5)

    await asyncio.gather(async_open_close_btn(multibutton), 
                         async_kill_switch(multibutton), 
                         async_stop_btn(multibutton), 
                         async_light_btn(multibutton), 
                         async_monitor_current(), 
                         async_move_door(0), 
                         async_dump_throttle(), 
                         async_ntp(), 
                         async_monitor_fault(),
                         )

    print("End of Main")

rtc.RTC().datetime = rtc_module.datetime
rtc.set_time_source(rtc_module)

print(str(wifi.radio.ipv4_address))
pool = socketpool.SocketPool(wifi.radio)
ntp = adafruit_ntp.NTP(pool, tz_offset=0)
# print("Localtime: " + str(time.localtime()))
# print("RTC time:  " + str(rtc.RTC().datetime))
# print("i2c time:  " + str(rtc_module.datetime))
print("Date:       " + f'{time.localtime().tm_year:04d} {time.localtime().tm_mon:02d} {time.localtime().tm_mday:02d}')
print("Time:       " + f'{time.localtime().tm_hour:02d} {time.localtime().tm_min:02d} {time.localtime().tm_sec:02d}')

asyncio.run(main())

Behavior

Eventually the board reboots into safe mode.

Auto-reload is off.
Running in safe mode! Not running saved code.

You are in safe mode because:
CircuitPython core code crashed hard. Whoops!
Hard fault: memory access or instruction error.
Please file an issue with your program at github.com/adafruit/circuitpython/issues.
Press reset to exit safe mode.

Press any key to enter the REPL. Use CTRL-D to reload.

Description

No response

Additional information

I hooked up a USB-TTL adapter to the TX pin on the board and got this. Is there anything that can be done to output more debug info?

ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x1 (POWERON),boot:0xb (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3818,len:0x138
load:0x403c9700,len:0x4
load:0x403c9704,len:0xa9c
load:0x403cc700,len:0x26d4
entry 0x403c9870
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x3 (RTC_SW_SYS_RST),boot:0xb (SPI_FAST_FLASH_BOOT)
Saved PC:0x4037cd5e
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3818,len:0x138
load:0x403c9700,len:0x4
load:0x403c9704,len:0xa9c
load:0x403cc700,len:0x26d4
entry 0x403c9870

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions