Skip to content

Commit 368478c

Browse files
authored
Merge pull request #2315 from adafruit/picow_http_server
Adding code for HTTP server
2 parents 0332590 + 35fd6c4 commit 368478c

1 file changed

Lines changed: 252 additions & 0 deletions

File tree

  • PicoW_CircuitPython_HTTP_Server
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import os
6+
import time
7+
import ipaddress
8+
import wifi
9+
import socketpool
10+
import busio
11+
import board
12+
import microcontroller
13+
import displayio
14+
import terminalio
15+
from adafruit_display_text import label
16+
import adafruit_displayio_ssd1306
17+
import adafruit_imageload
18+
from digitalio import DigitalInOut, Direction
19+
from adafruit_httpserver import HTTPServer, HTTPResponse
20+
from adafruit_onewire.bus import OneWireBus
21+
from adafruit_ds18x20 import DS18X20
22+
23+
# onboard LED setup
24+
led = DigitalInOut(board.LED)
25+
led.direction = Direction.OUTPUT
26+
led.value = False
27+
28+
# pin used for party parrot animation
29+
parrot_pin = DigitalInOut(board.GP10)
30+
parrot_pin.direction = Direction.OUTPUT
31+
parrot_pin.value = False
32+
33+
# one-wire bus for DS18B20
34+
ow_bus = OneWireBus(board.GP6)
35+
36+
# scan for temp sensor
37+
ds18 = DS18X20(ow_bus, ow_bus.scan()[0])
38+
39+
# function to convert celcius to fahrenheit
40+
def c_to_f(temp):
41+
temp_f = (temp * 9/5) + 32
42+
return temp_f
43+
44+
# i2c display setup
45+
displayio.release_displays()
46+
oled_reset = board.GP9
47+
48+
# STEMMA I2C on picowbell
49+
i2c = busio.I2C(board.GP5, board.GP4)
50+
display_bus = displayio.I2CDisplay(i2c, device_address=0x3D, reset=oled_reset)
51+
52+
WIDTH = 128
53+
HEIGHT = 64
54+
offset_y = 5
55+
56+
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=WIDTH, height=HEIGHT)
57+
58+
# default display group
59+
splash = displayio.Group()
60+
display.show(splash)
61+
62+
# connect to network
63+
print()
64+
print("Connecting to WiFi")
65+
connect_text = "Connecting..."
66+
connect_text_area = label.Label(
67+
terminalio.FONT, text=connect_text, color=0xFFFFFF, x=0, y=offset_y
68+
)
69+
splash.append(connect_text_area)
70+
71+
# set static IP address
72+
ipv4 = ipaddress.IPv4Address("192.168.1.42")
73+
netmask = ipaddress.IPv4Address("255.255.255.0")
74+
gateway = ipaddress.IPv4Address("192.168.1.1")
75+
wifi.radio.set_ipv4_address(ipv4=ipv4,netmask=netmask,gateway=gateway)
76+
# connect to your SSID
77+
wifi.radio.connect(os.getenv('WIFI_SSID'), os.getenv('WIFI_PASSWORD'))
78+
79+
print("Connected to WiFi")
80+
pool = socketpool.SocketPool(wifi.radio)
81+
server = HTTPServer(pool)
82+
83+
# variables for HTML
84+
# comment/uncomment desired temp unit
85+
86+
# temp_test = str(ds18.temperature)
87+
# unit = "C"
88+
temp_test = str(c_to_f(ds18.temperature))
89+
unit = "F"
90+
# font for HTML
91+
font_family = "monospace"
92+
party_state = "Party?"
93+
94+
# the HTML script
95+
# setup as an f string
96+
# this way, can insert string variables from code.py directly
97+
# of note, use {{ and }} if something from html *actually* needs to be in brackets
98+
# i.e. CSS style formatting
99+
def webpage():
100+
html = f"""
101+
<!DOCTYPE html>
102+
<html>
103+
<head>
104+
<meta http-equiv="Content-type" content="text/html;charset=utf-8">
105+
<meta name="viewport" content="width=device-width, initial-scale=1">
106+
<style>
107+
html{{font-family: {font_family}; background-color: lightgrey;
108+
display:inline-block; margin: 0px auto; text-align: center;}}
109+
h1{{color: deeppink; padding: 2vh; font-size: 35px;}}
110+
p{{font-size: 1.5rem;}}
111+
.button{{font-family: {font_family};display: inline-block;
112+
background-color: black; border: none;
113+
border-radius: 4px; color: white; padding: 16px 40px;
114+
text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}}
115+
p.dotted {{margin: auto; height: 50px;
116+
width: 75%; font-size: 25px; text-align: center;}}
117+
</style>
118+
</head>
119+
<body>
120+
<title>Pico W HTTP Server</title>
121+
<h1>Pico W HTTP Server</h1>
122+
<p class="dotted">This is a Pico W running an HTTP server with CircuitPython.</p>
123+
<p class="dotted">The current ambient temperature near the Pico W is
124+
<span style="color: deeppink;">{temp_test}°{unit}</span></p>
125+
<h1>Control the LED on the Pico W with these buttons:</h1>
126+
<form accept-charset="utf-8" method="POST">
127+
<button class="button" name="LED ON" value="ON" type="submit">LED ON</button></a></p></form>
128+
<p><form accept-charset="utf-8" method="POST">
129+
<button class="button" name="LED OFF" value="OFF" type="submit">LED OFF</button></a></p></form>
130+
<h1>Party?</h>
131+
<p><form accept-charset="utf-8" method="POST">
132+
<button class="button" name="party" type="submit">PARTY!</button></a></p></form>
133+
</body></html>
134+
"""
135+
return html
136+
137+
# route default static IP
138+
@server.route("/")
139+
def base(request): # pylint: disable=unused-argument
140+
# serve the HTML f string
141+
# with content type text/html
142+
return HTTPResponse(content_type="text/html", body=webpage())
143+
144+
# if a button is pressed on the site
145+
@server.route("/", "POST")
146+
def buttonpress(request):
147+
# get the raw text
148+
raw_text = request.raw_request.decode("utf8")
149+
print(raw_text)
150+
# if the led on button was pressed
151+
if "ON" in raw_text:
152+
# turn on the onboard LED
153+
led.value = True
154+
# if the led off button was pressed
155+
if "OFF" in raw_text:
156+
# turn the onboard LED off
157+
led.value = False
158+
# if the party button was pressed
159+
if "party" in raw_text:
160+
# toggle the parrot_pin value
161+
parrot_pin.value = not parrot_pin.value
162+
# reload site
163+
return HTTPResponse(content_type="text/html", body=webpage())
164+
165+
print("starting server..")
166+
# startup the server
167+
try:
168+
server.start(str(wifi.radio.ipv4_address))
169+
print("Listening on http://%s:80" % wifi.radio.ipv4_address)
170+
# if the server fails to begin, restart the pico w
171+
except OSError:
172+
time.sleep(5)
173+
print("restarting..")
174+
microcontroller.reset()
175+
ping_address = ipaddress.ip_address("8.8.4.4")
176+
177+
# text objects for screen
178+
# connected to SSID text
179+
connect_text_area.text = "Connected to:"
180+
ssid_text = "%s" % os.getenv('WIFI_SSID')
181+
ssid_text_area = label.Label(
182+
terminalio.FONT, text=ssid_text, color=0xFFFFFF, x=0, y=offset_y+15
183+
)
184+
splash.append(ssid_text_area)
185+
# display ip address
186+
ip_text = "IP: %s" % wifi.radio.ipv4_address
187+
ip_text_area = label.Label(
188+
terminalio.FONT, text=ip_text, color=0xFFFFFF, x=0, y=offset_y+30
189+
)
190+
splash.append(ip_text_area)
191+
# display temp reading
192+
temp_text = "Temperature: %.02f F" % float(temp_test)
193+
temp_text_area = label.Label(
194+
terminalio.FONT, text=temp_text, color=0xFFFFFF, x=0, y=offset_y+45
195+
)
196+
splash.append(temp_text_area)
197+
198+
# party parrot display group
199+
parrot_group = displayio.Group()
200+
# load in party parrot bitmap
201+
parrot_bit, parrot_pal = adafruit_imageload.load("/partyParrots64.bmp",
202+
bitmap=displayio.Bitmap,
203+
palette=displayio.Palette)
204+
parrot_grid = displayio.TileGrid(parrot_bit, pixel_shader=parrot_pal,
205+
width=1, height=1,
206+
tile_height=64, tile_width=64,
207+
default_tile=1,
208+
x=32, y=0)
209+
parrot_group.append(parrot_grid)
210+
211+
clock = time.monotonic() # time.monotonic() holder for server ping
212+
parrot = False # parrot state
213+
party = 0 # time.monotonic() holder for party parrot
214+
p = 0 # index for tilegrid
215+
216+
while True:
217+
try:
218+
# every 30 seconds, ping server & update temp reading
219+
if (clock + 30) < time.monotonic():
220+
if wifi.radio.ping(ping_address) is None:
221+
connect_text_area.text = "Disconnected!"
222+
ssid_text_area.text = None
223+
print("lost connection")
224+
else:
225+
connect_text_area.text = "Connected to:"
226+
ssid_text_area.text = "%s" % os.getenv('WIFI_SSID')
227+
print("connected")
228+
clock = time.monotonic()
229+
# comment/uncomment for desired units
230+
# temp_test = str(ds18.temperature)
231+
temp_test = str(c_to_f(ds18.temperature))
232+
temp_text_area.text = "Temperature: %d F" % temp_test
233+
234+
#if parrot is True:
235+
if parrot_pin.value is True:
236+
# switch to party parrot display group
237+
display.show(parrot_group)
238+
if (party + 0.1) < time.monotonic():
239+
# the party parrot animation cycles
240+
parrot_grid[0] = p
241+
# p is the tilegrid index location
242+
p = (p + 1) % 10
243+
party = time.monotonic()
244+
# if it isn't a party
245+
else:
246+
# show default display with info
247+
display.show(splash)
248+
# poll the server for incoming/outgoing requests
249+
server.poll()
250+
# pylint: disable=broad-except
251+
except Exception:
252+
continue

0 commit comments

Comments
 (0)