I'm working on a project using the Raspberry Pi Pico W 2 that involves displaying data from BLE sensors on a web page, with everything running on the Pico.
My intention is to:
- Run an asynchronous web server on Core 0.
- Use a thread on Core 1 to handle sensor interactions.
The data sharing between the cores is based on a shared memory model with locking (not part of my issue here).
To test this, I used a sample script from Random Nerd Tutorials, replacing all hardware I/O with console printouts so the test only needs the Pico itself. The web server runs perfectly on its own, but when I add a trivial task to run on Core 1, the following issues occur:
- The console outputs appear as expected.
- The web server becomes inaccessible.
- The Pico locks up completely, requiring a full reset with flash_nuke.uf2 to upload any new code.
Problem
What could be causing the Pico to crash when adding a thread on Core 1, and how can I resolve this issue?
Test Code:
Here is the modified code:
# Modified from Random Nerd Tutorials
# https://randomnerdtutorials.com/raspberry-pi-pico-w-asynchronous-web-server-micropython/
import network
import asyncio
import time
from machine import Pin
import _thread
# Wi-Fi credentials
ssid = "wifi-name"
password = "wifi-password"
# Onboard LED setup
led_control = Pin("LED", Pin.OUT)
# Shared variables
state = "OFF"
random_value = 0
# HTML template
def webpage(random_value, state):
return f"""
<!DOCTYPE html>
<html>
<head>
<title>Pico Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Pico Web Server</h1>
<p>LED state: {state}</p>
<p>Random value: {random_value}</p>
</body>
</html>
"""
# Initialize Wi-Fi
def init_wifi(ssid, password):
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
for _ in range(10):
if wlan.status() >= 3:
print('Wi-Fi connected')
print('IP address:', wlan.ifconfig()[0])
return True
time.sleep(1)
print('Wi-Fi connection failed')
return False
# Asynchronous web server
async def handle_client(reader, writer):
global state, random_value
request = await reader.readline()
if '/lighton' in request:
led_control.value(1)
state = "ON"
elif '/lightoff' in request:
led_control.value(0)
state = "OFF"
elif '/value' in request:
random_value = random.randint(0, 20)
response = webpage(random_value, state)
writer.write('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
writer.write(response)
await writer.drain()
await writer.wait_closed()
# Blink task
async def blink_led():
while True:
print("Blinking LED")
await asyncio.sleep(1)
# Core 1 task
def parallel_core_task():
while True:
print("Running on Core 1")
time.sleep(2)
# Main function
async def main():
if not init_wifi(ssid, password):
return
server = await asyncio.start_server(handle_client, "0.0.0.0", 80)
asyncio.create_task(blink_led())
async with server:
await server.serve_forever()
# Start Core 1 thread
_thread.start_new_thread(parallel_core_task, ())
# Start asyncio event loop
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Console Output:
MPY: soft reboot
Running on Core 1
Wi-Fi connected
IP address: 192.168.0.3
Blinking LED
Running on Core 1
...
Additional Notes:
- The issue occurs when _thread.start_new_thread() is introduced.
- Without the Core 1 thread, the server works perfectly.
- Any insights on why adding a thread causes the crash and how to fix this would be appreciated.
threadingmodule, not the low level, private_thread. That said, due to the GIL, mixng threads and asyncio may give you an easy way to have concurrent things ongoing, but it won't use your two cores in parallel, unless your are using a free-threading Python build (marked Python 3.13t <- observe the "t" close to the version number).