Skip to content

Hard crashes on Pico W with high network activiity #10618

@dhalbert

Description

@dhalbert

From #10615 (comment):

The PR seems like a rational fix, so maybe completely unrelated, but under atypically extreme duress (hammering on TCP socket, and hammering on interrupts ...assuming the code is doing what I think), a TCP client will get hardfaults somewhat regularly with PR artifacts on Pico W:

Client code (on a Pico W):
# Adafruit CircuitPython 10.0.0-beta.3 on 2025-08-29; Raspberry Pi Pico W with rp2040
# Adafruit CircuitPython 10.0.0-beta.3-8-g9e7a03cbc2 on 2025-09-05; Raspberry Pi Pico W with rp2040
# "Hard fault: memory access or instruction error." (after "Connecting")

import time
import supervisor
import microcontroller
import os
import random
import traceback
import wifi
import socketpool
import array
import pulseio
import board

# edit host and port to match server
HOST = "192.168.6.57"
PORT = 5000
TIMEOUT = 1
INTERVAL = 0.1
MAXBUF = 8192

def send_pulses():
    # on off on ...
    MAX_TRAIN = 17
    for p_out in range(1, len(pulse_outs)):
        p_train = []
        for p_item in range(0, random.randint(3, MAX_TRAIN)):
            p_train.append(random.randint(100, 10_000))
        pulses = array.array('H', p_train)
        pulse_outs[p_out].send(pulses)

time.sleep(3)  # wait for serial after reset

print("Connecting to Wifi")
while not wifi.radio.connected:
    try:
        wifi.radio.connect(os.getenv('WIFI_SSID'), os.getenv('WIFI_PASSWORD'))
    except Exception as ex:
        traceback.print_exception(ex, ex, ex.__traceback__)
        time.sleep(1 + random.random())
        supervisor.reload()
        # microcontroller.reset()
pool = socketpool.SocketPool(wifi.radio)
print(wifi.radio.ipv4_address)

# 50% duty cycle at 38kHz.
pulse_outs = []
pulse_outs.append(pulseio.PulseOut(board.GP0, frequency=38000, duty_cycle=32768))
pulse_outs.append(pulseio.PulseOut(board.GP1, frequency=38000, duty_cycle=32768))
pulse_outs.append(pulseio.PulseOut(board.GP2, frequency=38000, duty_cycle=32768))
pulse_outs.append(pulseio.PulseOut(board.GP3, frequency=38000, duty_cycle=32768))

buf = bytearray(MAXBUF)
while True:
    try:
        print("Create TCP Client Socket")
        with pool.socket(pool.AF_INET, pool.SOCK_STREAM) as s:
            s.settimeout(TIMEOUT)

            print("Connecting")
            s.connect((HOST, PORT))

            payload = bytearray()
            for _ in range(random.randint(0, MAXBUF)):
                payload.append(random.choice((b'0123456789ABCDEF')))

            send_pulses()  #####

            size = s.send(payload)
            print("Sent", size, "bytes")

            send_pulses()  #####

            size = s.recv_into(buf)
            # occasionally on recv_into:
            # OSError: [Errno 104] ECONNRESET
            # OSError: [Errno 9] EBADF
            # OSError: [Errno 116] ETIMEDOUT

            print('Received', size, "bytes", buf[:size])
    except Exception as ex:
        print(f"⚠️", end=" ")
        traceback.print_exception(ex, ex, ex.__traceback__)

    time.sleep(INTERVAL)
Server code (on a Pico 2W):
import time
import microcontroller
import os
import random
import traceback
import wifi
import socketpool

HOST = ""
PORT = 5000
TIMEOUT = None
BACKLOG = 2
MAXBUF = 8192

time.sleep(3)  # wait for serial after reset

print("Connecting to Wifi")
while not wifi.radio.connected:
    try:
        wifi.radio.connect(os.getenv('WIFI_SSID'), os.getenv('WIFI_PASSWORD'))
    except Exception as ex:
        traceback.print_exception(ex, ex, ex.__traceback__)
        time.sleep(1)
        microcontroller.reset()
pool = socketpool.SocketPool(wifi.radio)
print(wifi.radio.ipv4_address)

print("Create TCP Server socket", (HOST, PORT))
with pool.socket(pool.AF_INET, pool.SOCK_STREAM) as s:
    s.setsockopt(pool.SOL_SOCKET, pool.SO_REUSEADDR, 1)  # 
    s.settimeout(TIMEOUT)

    s.bind((HOST, PORT))
    s.listen(BACKLOG)
    print("Listening")

    buf = bytearray(MAXBUF)
    while True:
        try:
            print("Accepting connections")
            conn, addr = s.accept()
            conn.settimeout(TIMEOUT)
            print("Accepted from", addr)

            size = conn.recv_into(buf, MAXBUF)
            print("Received", buf[:size], size, "bytes")

            conn.send(buf[:size])
            print("Sent", buf[:size], size, "bytes")

        except Exception as ex:
            print(f"⚠️", end=" ")
            traceback.print_exception(ex, ex, ex.__traceback__)
        finally:

            time.sleep(0.1)  # too short and client gets "OSError: [Errno 104] ECONNRESET" in recv

            conn.close()
With beta.3, hardfaults occur under less duress than above, but I hadn't actually replicated it with the prior testing in the previous comment, so just wanted to see how it could be triggered.

There also seems to be a situation where the server closing the connection immediately after sending the response can cause OSError: [Errno 104] ECONNRESET at the client (somehow before the client has read all of the incoming packet... some buffering issue?).

Originally posted by @anecdata in #10615 (comment)

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions