adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.11k stars 1.22k forks source link

ESP32-S3 hard fault with MQTT #8355

Open davesmeghead opened 1 year ago

davesmeghead commented 1 year ago

CircuitPython version

Adafruit CircuitPython 8.2.4 on 2023-08-22; Adafruit Feather ESP32-S3 Reverse TFT with ESP32S3
Board ID:adafruit_feather_esp32s3_reverse_tft

Code/REPL

import board
import digitalio
import busio
import asyncio
import binascii
import os
import time
import ssl
import socketpool
import wifi
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_httpserver import (
    Server,
    REQUEST_HANDLED_RESPONSE_SENT,
    Request,
    FileResponse,
    Response,
)

mqtt_address = os.getenv("MQTT_ADDRESS")
mqtt_port = int(os.getenv("MQTT_PORT"))      # Defined as int in the settings.toml file but convert to make sure
mqtt_username = os.getenv("MQTT_USERNAME")
mqtt_password = os.getenv("MQTT_PASSWORD")
mqtt_primary_topic = os.getenv("MQTT_TOPIC")
mqtt_cert_path = os.getenv("MQTT_SSL_CERT_PATH")
mqtt_key_path = os.getenv("MQTT_SSL_KEY_PATH")

print(f"Connecting to {os.getenv('WIFI_SSID')}")
wifi.radio.connect(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"))
print(f"Connected to {os.getenv('WIFI_SSID')}!")

### MQTT Feeds ###
mqtt_topic = mqtt_primary_topic + "/sensors"

# Setup a feed named 'onoff' for subscribing to changes
mqtt_feed = mqtt_primary_topic + "/onoff"

# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)
server = Server(pool, "/static", debug=True)

# Create the SSL Context
ssl_context = ssl.create_default_context()
if mqtt_cert_path is not None and mqtt_key_path is not None:
   ssl_context.load_cert_chain(certfile=mqtt_cert_path, keyfile=mqtt_key_path)

# Set up a MiniMQTT Client
if mqtt_username is not None and mqtt_password is not None:
    mqtt_client = MQTT.MQTT(
        broker=mqtt_address,
        port=mqtt_port,
        username=mqtt_username,
        password=mqtt_password,
        socket_pool=pool,
        ssl_context=ssl_context,
    )
else:
    mqtt_client = MQTT.MQTT(
        broker=mqtt_address,
        port=mqtt_port,
        socket_pool=pool,
        ssl_context=ssl_context,
    )

# Define callback methods which are called when events occur
# pylint: disable=unused-argument, redefined-outer-name
def connected(client, userdata, flags, rc):
    # This function will be called when the client is connected
    # successfully to the broker.
    print(f"Connected to Adafruit IO! Listening for topic changes on {mqtt_feed}")
    # Subscribe to all changes on the mqtt_feed.
    client.subscribe(mqtt_feed)

def disconnected(client, userdata, rc):
    # This method is called when the client is disconnected
    print("Disconnected from Adafruit IO!")

def message(client, topic, message):
    # This method is called when a topic the client is subscribed to
    # has a new message.
    print(f"New message on topic {topic}: {message}")

# Setup the callback methods above
mqtt_client.on_connect = connected
mqtt_client.on_disconnect = disconnected
mqtt_client.on_message = message

# Connect the client to the MQTT broker.
print("Connecting to MQTT broker...")
mqtt_client.connect()

uart = busio.UART(
    board.TX,
    board.RX,
    baudrate=9600,
    bits=8,
    parity=None,
    stop=1,
    timeout=0,
    receiver_buffer_size=256,
)

objects = [
    {"id": 1, "name": "Object 1"},
]

@server.route("/api", [GET, POST, PUT, DELETE], append_slash=True)
def api(request: Request):
    """
    Performs different operations depending on the HTTP method.
    """
    # Get objects
    if request.method == GET:
        return JSONResponse(request, objects)

    # Upload or update objects
    if request.method in [POST, PUT]:
        uploaded_object = request.json()
        # Find object with same ID
        for i, obj in enumerate(objects):
            if obj["id"] == uploaded_object["id"]:
                objects[i] = uploaded_object
                return JSONResponse(
                    request, {"message": "Object updated", "object": uploaded_object}
                )
        # If not found, add it
        objects.append(uploaded_object)
        return JSONResponse(
            request, {"message": "Object added", "object": uploaded_object}
        )
    # Delete objects
    if request.method == DELETE:
        deleted_object = request.json()
        # Find object with same ID
        for i, obj in enumerate(objects):
            if obj["id"] == deleted_object["id"]:
                del objects[i]
                return JSONResponse(
                    request, {"message": "Object deleted", "object": deleted_object}
                )
        # If not found, return error
        return JSONResponse(
            request, {"message": "Object not found", "object": deleted_object}
        )
    # If we get here, something went wrong
    return JSONResponse(request, {"message": "Something went wrong"})

@server.route("/")  # magic that attaches this function to "server" object
def base(request : Request):
    #my_str = f"<html><body><h1> Hello! Current time.monotonic is {time.monotonic()}</h1></body></html>"
    #return HTTPResponse(body=my_str, content_type="text/html")
    return Response(request, "Hello from the CircuitPython HTTP Server!")
    # return FileResponse(request, "index.html")

my_port = 80
server.start(str(wifi.radio.ipv4_address))
print(f"Listening on http://{wifi.radio.ipv4_address}:{my_port}")

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

fs_stat = os.statvfs('/')
print("Disk size in MB", fs_stat[0] * fs_stat[2] / 1024 / 1024)
print("Free space in MB", fs_stat[0] * fs_stat[3] / 1024 / 1024)

photocell_val = 0
led_toggler = False
mqtt_loop_counter = 0
while True:
    # Poll the mqtt message queue
    mqtt_client.loop(timeout=0.0)
    pool_result = server.poll()
    if pool_result == REQUEST_HANDLED_RESPONSE_SENT:
        # Do something only after handling a request
        pass

    mqtt_loop_counter = mqtt_loop_counter + 1
    if mqtt_loop_counter == 5:
        photocell_val += 1
        mqtt_client.publish(topic = mqtt_topic, msg = photocell_val, retain = False, qos = 1)
        mqtt_loop_counter = 0

    led_toggler = not led_toggler
    led.value = led_toggler

    # Process uart data
    data = None
    if uart.in_waiting > 0:
        print("@")
        data = uart.read()  # read
        print(data)  # this is a bytearray type
        if data is not None:
            # convert bytearray to string
            data_string = "".join([chr(b) for b in data])
            print(data_string, end="")
            uart.write(data)
        else:
            print("The uart said there was data but there isnt")
    else:
        print(".", end="")
        s = 'A0 00 00 00 99 99 00 00 00 00 00 43'.replace(' ', '')
        uart.write(binascii.unhexlify(s))

Behavior

CircuitPython core code crashed hard. Crikey! Hard fault: memory access or instruction error

Description

I'm just starting to mess about with it so the program is a mishmash of me trying things but I copied it verbatim

EDIT: After powering the board off and on I can't repeat the problem. I was using it for a long time before though. Also, I realise that I've missed out some imports for the HTTP but I wanted to copy the code as I tried it verbatim.

Additional information

No response

tannewt commented 1 year ago

Moved to long term because it wasn't reproducible by the original filer. Leaving it open in case someone else sees it.

davesmeghead commented 1 year ago

Hi, apologies and I should have updated this issue here but I do get this regularly: CircuitPython core code crashed hard. Crikey! Hard fault: memory access or instruction error

Although I'm only developing the software for the ESP32-S3 at the moment and not using it properly so I assumed that this is a regular occurance and was a known problem being worked on. I also have a lot of python code in at the moment and it's difficult to upload it all but I have no problem with doing that if you want me to. Is it possible that I have a faulty board? If it isn't then I'll try and get something that is repeatable and upload the code here. Is there more information about the problem that I can do with CircuitPython to get it to output more details? Thanks

tannewt commented 1 year ago

Is it possible that I have a faulty board?

I doubt it. These errors are usually a software problem.

If it isn't then I'll try and get something that is repeatable and upload the code here.

A small, reproducible code snippet is always best for debugging.

Is there more information about the problem that I can do with CircuitPython to get it to output more details?

You can build a version of CircuitPython that will output the ESP-IDF logging over UART and it'll include a backtrace. See the build instructions and add DEBUG=1. The #circuitpython-dev channel on the Adafruit Discord is the best place for help getting the build going.