adafruit / circuitpython

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

CircuitPython crashes with Internal watchdog timer expired. #9428

Open wbeebe opened 1 month ago

wbeebe commented 1 month ago

CircuitPython version

Adafruit CircuitPython 9.1.0 on 2024-07-10; ESP32-S3-DevKitC-1-N32R8 with ESP32S3
Board ID:espressif_esp32s3_devkitc_1_n32r8
UID:C7FD1A1E82C7

Code/REPL

import os
import rtc
import time
import ipaddress
import wifi
import socketpool
import board
import microcontroller
import adafruit_ntp
import neopixel
import binascii

from adafruit_httpserver import Server, Request, Response, POST
#
# Global setup
#
SYSNAME = os.uname().sysname
UNIQUE_ID = binascii.hexlify(microcontroller.cpu.uid).decode('utf-8').upper()
SSID = SYSNAME + '-' + UNIQUE_ID[-4:]
led = neopixel.NeoPixel(board.IO38, 1)
led.fill(0x000000)
#
# Connect to a local WiFi access point.
# Because this code is running in a CircuitPython 9 or later environment,
# it requires a settings.toml file be created with at least the following
# six lines:
#
# AP_SSID = "WIFI SSID"
# AP_PASSWORD = "WIFI PASSWORD"
# STATIC_IP = "IP4 dotted IP address"
# GATEWAY = "192.168.0.1"
# TZ_OFFSET = -4
# DEBUG = "True" 
#
# DEBUG can be either True or False.
#
wifi.radio.hostname = SSID
wifi.radio.connect(os.getenv('AP_SSID'), os.getenv('AP_PASSWORD'))
pool = socketpool.SocketPool(wifi.radio)
ntp = adafruit_ntp.NTP(pool, tz_offset = os.getenv('TZ_OFFSET'))
rtc.RTC().datetime = ntp.datetime
server = Server(pool, "/static", debug=os.getenv('DEBUG'))

def get_date():
    dayname = [
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
        "Sunday",]

    monthname = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December" ]

    try:
        now = time.localtime()
        return f"{dayname[now.tm_wday]}, {now.tm_mday} {monthname[now.tm_mon-1]} {now.tm_year} - {now.tm_hour:02}:{now.tm_min:02}:{now.tm_sec:02}"
    except Exception as e:
        return f" >>> EXCEPTION {e}"

# The HTML page body is a Python 3 'f' string for advanced formatting.
#
# Double curly braces {{ and }} are used when HTML needs single braces for HTML
# page elements such as CSS styling.
#
def webpage():
    global SSID
    font_family = "sans-serif"
    html = f"""
    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-type" content="text/html;charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" href="data:," />
    <style>
    html{{
        font-family: {font_family};
        background-color: #FFFFFF;
        display:inline-block;
        margin: 0px auto;
        text-align: center;
        }}
    h1{{
        color:#D35F8D;
        word-wrap: break-word;
        font-size: 35px;
        }}
    h2{{
        color:#D35F8D;
        word-wrap: break-word;
        font-size: 20px;
        }}
    p{{
        font-size: 1.5rem;
        word-wrap: break-word;
        }}
    button{{
        font-family: {font_family};
        display: inline-block;
        width: 99%;
        border: none;
        border-radius: 4px;
        color: white;
        padding: 16px 40px;
        text-decoration: none;
        font-size: 30px;
        margin: 2px;
        cursor: pointer;
    }}
    .button-red{{
        background-color: #DC143C;
        }}
    .button-green{{
        background-color: #228B22;
        }}
    .button-dark-gray{{
        background-color: #404040;
        }}
    p.dotted {{
        margin: auto;
        width: 90%;
        font-size:
        25px;
        text-align: center;
        }}
    </style>
    </head>
    <body>
        <title>{SSID} LED Control</title>
        <h1>{SSID}</h1>
        <form accept-charset="utf-8" method="POST">
        <button class="button-green" name="LED ON" value="ONG" type="submit">LED ON</button></a>
        <button class="button-red" name="LED ON" value="ONR" type="submit">LED ON</button></a>
        <button class="button-dark-gray" name="LED OFF" value="OFF" type="submit">LED OFF</button></a>
        </form>
        <h2>CircuitPython {os.uname().version}<br/>
        {get_date()}</h2>
    </body>
    </html>
    """
    return html

# Default route
#
@server.route("/")
def base(request: Request):  # pylint: disable=unused-argument
    return Response(request, webpage(), content_type='text/html')

# Determine which button was pressed and
# execute that button's associated action.
#
@server.route("/", POST)
def buttonpress(request: Request):
    raw_text = request.raw_request.decode("utf8")
    #print(raw_text)
    #
    #
    if "ONG" in raw_text:
        led.fill(0x006400)
    #
    #
    if "ONR" in raw_text:
        led.fill(0x640000)
    #
    # If the led off button was pressed...
    if "OFF" in raw_text:
        led.fill(0x000000)

    return Response(request, webpage(), content_type='text/html')

try:
    server.start(str(wifi.radio.ipv4_address))
    print(f" Listening on http://{wifi.radio.ipv4_address}")
#
# If the server fails to start then reset the microprocessor.
# If there is a hardware failure then this can put the software into a
# boot loop. That's why the time.sleep(5) is there so that a Ctrl C can exit
# at the REPL during development.
#
except OSError as e:
    print(f" !!! OSError: {e}")
    print(" !!! OSError Exception, Waiting !!!")
    time.sleep(5)
    print(" !!! OSError Exception, Restarting !!!")
    microcontroller.reset()
#
# DO NOT hard code the while loop as True,
# instead provide a test and exit if something goes wrong.
#
is_healthy = True;

while is_healthy:
    try:
        # Poll for incoming requests.
        server.poll()
    except Exception as e:
        print(f' EXCEPTION {e}')
        is_healthy = False
        continue

Behavior

When left to run unattended the board will stop executing, and any attempt to perform ^D to restart returns the following message at the REPL:

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

You are in safe mode because: Internal watchdog timer expired. Press reset to exit safe mode.

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

Description

Not only does this occur with the Espressif ESP32-S3 board, but it also occurs with the Raspberry Pi Pico and Pico/W with the same version of CircuitPython. It will also occur with CircuitPython 9.0.5, again with the boards listed above.

Additional information

No response

dhalbert commented 1 month ago

Thanks. Is the failure also Internal watchdog timer expired. on the Pico W?

The plain Pico doesn't have wifi, so how did you make this run on the Pico? Or is that a typo?

How long does it take for the safe mode failure to happen?

Are you using the latest versions of the needed libraries?

wbeebe commented 1 month ago

Thanks. Is the failure also Internal watchdog timer expired. on the Pico W?

Yes it is.

The plain Pico doesn't have wifi, so how did you make this run on the Pico? Or is that a typo?

It is a Pico/W only issue. I'm so used to including both versions of the Pico when I write about them. My apologies.

How long does it take for the safe mode failure to happen?

I can only say overnight. For example I went to bed around 11pm after checking it was still operational, then when I got up this morning a little after 6 am it was in safe mode.

Are you using the latest versions of the needed libraries?

Yes. Whenever I update I always pull the version of the libraries that match the CircuitPython version as well as the release date.

wbeebe commented 1 month ago

BTW, this is what the code looks like when it's running. A view of the web page on my iPhone. Nothing fancy at all because I stripped out a lot for this test code.

IMG_5503

dhalbert commented 1 month ago

Could you post a redacted (hide password, SSID, etc.) version of your settings.toml? I am interested in whether you have any CIRCUITPY_... settings, or whether you only have the keys referenced above.

wbeebe commented 1 month ago
AP_SSID = "SSID"
AP_PASSWORD = "PASSWORD"
TZ_OFFSET = -4
DEBUG = "False"

Just the four shown above. STATIC_IP and GATEWAY aren't needed, and I need to adjust the comment block accordingly.