Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTPS implementation #88

Merged
merged 13 commits into from
Dec 29, 2024
Merged

Conversation

michalpokusa
Copy link
Contributor

@michalpokusa michalpokusa commented Feb 23, 2024

Depends on #84, #87 and adafruit/circuitpython#9003

⭐ Added:

🛠️ Updated/Changed:

  • Server constructor now accepts https, certfile and keyfile args that together enable HTTPS
  • In debug mode, time it took server to handle the request is now measured from before .accept(), which includes the time of handling TLS decryption, thus the time is more "correct"
  • Docs now include info about HTTPS

🏗️ Refactor:

  • Some parts of docs that used to have bolded paragraphs not have them in Sphinx's notes

After fixing the issues mentioned at the top, this code should also work on other platforms than ESP32-S3.

@dhalbert
Copy link
Contributor

@michalpokusa I'm sorry we missed merging this. Is it ready to go? Any retesting needed since various other library changes? Thanks.

@michalpokusa
Copy link
Contributor Author

@michalpokusa I'm sorry we missed merging this. Is it ready to go? Any retesting needed since various other library changes? Thanks.

No problem. I didn't push on merging this as although this works (or did when I marked it as ready) on ESP32-S3, it is not compatible with RPico, S2 etc. Recently there were also some changes to the SSL module on CircuitPython, so I was waiting on how things play out.

I am not sure whether the MemoryError is fixed on other MCUs or if there are plans to resolve them in future.

Meanwhile this PR can be merged as is or I could add a warning that prints info about compatibility or checks the board id, or I can convert it to draft for now. Please tell me what would be best in your opinion.

Retest is always good, if you decide to merge I will do it with latest CircuitPython.

@anecdata
Copy link
Member

anecdata commented Dec 14, 2024

I would be nice to get this merged so it can be used easily with boards that have enough memory (primarily ESP32-S3, but also perhaps some raspberrypi boards with enough memory. Some mention of the memory issue in documentation would help.

@michalpokusa
Copy link
Contributor Author

I would be nice to get this merged so it can be used easily with boards that have enough memory (primarily ESP32-S3, but also perhaps some raspberrypi boards with enough memory. Some mention of the memory issue in documentation would help.

I will retest everything with latest CircuitPython and make sure it works as before. Will also include info about th memory in docs near HTTPS example.

@michalpokusa
Copy link
Contributor Author

@anecdata I think we are good to go with this PR.

@anecdata
Copy link
Member

Sounds good! I should be able to do some testing this weekend.

@anecdata
Copy link
Member

anecdata commented Dec 21, 2024

Brief testing so far on:

  • ESP32-S2:
    Adafruit CircuitPython 9.2.1 on 2024-11-20; Adafruit Feather ESP32-S2 TFT with ESP32S2
    As expected, MemoryError:

  • ESP32-S3:
    Adafruit CircuitPython 9.2.1 on 2024-11-20; Adafruit Feather ESP32-S3 TFT with ESP32S3
    OK generally, but crashes (ejects, serial disconnects, restarts) sometimes while listening, and saw one safe mode. Will do more testing on this.
    Responses in about 300-400ms

  • Raspberry Pi Pico with Ethernet:
    Adafruit CircuitPython 9.2.1 on 2024-11-20; W5500-EVB-Pico with rp2040
    Adafruit_CircuitPython_Wiznet5 (v7.2.0)
    I don't know if this has been tested. Responds once, slowly (~15s), then usually some issues. May look at this more, but memory headroom is low (<30KB free in the polling loop).

  • Raspberry Pi Pico 2 with Ethernet:
    Adafruit CircuitPython 9.2.1 on 2024-11-20; W5100S-EVB-Pico2 with rp2350a
    Adafruit_CircuitPython_Wiznet5 (v7.2.0)
    Similar results to Raspberry Pi Pico with Ethernet, but with much more memory headroom. Faster first response, <2s.

  • Pico W:
    Adafruit CircuitPython 9.2.1 on 2024-11-20; Raspberry Pi Pico W with rp2040
    As expected, MemoryError:

  • Pico 2 W:
    Adafruit CircuitPython 9.2.1 on 2024-11-20; Raspberry Pi Pico 2 W with rp2350a
    Responses in about 1.5-2.25 seconds. Sometimes takes a long time to connect (* Trying 192.168.6.79:5000...), or times out but the server is still polling (control-C traceback)

  • Pimoroni Pico Plus 2 W:
    Adafruit CircuitPython 9.2.1-36-g00562a307a on 2024-12-17; Pimoroni Pico Plus 2 W with rp2350b
    As expected, works just like Pico 2 W (including some long connect times and timeouts), but with more memory.

Client:
curl -ikLv https://192.168.6.79:5000 (-k == --insecure; skip cert checking on the self-signed cert)

Typical client output
*   Trying 192.168.6.79:5000...
* Connected to 192.168.6.79 (192.168.6.79) port 5000
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 / [blank] / UNDEF
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: CN=picow.local
*  start date: Feb 28 05:42:56 2023 GMT
*  expire date: Feb 28 05:42:56 2024 GMT
*  issuer: CN=picow.local
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* using HTTP/1.x
> GET / HTTP/1.1
> Host: 192.168.6.79:5000
> User-Agent: curl/8.7.1
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< content-length: 36
content-length: 36
< content-type: text/plain
content-type: text/plain
< connection: close
connection: close
< 

* Closing connection
CP79PLUS2W t=38638610852 mem=8371232
HTTPS Server code.py
import time
import os
import storage
import gc
import board
import digitalio
import traceback
import ssl
import adafruit_connection_manager
from adafruit_httpserver import Server, Request, Response, REQUEST_HANDLED_RESPONSE_SENT


time.sleep(3)  # wait for serial
print(f"{gc.mem_free()=}")

try:  # native wifi: espressif or raspberypi
    import wifi
    import socketpool

    wifi.radio.connect(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"))
    radio = wifi.radio
    ipv4_address = str(wifi.radio.ipv4_address)
except ImportError:
    from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K

    spi = board.SPI()
    # WIZnet EVB-Pico or EVB-Pico2 (except W55RP20)
    cs = digitalio.DigitalInOut(board.GP17)
    rst = digitalio.DigitalInOut(board.GP20)
    radio = WIZNET5K(spi, cs, reset=rst, mac='de:ad:be:ef:fe:ee', debug=False)
    ipv4_address = radio.pretty_ip(radio.ip_address)

pool = adafruit_connection_manager.get_radio_socketpool(radio)


server = Server(
    pool,
    root_path="/static",
    https=True,
    certfile="cert.pem",
    keyfile="key.pem",
    debug=True,
)


@server.route("/")
def base(request: Request):
    resp = f"{storage.getmount("/").label} t={time.monotonic_ns()} mem={gc.mem_free()}"
    print(resp)
    return Response(request, resp)


server.start(ipv4_address)
while True:
    try:
        server.poll()
    except Exception as ex:
        traceback.print_exception(ex, ex, ex.__traceback__)

Typical server output:

gc.mem_free()=8333088
Started development server on https://192.168.6.79:5000
CP79PLUS2W t=21965820312 mem=8371136
192.168.5.10 -- "GET /" 80 -- "200 OK" 120 -- 2262ms

@michalpokusa
Copy link
Contributor Author

I tested on ESP32-S2, ESP32-S3 and Pico W, I do not own Pico with Ethernet or any of the Pico 2.

  • ESP32-S2: same results as you, crash loop with safemode.py with microcontroller.reset()

  • ESP32-S3: I had errors that were caused by the code itself, as I was missing the try/except block. I had shorter response times, but my AP was right next to me, so maybe that's why.

  • Pico W: same results as you.

  • CPython: generally works, some parts of the code raise CPython specific errors, but they can be catched in the code and it still works.

I used the MEMENTO board for testing ESP32-S3, and Feather ESP32-S2 TFT.

Maybe using the .mpy version of the lib would be more stable?
adafruit/circuitpython#8736 (comment)

@anecdata
Copy link
Member

anecdata commented Dec 22, 2024

HTTP

Just to check any regressions, ran all 7 configs above with http:// and all seem to be working fine and reasonably fast, will let them run all night then get back to HTTPS.

update: The espressif and ethernet devices still running this morning, just a few non-fatal exceptions that were automatically recovered in code. All 3 raspberrypi devices got hung up seemingly coinciding with a loss of wifi connection and re-connection. Perhaps it's expected to restart the server in that case, but reload got them going again reset was ultimately required.

HTTPS

I'll make a note to test with .mpy of this library for HTTPS, I suspect it would make the most difference on ESP32-S2, Pico [1] W, and Pico [1] with Ethernet.

Pico W did work with CircuitPython 8.0.0, albeit slowly, but now doesn't have enough RAM. Could be a combination of factors that are contributing to insufficient RAM. I'm not aware of any Pico W variants with extra PSRAM. Pico W may be a lost cause to run more than a simple TLS socket server.

Ethernet: I don't know that HTTPS Server has ever been tested on Ethernet boards, so there could be any number of issues in that stack. Hopefully fixable in the WIZnet library.

ESP32-S2 (4MB PSRAM) lack of memory is perhaps something to do with how RAM is partitioned / allocated in circuitpython 9, and between circuitpython and esp-idf... technically 4MB PSRAM should be plenty if a larger chunk was usable by esp-idf for sockets and such, and perhaps some other uses.

ESP32-S3 (4MB PSRAM, but more SRAM than ESP32-S2) should work. It worked early in the year, but there have been updates to esp-idf, and to the core. Hard fault safemode points to something deeper than this library.

RP2350-based Pico 2 W & Pimoroni Pico Plus 2 W look promising, both seem to have plenty of memory. But the boards and SDK changes are new, and there could be some lingering socket or related issues causing the delays and timeouts. Sometimes it can recover from a timeout, other times it requires restart.

@anecdata
Copy link
Member

anecdata commented Dec 22, 2024

To try to isolate where the issues may lie, I'm now bypassing the library and running a relatively simple TLS socket server mimicking HTTPS.

The ESP32-S2 still gets MemoryError:.

The Ethernet boards sometimes work, but usually timeout (often from the sever perspective the response completes, but the client times out anyway). So for now, I'm focusing on the ESP32-S3 and the raspberrypi boards. (Looks like ethernet HTTPS serving was at least briefly checked in the past, but with some issues I don't recall.)

Client has been the curl commands from earlier comment, or long runs of this code:

Client code.py
#!/usr/bin/env python3

import time
import sys
import requests
import traceback

def duration(start):
    return time.time() - start

while True:
    
    for last in (251, 190, 252, 198, 56, 79,):  # No S2 (memory)
        start = time.time()
        try:
            url = f"https://192.168.6.{str(last)}:5000"
            print(f"Connecting to {url} ...")
            with requests.get(url, timeout=(30, 60), verify=False) as resp:
                print(f"{resp.status_code} {resp.reason} {resp.content}")
            print(f"🟢 Completed in {duration(start):.3f}s")
        except requests.exceptions.Timeout as ex:
            print(f"🔴\aTimeout in {duration(start):.3f}s")
        except Exception as ex:
            print(f"🔴\a")
            traceback.print_exception(ex, ex, ex.__traceback__)
    time.sleep(5)
Minimal HTTP TLS socket server code.py
import time
import gc
import os
import rtc
import storage
import traceback
import ssl
import adafruit_connection_manager
import adafruit_ntp


HOST = ""  # see below
PORT = 5000
BACKLOG = 2
MAXBUF = 256


def connect(radio):
    if radio.__class__.__name__ == "Radio":
        while not radio.connected:
            try:
                radio.connect(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"))
                print(f"Connected: {radio.ipv4_address}")
            except Exception as ex:
                traceback.print_exception(ex, ex, ex.__traceback__)
        return str(wifi.radio.ipv4_address)
    elif radio.__class__.__name__ == "WIZNET5K":
        ipv4_address = radio.pretty_ip(radio.ip_address)
        print(f"Connected: {radio.ipv4_address}")
        return ipv4_address
    return None

def create_response(body):
    r  = "HTTP/1.1 200 OK\r\n"
    r += f"Content-length: {len(body)}\r\n"
    r += "\r\n"
    r += body
    return r.encode()


time.sleep(3)  # wait for serial
print(f"{gc.mem_free()=}")
try:  # native wifi: espressif or raspberypi
    import wifi
    import socketpool

    radio = wifi.radio
except ImportError:
    import board
    import digitalio
    from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K

    spi = board.SPI()
    # WIZnet EVB-Pico or EVB-Pico2 (except W55RP20)
    cs = digitalio.DigitalInOut(board.GP17)
    rst = digitalio.DigitalInOut(board.GP20)
    radio = WIZNET5K(spi, cs, reset=rst, mac='de:ad:be:ef:fe:ed', debug=False)

pool = adafruit_connection_manager.get_radio_socketpool(radio)
ipv4_address = connect(radio)
HOST = ipv4_address

ntp = adafruit_ntp.NTP(pool, tz_offset=0)
while True:
    ipv4_address = connect(radio)
    try:
        rtc.RTC().datetime = ntp.datetime
        print(f"{time.localtime(time.time() - 6*60*60)}")
        break
    except Exception as ex:
        traceback.print_exception(ex, ex, ex.__traceback__)
        time.sleep(5)

print("Create TCP Server socket", (HOST, PORT))
s = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
s.setsockopt(pool.SOL_SOCKET, pool.SO_REUSEADDR, 1)
ssl_context = ssl.create_default_context()
ssl_context.load_verify_locations(cadata="")
ssl_context.load_cert_chain("cert.pem", "key.pem")
ssl_context.check_hostname = False
ss = ssl_context.wrap_socket(s, server_side=True)
ss.bind((HOST, PORT))
ss.listen(BACKLOG)
# ss.setblocking(False)
print("Listening\n")

buf = bytearray(MAXBUF)
while True:
    try:
        ipv4_address = connect(radio)

        print(f"Accepting connections (mem={gc.mem_free()})")
        conn, addr = ss.accept()
        t = time.localtime(time.time() - 6*60*60)
        print(f"Accepted from {addr} at {t}")
        conn.settimeout(None)
        size = conn.recv_into(buf, MAXBUF)
        print(f"Received {buf[:size]} {size} bytes")

        body = f"{storage.getmount("/").label} {ipv4_address} {gc.mem_free()=} {t}"
        r = create_response(body)
        conn.send(r)
        print(f"Sent {r}\n")

        conn.close()
    except Exception as ex:
        traceback.print_exception(ex, ex, ex.__traceback__)

The ESP32-S3 and the raspberrypi boards seem to be behaving well. Overall time from the client perspective are:
• ESP32-S3: 350-500 msec
• PicoW: 4.5-5.5 sec
• Pico2W boards: 1.5-2.5 sec

I haven't seen a safemode yet, which baffles me since the code sequences are basically cribbed from here (which in turn is cribbed from some jepler code I can no longer find), and from this library. I'll let it run for some time longer, then go back to testing the library on these boards.

Update: Adding a Content-length header to the response seems to have helped the Ethernet boards (still a few scattered timeouts), not sure why requests behaves differently between the wifi boards and the Ethernet boards. Overall time from the client perspective are:
• Pico+WIZ: 4-5 sec
• Pico2+WIZ: 1.5-2.5 sec

@anecdata
Copy link
Member

anecdata commented Dec 22, 2024

Back to the library and this PR... for whatever reason, HTTPS Server is behaving better today using the requests client from above, and updated server code:

HTTPS Server code.py
import time
import rtc
import os
import storage
import gc
import traceback
import ssl
import adafruit_connection_manager
import adafruit_ntp
from adafruit_httpserver import Server, Request, Response


def connect(radio):
    if radio.__class__.__name__ == "Radio":
        while not radio.connected:
            try:
                radio.connect(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"))
                print(f"Connected: {radio.ipv4_address}")
            except Exception as ex:
                traceback.print_exception(ex, ex, ex.__traceback__)
        return str(wifi.radio.ipv4_address)
    elif radio.__class__.__name__ == "WIZNET5K":
        ipv4_address = radio.pretty_ip(radio.ip_address)
        if not radio.ip_address:
            print(f"Connected: {radio.ipv4_address}")
        return ipv4_address
    return None


time.sleep(3)  # wait for serial
print(f"{gc.mem_free()=}")
try:  # native wifi: espressif or raspberypi
    import wifi
    import socketpool

    radio = wifi.radio
except ImportError:
    import board
    import digitalio
    from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K

    spi = board.SPI()
    # WIZnet EVB-Pico or EVB-Pico2 (except W55RP20)
    cs = digitalio.DigitalInOut(board.GP17)
    rst = digitalio.DigitalInOut(board.GP20)
    radio = WIZNET5K(spi, cs, reset=rst, mac='de:ad:be:ef:fe:ed', debug=False)

pool = adafruit_connection_manager.get_radio_socketpool(radio)

ntp = adafruit_ntp.NTP(pool, tz_offset=0)
while True:
    ipv4_address = connect(radio)
    try:
        rtc.RTC().datetime = ntp.datetime
        break
    except Exception as ex:
        traceback.print_exception(ex, ex, ex.__traceback__)
        time.sleep(5)

server = Server(
    pool,
    root_path="/static",
    https=True,
    certfile="cert.pem",
    keyfile="key.pem",
    debug=True,
)


@server.route("/")
def base(request: Request):
    resp = f"{storage.getmount("/").label} {ipv4_address} {gc.mem_free()=} {time.localtime(time.time() - 6*60*60)}"
    print(resp)
    return Response(request, resp)


ipv4_address = connect(radio)
server.start(ipv4_address)
while True:
    try:
        ipv4_address = connect(radio)
        server.poll()
    except Exception as ex:
        traceback.print_exception(ex, ex, ex.__traceback__)

No safemode observed yet. Leaving off ESP32-S2 and Pico W due to MemoryError:

Initially trying requests to each board every 5+ seconds:

ESP32-S3:

Looking good so far.

Pico 2 W boards:

Looking good so far.

Ethernet:

The two Ethernet boards are generally OK, with a couple of low-level issues:

[tio 17:29:34] Connected
gc.mem_free()=371680
Started development server on https://192.168.6.251:5000
CPW2350A 192.168.6.251 gc.mem_free()=262816 struct_time(tm_year=2024, tm_mon=12, tm_mday=22, tm_hour=17, tm_min=30, tm_sec=28, tm_wday=6, tm_yday=357, tm_isdst=-1)
199.180.255.17 -- "GET /" 149 -- "200 OK" 248 -- 45385ms
CPW2350A 192.168.6.251 gc.mem_free()=262848 struct_time(tm_year=2024, tm_mon=12, tm_mday=22, tm_hour=17, tm_min=31, tm_sec=53, tm_wday=6, tm_yday=357, tm_isdst=-1)
0.0.0.0 -- "GET /" 149 -- "200 OK" 248 -- 84983ms
CPW2350A 192.168.6.251 gc.mem_free()=262816 struct_time(tm_year=2024, tm_mon=12, tm_mday=22, tm_hour=17, tm_min=32, tm_sec=9, tm_wday=6, tm_yday=357, tm_isdst=-1)
192.168.5.10 -- "GET /" 149 -- "200 OK" 247 -- 16513ms
CPW2350A 192.168.6.251 gc.mem_free()=262816 struct_time(tm_year=2024, tm_mon=12, tm_mday=22, tm_hour=17, tm_min=33, tm_sec=34, tm_wday=6, tm_yday=357, tm_isdst=-1)
192.168.5.10 -- "GET /" 149 -- "200 OK" 248 -- 84443ms

The first couple of client IP addresses are wrong (the first one I think is the address from the NTP server), I think this is coming from the WIZnet library. Also, the reported transaction durations are way off - client reports these in under 5 seconds.

Traceback (most recent call last):
  File "adafruit_httpserver/server.py", line 476, in poll
OSError: [Errno 22] Invalid argument
Traceback (most recent call last):
  File "code.py", line 82, in <module>
  File "adafruit_httpserver/server.py", line 528, in poll
  File "adafruit_httpserver/server.py", line 476, in poll
OSError: [Errno 22] Invalid argument

Encountered these exceptions after a few successful responses, but the server recovered automatically and handled subsequent requests fine.

Also, occasional Timeouts on the Ethernet boards (after ~30s, which is the requests connect timeout kwarg).

Update: I removed the NTP call in case that was that was making a difference from the first test, and let the boards run all night. ESP32-S3, both Ethernet boards, and both Pico 2 W boards, ran fine all night. Some auto-recovered timeouts on the Ethernet boards. No safemodes on the S3, no timeouts on the Pico 2 Ws, or any other issues.

Update: I left each server board running (polling / listening) for over an hour, but without any clients trying to connect. No issues.

Copy link
Member

@anecdata anecdata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went back to the original code:
#88 (comment)

No client running, and the ESP32-S3 goes into safemode relatively soon, and frequently again after reset. safemode.py (which writes debug data to disk and resets) yields:
supervisor.SafeModeReason.WATCHDOG
microcontroller.ResetReason.WATCHDOG

So I think there is something here, though don't see a material difference between this code and the later test code (even without NTP) that had no issues.

Update: Ran the httpserver_start_and_poll.py with the addition of import os and explicit wifi connect, and the changes in the Server init for HTTPS. No client running. It also regularly goes into safemode every 1-5 minutes. safemode.py yields:
supervisor.SafeModeReason.HARD_FAULT
microcontroller.ResetReason.SOFTWARE

Not seeing any issues on the two RP2350 wifi boards running the original test code with no clients.

@michalpokusa
Copy link
Contributor Author

@anecdata
Thank you very much for your extensive testing.

Do you think the issue is somewhere in the adafruit_httpserver or somewhere on the lower level in CP itself?

There is little to none information about the specific reason, and I am not as familiar with CP to determine it from your results.

@anecdata
Copy link
Member

anecdata commented Dec 23, 2024

I would think any entry into safemode is ultimately due to circuitpython core or lower-level code (included SDK/ESP-IDF, MBEDTLS, or other modules). They are hard to debug.

edit: ...or possibly disk corruption. I'm trying it on the ESP32-S3, freshly reformatted (storage.erase_filesystem()) and CP 9.2.1 re-installed. If that still fails, I'll try it next on an alternate ESP32-S3 device.

@anecdata
Copy link
Member

The cleaned ESP32-S3 is still going into safemode. I'll try an alternate device.

@michalpokusa Can you try this exact file on your ESP32-S3? It will need appropriate settings.toml. No clients need to be trying to connect.
code.py.txt

@anecdata
Copy link
Member

Good news: I can't reproduce it on a brand new identically-configured ESP32-S3 reverse TFT, so if you can't reproduce it either, I'm going to chalk it up to some permanent flash corruption on the other S3 board.

@dhalbert
Copy link
Contributor

@anecdata Which board exactly is the one that is not working well?

@anecdata
Copy link
Member

anecdata commented Dec 23, 2024

It's an Adafruit ESP32-S3 Feather TFT (non-reverse). It has been in nearly continuous operation for other uses for over two years, so it's not inconceivable it's bad. I have two pretty new ESP32-S3 QT Py also now running fine, along with the new reverse TFT.

edit: the feather TFT was perhaps an unfortunate choice of test board, just occurred to me it had been getting regular flash writes from safemode.py and boot.py for about the past year.

Copy link
Member

@anecdata anecdata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now all (3) of the replacement ESP32-S3 boards have run for an hour just listening with no clients, and then for another 3 hours+ (and going) with the client code above looping to each. No issues. Sorry for the bad board rabbit hole. Server reports response times on ESP32-S3 in the 300-400ms range. Client reports slightly longer overall times.

Looks good to me operationally (thanks, @michalpokusa!). The following configs are working:

  • ESP32-S3 wifi (N4R2 or better; not tested w/o PSRAM)
  • RPi Pico with WIZnet *
  • RPi Pico 2 with WIZnet *
  • RPI Pico 2 W wifi (and variants)

(* Functional but not quite as robust: minor caveats about timeouts in comments above, and IP address oddities in the first couple of transactions. I don't think these are due to this library.)

The following configs don't have enough memory currently:

  • ESP32-S2 wifi
  • RPi Pico W wifi

I'll leave this for @dhalbert or @FoamyGuy to merge, pending any code review or other comments.

@michalpokusa
Copy link
Contributor Author

michalpokusa commented Dec 28, 2024

I ran the file provided by @anecdata without any clients on a nearly new MEMENTO for a whole day:

  • about every 1-2 hours the board resetted to safe mode without any message
  • one time I got a message "Hard fault: memory access or instruction error." and one time "Internal watchdog timer expired."
  • never got any Python exception like MemoryError etc.

@anecdata
Copy link
Member

@michalpokusa Is it repeatable on any other device(s)?

I'm trying to reproduce it with a DEBUG build and a regular build, on two identically-configured boards. Will update if I se anything.

You could try to erase flash and clean install to see if that makes any difference.

@michalpokusa
Copy link
Contributor Author

I have only one board type with ESP32-S3 - MEMENTO, so unfortunately I am unable to test on other devices.

Following your suggestion I will do some more testing on the second unopened MEMENTO and come back with results.

@dhalbert
Copy link
Contributor

A crash is a CircuitPython bug. If the server code otherwise works, we can merge it in to the library and open an issue on CircuitPython itself.

@anecdata
Copy link
Member

The code works from my perspective, and I think we would benefit from merging and getting it out there for others to use. I found no regressions with HTTP from the HTTPS changes.

@michalpokusa on my "bad" board, httpserver_start_and_poll.py (adjusted for HTTPS) also crashed. I ran tip of main regular and debug builds on identical Waveshare ESP32-S2-ETH boards using wifi and they ran all night without issue. i'll keep trying to reproduce it on some board other than my suspect board.

Copy link
Contributor

@dhalbert dhalbert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @michalpokusa and thanks @anecdata for testing!

@dhalbert dhalbert merged commit bcef270 into adafruit:main Dec 29, 2024
1 check passed
@michalpokusa michalpokusa deleted the https-implementation branch December 29, 2024 15:36
@anecdata
Copy link
Member

anecdata commented Dec 30, 2024

I tried a DEBUG build on the "bad" S3 TFT board, and it's not crashing. non-DEBUG build would consistently crash every few minutes. We should open an issue on circuitpython, but I don't think there is anything yet that is reproducible from person to person.

I have a hard time coming up with an explanation for the observations. Something reading or writing past a boundary, and it matters what's in flash (or ram) at that location? Reminds me of that ATB/FTB core issue from a few years ago.

update: Got one, but it's |<-CORRUPTED. I'll keep at it for a while.

update: A few more, always corrupted though. Not much help.

adafruit-adabot added a commit to adafruit/Adafruit_CircuitPython_Bundle that referenced this pull request Dec 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants