I have an http server running a very simple website via WiFi on a Pi Pico W.
The code is derived from https://learn.adafruit.com/pico-w-http- ... cuitpython with tweaks and updates. The server is updated by regular calls to server.poll().
Every now and again (typically when two on-screen buttons are pressed in quick succession), the whole system locks up. I can interrupt the code with Control-C in the Mu Serial window, and then a get a traceback that says the code was in the middle of handling a broken pipe exception, code 32.
Traceback (most recent call last):
File "code.py", line 330, in <module>
File "adafruit_httpserver/server.py", line 163, in poll
File "code.py", line 256, in buttonpress
File "adafruit_httpserver/response.py", line 165, in send
File "adafruit_httpserver/response.py", line 141, in _send_headers
File "adafruit_httpserver/response.py", line 242, in _send_bytes
Any thoughts as to what is going on? I have a try...except around the code to reset the Pico in the case of an exception, but the code never reaches it. Looks like there might be an exception handler in response.py that is hanging?
Any thoughts most appreciated, I'm confused. Thank you for your help. Feel free to ask clarifying questions!
Pi Pico W adafruit_httpserver hangs on broken pipe exception?
Moderators: adafruit_support_bill, adafruit
Please be positive and constructive with your questions and comments.
- BooleanMattock
- Posts: 49
- Joined: Wed Nov 16, 2022 8:14 am
- mikeysklar
- Posts: 13936
- Joined: Mon Aug 01, 2016 8:10 pm
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
Since the buttonpress appears to be triggering the hang are you able to slow down the button detection code slightly to prevent this? Go ahead and paste your version of the modified http server.
- BooleanMattock
- Posts: 49
- Joined: Wed Nov 16, 2022 8:14 am
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
Thank you! See below. This isn't all the code, I did my best to excerpt the relevant parts (some parts I don't want to share on a public forum) and avoid red herrings.mikeysklar wrote: ↑Wed Jan 18, 2023 6:03 pm Since the buttonpress appears to be triggering the hang are you able to slow down the button detection code slightly to prevent this? Go ahead and paste your version of the modified http server.
I tried slowing down the polling of the server by adding a time.sleep() in the main loop, which seemed to make things worse. I think you're suggesting the opposite - slowing down buttonpress() ?
Code: Select all
print("Connecting to WiFi")
try:
# set static IP address
ipv4 = ipaddress.IPv4Address(os.getenv('WIFI_ADDRESS'))
netmask = ipaddress.IPv4Address(os.getenv('WIFI_NETMASK'))
gateway = ipaddress.IPv4Address(os.getenv('WIFI_GATEWAY'))
wifi.radio.set_ipv4_address(ipv4=ipv4,netmask=netmask,gateway=gateway)
# connect to your SSID
wifi.radio.connect(os.getenv('WIFI_SSID'), os.getenv('WIFI_PASSWORD'))
print("Connected to WiFi")
except OSError:
# if the server fails to begin, it's probably because the wifi is not available
# go to sleep and wait for user to restart the pico w
print("Error connecting to network")
time.sleep(2)
print("going to sleep..")
# Exit the program, and then deep sleep until the user wakes us.
pin_alarm = alarm.pin.PinAlarm(pin=board.GP15, value=False, pull=True)
alarm.exit_and_deep_sleep_until_alarms(pin_alarm)
pool = socketpool.SocketPool(wifi.radio)
server = HTTPServer(pool)
server.request_buffer_size = 16384
# Constants for code
HARSH = "Harsh"
MILD = "Mild"
DISABLED = -100
runMode = MILD
# variables for HTML
passName = os.getenv('PASS_NAME')
# Countdown time in minutes
countdownTime = 5
# Keep a track of when we last did something - sleep if idle too long
lastTimeActive = time.monotonic()
IDLE_THRESHOLD_SECONDS = os.getenv("IDLE_THRESHOLD_SECONDS")
running = False
expired = False
running_str = ""
remainingTime = countdownTime*60
time_str = str(countdownTime)
remainingTime_str = ""
warningTime = DISABLED
# Reset the idle count
def ResetIdle():
global lastTimeActive
lastTimeActive = time.monotonic()
# If we have been idle too long, go into deep sleep
# Await wake from reset stud and then do complete reset
def CheckIdleDeepSleep():
global lastTimeActive
timeNow = time.monotonic()
idleTime = timeNow - lastTimeActive
# print("Idle for ", idleTime)
if (idleTime > IDLE_THRESHOLD_SECONDS):
# Exit the program, and then deep sleep until the alarm wakes us.
pin_alarm = alarm.pin.PinAlarm(pin=board.GP15, value=False, pull=True)
alarm.exit_and_deep_sleep_until_alarms(pin_alarm)
# font for HTML
font_family = "monospace"
# the HTML script
# setup as an f string
# this way, can insert string variables from code.py directly
# of note, use {{ and }} if something from html *actually* needs to be in brackets
# i.e. CSS style formatting
def webpage():
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">
<meta http-equiv="refresh" content="10">
<style>
html{{font-family: {font_family}; background-color: lightgrey;
display:inline-block; margin: 0px auto; text-align: center;}}
h1{{color: red; width: 200; word-wrap: break-word; padding: 2vh; font-size: 20px;}}
h2{{color: green; width: 200; word-wrap: break-word; font-size: 50px;}}
p{{font-size: 1.5rem; width: 200; word-wrap: break-word;}}
.button{{font-family: {font_family};display: inline-block;
background-color: black; border: none;
border-radius: 4px; color: white; padding: 10px 20px;
text-decoration: none; font-size: 15px; margin: 2px; cursor: pointer;}}
p.dotted {{margin: auto;
width: 75%; font-size: 15px; text-align: center;}}
</style>
</head>
<body>
<title>My Project</title>
<h1>My Project - {passName}</h1>
<p class="dotted">Set time and then hit a Start Button:</p>
<p class="dotted">Time set to:
<span style="color: green;">{time_str}:00</span></p><br>
<h2>{running_str}</h2><br>
<form accept-charset="utf-8" method="POST">
<button class="button" name="TIME UP" value="UP" type="submit">TIME +1 min</button></a></p></form>
<form accept-charset="utf-8" method="POST">
<button class="button" name="TIME DOWN" value="DOWN" type="submit">TIME -1 min</button></a></p></form>
<form accept-charset="utf-8" method="POST">
<button class="button" name="STOP" value="STOP" type="submit">**STOP**</button></a></p></form>
<form accept-charset="utf-8" method="POST">
<button class="button" name="START MILD" value="MILD" type="submit">START MILD</button></a></p></form>
<p><form accept-charset="utf-8" method="POST">
<button class="button" name="START HARSH" value="HARSH" type="submit">START HARSH</button></a></p></form>
</body></html>
"""
return html
# route default static IP
@server.route("/")
def base(request): # pylint: disable=unused-argument
# serve the HTML f string
# with content type text/html
response = HTTPResponse( request )
return response.send(content_type="text/html", body=webpage())
# if a button is pressed on the site
@server.route("/", "POST")
def buttonpress(request):
global countdownTime, time_str, running, running_str, remainingTime, remainingTime_str, runMode, warningTime, expired
# If a button has been pressed, we're not idle
ResetIdle()
# get the raw text
raw_text = request.raw_request.decode("utf8")
# print(raw_text)
# if the UP button was pressed
if "UP" in raw_text:
countdownTime = countdownTime + 1
# if the DOWN button was pressed
if "DOWN" in raw_text:
countdownTime = countdownTime - 1
if "MILD" in raw_text:
print("Countdown started")
running = True
runMode = MILD
warningTime = DISABLED
remainingTime = countdownTime*60
remainingTime_str = f"{int(remainingTime/60):02}:{remainingTime%60:02}"
running_str = f"Running({runMode}): "+remainingTime_str
if "HARSH" in raw_text:
print("Countdown started")
running = True
runMode = HARSH
# warning time must be a multiple of 10 seconds or the count will miss it
# (lazy coding)
warningTime = 60
remainingTime = countdownTime*60
remainingTime_str = f"{int(remainingTime/60):02}:{remainingTime%60:02}"
running_str = f"Running({runMode}): "+remainingTime_str
if "STOP" in raw_text:
running = False
expired = False
running_str = ""
time_str = str(countdownTime)
response = HTTPResponse( request )
return response.send(content_type="text/html", body=webpage())
print("starting server..")
# startup the server
try:
server.start(str(wifi.radio.ipv4_address))
print("Listening on http://%s:80" % wifi.radio.ipv4_address)
except OSError:
# if the server fails to begin, it's probably because the wifi is not available
# go to sleep and wait for user to restart the pico w
print("Error starting server")
time.sleep(2)
print("going to sleep..")
# Exit the program, and then deep sleep until the user wakes us.
pin_alarm = alarm.pin.PinAlarm(pin=board.GP15, value=False, pull=True)
alarm.exit_and_deep_sleep_until_alarms(pin_alarm)
# Main web server loop
# Since this has to run continuously, we're lazy
# and also use it for timing before warning
while True:
try:
# every 10 seconds, update countdown
if (clock + 10) < time.monotonic():
clock = time.monotonic()
# force garbage collection
gc.collect()
print(gc.mem_free(), " bytes free")
# If time has expired, just keep on repeating the expired
# display until user hits Stop
if (expired):
TimeExpiredDisplay(runMode)
if (running):
# Can't be idle while timer is running
ResetIdle()
# Check timer for expiry
remainingTime = remainingTime - 10
remainingTime_str = f"{int(remainingTime/60):02}:{remainingTime%60:02}"
running_str = f"Running({runMode}): "+remainingTime_str
# Assume that the countdown will at some point exactly hit the
# warning time value
if (remainingTime == warningTime):
TimeWarningDisplay()
if (remainingTime <= 0):
running = False
expired = True
remainingTime = 0
running_str = "TIME EXPIRED"
TimeExpiredDisplay(runMode)
# poll the server for incoming/outgoing requests
server.poll()
# Check for idle time expiry
# If we're idle too long this doesn't return - sleep until reset
CheckIdleDeepSleep()
except Exception as e:
print("Got an error")
print(e)
print("going to sleep..")
# Exit the program, and then deep sleep until the user wakes us.
pin_alarm = alarm.pin.PinAlarm(pin=board.GP15, value=False, pull=True)
alarm.exit_and_deep_sleep_until_alarms(pin_alarm)
- mikeysklar
- Posts: 13936
- Joined: Mon Aug 01, 2016 8:10 pm
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
We have some helpful guides in regards to button detection, state change and debouncing. It might help to take a look at these which do show an example of adding in short delays for the button_pressed() function, but other built-ins to explore.
https://learn.adafruit.com/multi-taskin ... on/buttons
https://learn.adafruit.com/debouncer-li ... debouncing
https://learn.adafruit.com/multi-taskin ... on/buttons
https://learn.adafruit.com/debouncer-li ... debouncing
- BooleanMattock
- Posts: 49
- Joined: Wed Nov 16, 2022 8:14 am
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
LOL my apologies for not being clearer. The buttons in question are the ones on the web page being served by my application. If you look at the html for the webpage you'll see them. As I said, the exception occurs when two of the buttons on the webpage are pressed (clicked, tapped) in quick succession.mikeysklar wrote: ↑Mon Jan 23, 2023 6:13 pm We have some helpful guides in regards to button detection, state change and debouncing. It might help to take a look at these which do show an example of adding in short delays for the button_pressed() function, but other built-ins to explore.
https://learn.adafruit.com/multi-taskin ... on/buttons
https://learn.adafruit.com/debouncer-li ... debouncing
- mikeysklar
- Posts: 13936
- Joined: Mon Aug 01, 2016 8:10 pm
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
Are you using a web browser to connect to the httpserver? If so, which one? There is an open issue with Chrome causing hangs. If the button hang condition is happening differently or with another browser it would be a good idea to open a case about on screen buttons being pressed in quick succession. I assume you can reproduce this with the stock example code (not the modified you are using).
https://github.com/adafruit/Adafruit_Ci ... /issues/31
https://github.com/adafruit/Adafruit_Ci ... /issues/31
- BooleanMattock
- Posts: 49
- Joined: Wed Nov 16, 2022 8:14 am
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
Good question, thank you. I took a look at that issue report.mikeysklar wrote: ↑Thu Jan 26, 2023 10:15 am Are you using a web browser to connect to the httpserver? If so, which one? There is an open issue with Chrome causing hangs. If the button hang condition is happening differently or with another browser it would be a good idea to open a case about on screen buttons being pressed in quick succession. I assume you can reproduce this with the stock example code (not the modified you are using).
https://github.com/adafruit/Adafruit_Ci ... /issues/31
- My issue happens independent of browser type.
- It's also not the same failure: in my case, the code gets a broken pipe exception and then appears to hang handling it, which is (I think) different from what's described in issue report #31.
The stock example code actually doesn't work with the latest library code, and also needs hardware I don't have. I will cut the web server part out of it verbatim, fix the library calls, and try running that.
Last edited by BooleanMattock on Thu Jan 26, 2023 3:21 pm, edited 1 time in total.
- mikeysklar
- Posts: 13936
- Joined: Mon Aug 01, 2016 8:10 pm
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
I can look into the stock adafruit example code not working with the Adafruit the current httpserver. Thank you for pointing that out.
https://learn.adafruit.com/pico-w-http- ... ttp-server
https://learn.adafruit.com/pico-w-http- ... ttp-server
- BooleanMattock
- Posts: 49
- Joined: Wed Nov 16, 2022 8:14 am
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
Going back to look at it now, it has changed since I copied it for my project, so I think it may already have been fixed? I'll try the updated code, it might also fix my issue.mikeysklar wrote: ↑Thu Jan 26, 2023 3:19 pm I can look into the stock adafruit example code not working with the Adafruit the current httpserver. Thank you for pointing that out.
https://learn.adafruit.com/pico-w-http- ... ttp-server
- mikeysklar
- Posts: 13936
- Joined: Mon Aug 01, 2016 8:10 pm
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
Please let me know how it goes and if the updates browser buttons respond any differently.
- BooleanMattock
- Posts: 49
- Joined: Wed Nov 16, 2022 8:14 am
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
So the example code does not show the problem.mikeysklar wrote: ↑Thu Jan 26, 2023 7:21 pm Please let me know how it goes and if the updates browser buttons respond any differently.
I wondered if it might be my use of:
<meta http-equiv="refresh" content="10">
to force the webpage to update every 10 seconds? That's the main difference that I can think of between my code and the example code. But I put that line into the example code and it works fine.
Struggling to see what the difference is that breaks the code...the only clue I have is that sometimes the web server code seems slow to respond. But I can't see what makes it go slower sometimes, it's executing the same code as far as I can see.
If I could find out what the possible causes of a broken pipe exception are, I might be able to work backwards from there. Any thoughts?
- mikeysklar
- Posts: 13936
- Joined: Mon Aug 01, 2016 8:10 pm
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
There is a new release candidate for CircuitPython 8.x so you can go ahead and update to that while testing your code.
I don't know how different your code is from the example. I normally make a copy of my problematic code and strip away large sections until it stabilizes. Then review the changes for the culprit.
I don't know how different your code is from the example. I normally make a copy of my problematic code and strip away large sections until it stabilizes. Then review the changes for the culprit.
- BooleanMattock
- Posts: 49
- Joined: Wed Nov 16, 2022 8:14 am
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
Hi Mike, already did that, didn't find anything, that's why I'm struggling. Code is in OP.mikeysklar wrote: ↑Tue Jan 31, 2023 6:21 pm
I don't know how different your code is from the example. I normally make a copy of my problematic code and strip away large sections until it stabilizes. Then review the changes for the culprit.
- BooleanMattock
- Posts: 49
- Joined: Wed Nov 16, 2022 8:14 am
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
OK, latest update. I have now managed to make the same lockup happen on the example code.
Code enclosed below, cut down from the latest version of the example here: https://learn.adafruit.com/pico-w-http- ... ttp-server.
Using the latest release:
adafruit-circuitpython-raspberry_pi_pico_w-en_US-8.0.0-rc.2.uf2
and libraries:
adafruit-circuitpython-bundle-8.x-mpy-20230203.zip
Basically if you click repeatedly and rapidly on one of the on-screen buttons on the web page, eventually the code hangs in:
Traceback (most recent call last):
File "code.py", line 157, in <module>
File "adafruit_httpserver/server.py", line 163, in poll
File "code.py", line 128, in buttonpress
File "adafruit_httpserver/response.py", line 165, in send
File "adafruit_httpserver/response.py", line 141, in _send_headers
File "adafruit_httpserver/response.py", line 242, in _send_bytes
Code enclosed below, cut down from the latest version of the example here: https://learn.adafruit.com/pico-w-http- ... ttp-server.
Using the latest release:
adafruit-circuitpython-raspberry_pi_pico_w-en_US-8.0.0-rc.2.uf2
and libraries:
adafruit-circuitpython-bundle-8.x-mpy-20230203.zip
Basically if you click repeatedly and rapidly on one of the on-screen buttons on the web page, eventually the code hangs in:
Traceback (most recent call last):
File "code.py", line 157, in <module>
File "adafruit_httpserver/server.py", line 163, in poll
File "code.py", line 128, in buttonpress
File "adafruit_httpserver/response.py", line 165, in send
File "adafruit_httpserver/response.py", line 141, in _send_headers
File "adafruit_httpserver/response.py", line 242, in _send_bytes
Code: Select all
# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import os
import time
import ipaddress
import wifi
import socketpool
import busio
import board
import microcontroller
import displayio
import terminalio
from digitalio import DigitalInOut, Direction
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
# onboard LED setup
led = DigitalInOut(board.LED)
led.direction = Direction.OUTPUT
led.value = False
# connect to network
print()
print("Connecting to WiFi")
# set static IP address
ipv4 = ipaddress.IPv4Address("192.168.1.55")
netmask = ipaddress.IPv4Address("255.255.255.0")
gateway = ipaddress.IPv4Address("192.168.1.1")
wifi.radio.set_ipv4_address(ipv4=ipv4,netmask=netmask,gateway=gateway)
# connect to your SSID
wifi.radio.connect("XXXX", "yyyyy")
print("Connected to WiFi")
pool = socketpool.SocketPool(wifi.radio)
server = HTTPServer(pool)
# variables for HTML
# comment/uncomment desired temp unit
# temp_test = str(ds18.temperature)
# unit = "C"
temp_test = "32"
unit = "F"
# font for HTML
font_family = "monospace"
# the HTML script
# setup as an f string
# this way, can insert string variables from code.py directly
# of note, use {{ and }} if something from html *actually* needs to be in brackets
# i.e. CSS style formatting
def webpage():
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">
<style>
html{{font-family: {font_family}; background-color: lightgrey;
display:inline-block; margin: 0px auto; text-align: center;}}
h1{{color: deeppink; width: 200; word-wrap: break-word; padding: 2vh; font-size: 35px;}}
p{{font-size: 1.5rem; width: 200; word-wrap: break-word;}}
.button{{font-family: {font_family};display: inline-block;
background-color: black; border: none;
border-radius: 4px; color: white; padding: 16px 40px;
text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}}
p.dotted {{margin: auto;
width: 75%; font-size: 25px; text-align: center;}}
</style>
</head>
<body>
<title>Pico W HTTP Server</title>
<h1>Pico W HTTP Server</h1>
<br>
<p class="dotted">This is a Pico W running an HTTP server with CircuitPython.</p>
<br>
<p class="dotted">The current ambient temperature near the Pico W is
<span style="color: deeppink;">{temp_test}°{unit}</span></p><br>
<h1>Control the LED on the Pico W with these buttons:</h1><br>
<form accept-charset="utf-8" method="POST">
<button class="button" name="LED ON" value="ON" type="submit">LED ON</button></a></p></form>
<p><form accept-charset="utf-8" method="POST">
<button class="button" name="LED OFF" value="OFF" type="submit">LED OFF</button></a></p></form>
<h1>Party?</h>
<p><form accept-charset="utf-8" method="POST">
<button class="button" name="party" value="party" type="submit">PARTY!</button></a></p></form>
</body></html>
"""
return html
# route default static IP
@server.route("/")
def base(request: HTTPRequest): # pylint: disable=unused-argument
# serve the HTML f string
# with content type text/html
with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response:
response.send(f"{webpage()}")
# if a button is pressed on the site
@server.route("/", method=HTTPMethod.POST)
def buttonpress(request: HTTPRequest):
# get the raw text
raw_text = request.raw_request.decode("utf8")
print(raw_text)
# if the led on button was pressed
if "ON" in raw_text:
# turn on the onboard LED
led.value = True
# if the led off button was pressed
if "OFF" in raw_text:
# turn the onboard LED off
led.value = False
# if the party button was pressed
if "party" in raw_text:
# toggle the parrot_pin value
led.value = False
# reload site
with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response:
response.send(f"{webpage()}")
print("starting server..")
# startup the server
try:
server.start(str(wifi.radio.ipv4_address))
print("Listening on http://%s:80" % wifi.radio.ipv4_address)
# if the server fails to begin, restart the pico w
except OSError:
time.sleep(5)
print("restarting..")
microcontroller.reset()
clock = time.monotonic() # time.monotonic() holder for server ping
parrot = False # parrot state
party = 0 # time.monotonic() holder for party parrot
p = 0 # index for tilegrid
while True:
try:
# every 30 seconds, ping server & update temp reading
if (clock + 30) < time.monotonic():
clock = time.monotonic()
# comment/uncomment for desired units
# temp_test = str(ds18.temperature)
temp_test = "31"
temp_text_area.text = "Temperature: %s F" % temp_test
# poll the server for incoming/outgoing requests
server.poll()
# pylint: disable=broad-except
except Exception as e:
print(e)
continue
- mikeysklar
- Posts: 13936
- Joined: Mon Aug 01, 2016 8:10 pm
Re: Pi Pico W adafruit_httpserver hangs on broken pipe exception?
Good work. You have a reproducible bug based on Adafruit example code.
Do you mind opening an issue here with the Learning System Guides github repo?
https://github.com/adafruit/Adafruit_Le ... des/issues
You can pretty much cut and paste your last post.
Do you mind opening an issue here with the Learning System Guides github repo?
https://github.com/adafruit/Adafruit_Le ... des/issues
You can pretty much cut and paste your last post.
Please be positive and constructive with your questions and comments.