circuitpython: Some ESP32-S2 8.0.0-beta code enters safe mode. 7.3.3 does not. Pico W does not.

CircuitPython version

Adafruit CircuitPython 8.0.0-beta.6-40-gcc6dbb373 on 2023-01-16; Adafruit QT Py ESP32S2 with ESP32S2
# and earlier betas... not sure when it started, but at least back to October or so

Code/REPL

import time
import random
import traceback
import board
import digitalio
import neopixel
import wifi
import socketpool
import ssl
from adafruit_httpserver.server import HTTPServer
from adafruit_httpserver.request import HTTPRequest
from adafruit_httpserver.response import HTTPResponse
from adafruit_httpserver.methods import HTTPMethod
from adafruit_httpserver.mime_type import MIMEType
import adafruit_requests

from secrets import secrets


PORT = 8080
URLS = ("http://wifitest.adafruit.com/testwifi/index.html",
        "https://httpbin.org/get", )
ONE_SECOND_NS   =   1_000_000_000
FIVE_MINUTES_NS = 300_000_000_000


def get_led():
    led = None
    if hasattr(board, "LED"):
        led = digitalio.DigitalInOut(board.LED)
    else:
        print(f"No LED")
    if led: led.switch_to_output(False)
    return led

def get_pix():
    pix = []
    num_pix = 1
    if hasattr(board, "NEOPIXEL"):
        import neopixel
        pix = neopixel.NeoPixel(board.NEOPIXEL, num_pix, brightness=0.25, pixel_order=neopixel.GRB)
    elif hasattr(board, "APA102_SCK"):  # UMFeatherS2; FunHouse & Itsy Bitsy M4 also have DOTSTAR_CLOCK
        import adafruit_dotstar
        pix = adafruit_dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, num_pix, brightness=0.25)
    else:
        print(f"No NeoPixel. No Dotstar")
    if pix: pix[0] = 0
    return pix

def connect():
    if pix: pix[0] = (0, 255, 255)  # green-blue when connecting
    print(f"{time.monotonic_ns()} Connected? ", end="")
    consecutive_connect_errors = 0
    while not wifi.radio.ipv4_address:  # check for 0.0.0.0 ...
        try:
            print(f"Connecting... ", end="")
            wifi.radio.connect(secrets["ssid"], secrets["password"])
        except ConnectionError as e:
            consecutive_connect_errors += 1
            print(f"ConnectionError: {e} consecutive={consecutive_connect_errors}", end=" ")
            time.sleep(1)
            if consecutive_connect_errors > 8:
                print(f"Too many consecutive connect failures. Reloading...")
                supervisor.reload()
    time.sleep(0.0100)  # Pico W wifi.radio.ipv4_address can lag wifi.radio.connect by tens of ms
    print(f"{wifi.radio.ipv4_address} ", end="")
    if hasattr(wifi.radio, "ap_info.rssi") and wifi.radio.ap_info:
        time.sleep(1)  # ap_info takes a moment to be valid
        print(f"RSSI={wifi.radio.ap_info.rssi} ", end="")
    print()
    if pix: pix[0] = 0

def poll():
    if not wifi.radio.ipv4_address:
        connect()

    if pix: pix[0] = (255, 0, 255)  # violet for polling
    try:
        server.poll()
    except Exception as ex:
        traceback.print_exception(ex, ex, ex.__traceback__)
    if pix: pix[0] = 0

def get_url(url):
    if not wifi.radio.ipv4_address:
        connect()

    if pix: pix[0] = (0, 0, 255)  # blue for requests
    try:
        with requests.request("GET", url, timeout=5,) as r:
            print(f"\n{time.monotonic_ns()} {r.status_code} {r.reason.decode('utf-8', 'ignore')} {r.headers}")
    except Exception as e:
        traceback.print_exception(e, e, e.__traceback__)
    if pix: pix[0] = 0



led = get_led()
pix = get_pix()

connect()
pool = socketpool.SocketPool(wifi.radio)
server = HTTPServer(pool)
server.start(str(wifi.radio.ipv4_address), port=PORT)
requests = adafruit_requests.Session(pool, ssl.create_default_context())


@server.route("/")
def base(request: HTTPRequest):
    with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response:
        print(f"\n{time.monotonic_ns()} Serving route...")
        response.send_file("index.html")


one_second_start = time.monotonic_ns() - ONE_SECOND_NS
five_minute_start = time.monotonic_ns() - FIVE_MINUTES_NS
while True:

    if (time.monotonic_ns() - one_second_start) >= ONE_SECOND_NS:
        if led: led.value = not led.value
        print(f"{time.monotonic_ns()} Listening on http://{wifi.radio.ipv4_address}:{PORT}", end="\r")
        one_second_start = time.monotonic_ns()

    try:
        poll()
    except OSError as e:
        traceback.print_exception(e, e, e.__traceback__)

    if (time.monotonic_ns() - five_minute_start) >= FIVE_MINUTES_NS:
        print(f"\n{time.monotonic_ns()} Requesting url...")
        url = random.choice(URLS)
        get_url(url)
        five_minute_start = time.monotonic_ns()

Behavior

8.0.0-beta code on ESP2-S2 enters safe mode under certain circumstances I can’t identify exactly.

Same projects on ESP32-S2 7.3.3 never enter safe mode. Same projects on Pico W never enter safe mode.

Characteristics of projects entering safe mode:

  • always involves httpserver (possibly httpserver combined with requests)
  • several relatively-complex requests-only (no httpserver) projects never enter safe mode
  • doesn’t seem to matter if httpserver ever serves any routes
  • web workflow on or off doesn’t seem to matter (lately I’ve been testing with web workflow off)
  • async or not doesn’t seem to matter, though for a long time I though it might since one async webclient-only (no httpserver) project also goes into safe mode (lately I’ve been testing projects reverted away from async)
  • presence or absence of boot.py doesn’t seem to matter
  • library versions don’t seem to matter - this has been occurring for months (latest tests using 20230110)
  • various versions and rewrites to try to get a minimal example all go into safe mode - finally got a shareable code example (above) that’s still somewhat large and probably has features it doesn’t need, but when I’ve made it too simple it doesn’t enter safe mode (I’ll keep trying to simplify the example)

Behavior: after some indeterminate duration (typically some hours)…

Usually:

You are in safe mode because: CircuitPython core code crashed hard. Whoops! Crash into the HardFault_Handler.

Occasionally:

You are in safe mode because: Internal watchdog timer expired.

I just started up 4 with the above code a half hour ago, fresh from a reset, and three have crashed: two hardfault and one internal watchdog. It always enters safe mode after the “Listening on http://…” print statement, most likely in server.poll(), and if so then most likely in _sock.accept().

Description

No response

Additional information

No response

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 28 (16 by maintainers)

Most upvoted comments

Please re-test after idf 5 upgrade