Overview
In this guide, you'll setup an HTTP (web) server with a Pico W running CircuitPython. HTTP servers are handy for creating custom web interfaces to monitor and control IoT projects.
This example sets a static IP address for the server, logs temperature readings from a DS18B20 temperature sensor, displays server information on an OLED and serves an HTML webpage with buttons that can send HTTP POST requests to toggle pins on the Pico W.
The HTML webpage is included in the code.py file as an f-string so that it can take in variables directly from code.py. The HTML code includes buttons that send POST requests for turning the Pico W onboard LED on and off and for displaying a party parrot animation on the OLED.
The OLED displays important information about the server. It tells you the connection status, the name of your SSID, the IP address and the current temperature reading from the DS18B20. It can also show the less important, but more fun, party parrot animation.
Since the Pico W is often affectionately referred to as the Pi-Cow, a bovine-themed 3D printed case is included for this project.
Page last edited March 08, 2024
Text editor powered by tinymce.
Circuit Diagram
OLED
- OLED STEMMA QT port to PiCowbell STEMMA QT port
DS18B20
- Sensor GND to PiCowbell GND (blue wire)
- Sensor Data to PiCowbell GP6 (yellow wire)
- Sensor VIN to PiCowbell 3V (red wire)
- 4.7K ohm resistor between 3V and GP6
LED for Party Parrot
- LED cathode to PiCowbell GND (black wire)
- LED anode to 220 ohm resistor (white wire)
- 220 ohm resistor to PiCowbell GP10
Page last edited March 08, 2024
Text editor powered by tinymce.
Wiring
Solder the 4.7K ohm resistor for the DS18B20 from the 3V line to GP6 on the PiCowbell. Then, solder a 220 ohm resistor from GP10 to one of the prototyping rails on the PiCowbell.
Solder the black wire of the JST cable to the GND pin on the PiCowbell. Solder the red wire of the JST cable to the prototyping rail connected to the 220 ohm resistor.
Solder the DS18B20 to the PiCowbell.
- Blue wire to PiCowbell GND
- Red wire to PiCowbell 3V
- Yellow wire to the prototyping rail next to GP6
Take the remaining plug end of the JST cable and slip a piece of heat shrink over each of the wires.
Solder the LED cathode to the black wire on the JST cable. Solder the LED anode to the red wire on the JST cable.
Page last edited March 08, 2024
Text editor powered by tinymce.
3D Printing
The HTTP server may be assembled with 3D printed parts, described below. The case has three lid options depending on your taste: a plain lid, a lid with a cow print or a lid with a cow face. The cow print and cow face versions of the lid can utilize the M600 filament change command in your slicer software to accentuate the design.
The STL files can be downloaded directly here or from Thingiverse.
Main Case
The main case portion needs supports for the DS18B20 temperature sensor slot. In your slicer software, you can use a support blocker to only add supports to the sensor slot.
Cow Print Lid
For the cow print lid, slice the model with a 0.2 mm layer height and insert a filament change command on layer 2.
Cow Face Lid
For the cow face lid, use the support on build plate only option in your slicer. Slice the model with a 0.2 mm layer height and insert filament change commands on layers 25, 28 and 32.
Page last edited March 08, 2024
Text editor powered by tinymce.
Installing CircuitPython
CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the CIRCUITPY drive to iterate.
CircuitPython Quickstart
Follow this step-by-step to quickly get CircuitPython working on your board.
Click the link above and download the latest UF2 file.
Download and save it to your desktop (or wherever is handy).
Start with your Pico W unplugged from USB. Hold down the BOOTSEL button, and while continuing to hold it (don't let go!), plug the Pico W into USB. Continue to hold the BOOTSEL button until the RPI-RP2 drive appears!
If the drive does not appear, unplug your Pico W and go through the above process again.
A lot of people end up using charge-only USB cables and it is very frustrating! So make sure you have a USB cable you know is good for data sync.
You will see a new disk drive appear called RPI-RP2.
Drag the adafruit_circuitpython_etc.uf2 file to RPI-RP2.
The RPI-RP2 drive will disappear and a new disk drive called CIRCUITPY will appear.
That's it, you're done! :)
Flash Resetting UF2
If your Pico W ever gets into a really weird state and doesn't even show up as a disk drive when installing CircuitPython, try installing this 'nuke' UF2 which will do a 'deep clean' on your Flash Memory. You will lose all the files on the board, but at least you'll be able to revive it! After nuking, re-install CircuitPython
Page last edited March 08, 2024
Text editor powered by tinymce.
Create Your settings.toml File
CircuitPython works with WiFi-capable boards to enable you to make projects that have network connectivity. This means working with various passwords and API keys. As of CircuitPython 8, there is support for a settings.toml file. This is a file that is stored on your CIRCUITPY drive, that contains all of your secret network information, such as your SSID, SSID password and any API keys for IoT services. It is designed to separate your sensitive information from your code.py file so you are able to share your code without sharing your credentials.
CircuitPython previously used a secrets.py file for this purpose. The settings.toml file is quite similar.
CircuitPython settings.toml File
This section will provide a couple of examples of what your settings.toml file should look like, specifically for CircuitPython WiFi projects in general.
The most minimal settings.toml file must contain your WiFi SSID and password, as that is the minimum required to connect to WiFi. Copy this example, paste it into your settings.toml, and update:
your_wifi_ssidyour_wifi_password
CIRCUITPY_WIFI_SSID = "your_wifi_ssid" CIRCUITPY_WIFI_PASSWORD = "your_wifi_password"
Many CircuitPython network-connected projects on the Adafruit Learn System involve using Adafruit IO. For these projects, you must also include your Adafruit IO username and key. Copy the following example, paste it into your settings.toml file, and update:
your_wifi_ssidyour_wifi_passwordyour_aio_usernameyour_aio_key
CIRCUITPY_WIFI_SSID = "your_wifi_ssid" CIRCUITPY_WIFI_PASSWORD = "your_wifi_password" ADAFRUIT_AIO_USERNAME = "your_aio_username" ADAFRUIT_AIO_KEY = "your_aio_key"
Some projects use different variable names for the entries in the settings.toml file. For example, a project might use ADAFRUIT_AIO_ID in the place of ADAFRUIT_AIO_USERNAME. If you run into connectivity issues, one of the first things to check is that the names in the settings.toml file match the names in the code.
Here is an example settings.toml file.
# Comments are supported CIRCUITPY_WIFI_SSID = "guest wifi" CIRCUITPY_WIFI_PASSWORD = "guessable" CIRCUITPY_WEB_API_PORT = 80 CIRCUITPY_WEB_API_PASSWORD = "passw0rd" test_variable = "this is a test" thumbs_up = "\U0001f44d"
In a settings.toml file, it's important to keep these factors in mind:
- Strings are wrapped in double quotes; ex:
"your-string-here" - Integers are not quoted and may be written in decimal with optional sign (
+1,-1,1000) or hexadecimal (0xabcd).- Floats (decimal numbers), octal (
0o567) and binary (0b11011) are not supported.
- Floats (decimal numbers), octal (
- Use
\uescapes for weird characters,\xand\oooescapes are not available in .toml files- Example:
\U0001f44dfor 👍 (thumbs up emoji) and\u20acfor € (EUR sign)
- Example:
- Unicode emoji, and non-ASCII characters, stand for themselves as long as you're careful to save in "UTF-8 without BOM" format
When your settings.toml file is ready, you can save it in your text editor with the .toml extension.
In your code.py file, you'll need to import the os library to access the settings.toml file. Your settings are accessed with the os.getenv() function. You'll pass your settings entry to the function to import it into the code.py file.
import os
print(os.getenv("test_variable"))
In the upcoming CircuitPython WiFi examples, you'll see how the settings.toml file is used for connecting to your SSID and accessing your API keys.
Page last edited March 08, 2024
Text editor powered by tinymce.
Code the Pico W HTTP Server
Once you've finished setting up your Pico W with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.
# SPDX-FileCopyrightText: 2023 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 i2cdisplaybus
import terminalio
from adafruit_display_text import label
import adafruit_displayio_ssd1306
import adafruit_imageload
from digitalio import DigitalInOut, Direction
from adafruit_httpserver import Server, Request, Response, POST
from adafruit_onewire.bus import OneWireBus
from adafruit_ds18x20 import DS18X20
# onboard LED setup
led = DigitalInOut(board.LED)
led.direction = Direction.OUTPUT
led.value = False
# pin used for party parrot animation
parrot_pin = DigitalInOut(board.GP10)
parrot_pin.direction = Direction.OUTPUT
parrot_pin.value = False
# one-wire bus for DS18B20
ow_bus = OneWireBus(board.GP6)
# scan for temp sensor
ds18 = DS18X20(ow_bus, ow_bus.scan()[0])
# function to convert celsius to fahrenheit
def c_to_f(temp):
temp_f = (temp * 9/5) + 32
return temp_f
# i2c display setup
displayio.release_displays()
oled_reset = board.GP9
# STEMMA I2C on picowbell
i2c = busio.I2C(board.GP5, board.GP4)
display_bus = i2cdisplaybus.I2CDisplayBus(i2c, device_address=0x3D, reset=oled_reset)
WIDTH = 128
HEIGHT = 64
offset_y = 5
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=WIDTH, height=HEIGHT)
# default display group
splash = displayio.Group()
display.root_group = splash
# connect to network
print()
print("Connecting to WiFi")
connect_text = "Connecting..."
connect_text_area = label.Label(
terminalio.FONT, text=connect_text, color=0xFFFFFF, x=0, y=offset_y
)
splash.append(connect_text_area)
# set static IP address
ipv4 = ipaddress.IPv4Address("192.168.1.42")
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(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
print("Connected to WiFi")
pool = socketpool.SocketPool(wifi.radio)
server = Server(pool, "/static", debug=True)
# variables for HTML
# comment/uncomment desired temp unit
# temp_test = str(ds18.temperature)
# unit = "C"
temp_test = c_to_f(ds18.temperature)
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:.2f}°{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: Request): # pylint: disable=unused-argument
# serve the HTML f string
# with content type text/html
return Response(request, f"{webpage()}", content_type='text/html')
# if a button is pressed on the site
@server.route("/", POST)
def buttonpress(request: Request):
# 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
parrot_pin.value = not parrot_pin.value
# reload site
return Response(request, f"{webpage()}", content_type='text/html')
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()
ping_address = ipaddress.ip_address("8.8.4.4")
# text objects for screen
# connected to SSID text
connect_text_area.text = "Connected to:"
ssid_text = f"{os.getenv('CIRCUITPY_WIFI_SSID')}"
ssid_text_area = label.Label(
terminalio.FONT, text=ssid_text, color=0xFFFFFF, x=0, y=offset_y+15
)
splash.append(ssid_text_area)
# display ip address
ip_text = f"IP: {wifi.radio.ipv4_address}"
ip_text_area = label.Label(
terminalio.FONT, text=ip_text, color=0xFFFFFF, x=0, y=offset_y+30
)
splash.append(ip_text_area)
# display temp reading
temp_text = f"Temperature: {temp_test:.2f} F"
temp_text_area = label.Label(
terminalio.FONT, text=temp_text, color=0xFFFFFF, x=0, y=offset_y+45
)
splash.append(temp_text_area)
# party parrot display group
parrot_group = displayio.Group()
# load in party parrot bitmap
parrot_bit, parrot_pal = adafruit_imageload.load("/partyParrots64.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette)
parrot_grid = displayio.TileGrid(parrot_bit, pixel_shader=parrot_pal,
width=1, height=1,
tile_height=64, tile_width=64,
default_tile=1,
x=32, y=0)
parrot_group.append(parrot_grid)
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():
if wifi.radio.ping(ping_address) is None:
connect_text_area.text = "Disconnected!"
ssid_text_area.text = None
print("lost connection")
else:
connect_text_area.text = "Connected to:"
ssid_text_area.text = f"{os.getenv('CIRCUITPY_WIFI_SSID')}"
print("connected")
clock = time.monotonic()
# comment/uncomment for desired units
# temp_test = ds18.temperature
temp_test = c_to_f(ds18.temperature)
temp_text_area.text = f"Temperature: {temp_test:.2f} F"
#if parrot is True:
if parrot_pin.value is True:
# switch to party parrot display group
display.root_group = parrot_group
if (party + 0.1) < time.monotonic():
# the party parrot animation cycles
parrot_grid[0] = p
# p is the tilegrid index location
p = (p + 1) % 10
party = time.monotonic()
# if it isn't a party
else:
# show default display with info
display.root_group = splash
# poll the server for incoming/outgoing requests
server.poll()
# pylint: disable=broad-except
except Exception as e:
print(e)
continue
Upload the Code and Libraries to the Pico W
After downloading the Project Bundle, plug your Pico W into the computer's USB port with a known good USB data+power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the Pico W's CIRCUITPY drive.
- lib folder
- code.py
- partyParrots64.bmp
Your Pico W CIRCUITPY drive should look like this after copying the lib folder, partyParrots64.bmp file and the code.py file.
Add Your settings.toml File
As of CircuitPython 8.0.0, there is support for Environment Variables. Environment variables are stored in a settings.toml file. Similar to secrets.py, the settings.toml file separates your sensitive information from your main code.py file. Add your settings.toml file as described in the Create Your settings.toml File page earlier in this guide. You'll need to include your CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD.
CIRCUITPY_WIFI_SSID = "your-ssid-here" CIRCUITPY_WIFI_PASSWORD = "your-ssid-password-here"
The code begins by setting up the onboard LED, LED connected to pin GP10 and the OneWireBus for the DS18B20 sensor.
# onboard LED setup led = DigitalInOut(board.LED) led.direction = Direction.OUTPUT led.value = False # pin used for party parrot animation parrot_pin = DigitalInOut(board.GP10) parrot_pin.direction = Direction.OUTPUT parrot_pin.value = False # one-wire bus for DS18B20 ow_bus = OneWireBus(board.GP6) # scan for temp sensor ds18 = DS18X20(ow_bus, ow_bus.scan()[0])
The function c_to_f() converts Celsius temperature readings to Fahrenheit.
# function to convert celcius to fahrenheit
def c_to_f(temp):
temp_f = (temp * 9/5) + 32
return temp_f
The I2C OLED is instantiated over I2C with the PiCowbell's STEMMA QT port pins (GP4 and GP5).
# STEMMA I2C on picowbell i2c = busio.I2C(board.GP5, board.GP4) display_bus = i2cdisplaybus.I2CDisplayBus(i2c, device_address=0x3D, reset=oled_reset) WIDTH = 128 HEIGHT = 64 offset_y = 5 display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=WIDTH, height=HEIGHT) # default display group splash = displayio.Group() display.root_group = splash
As the network connection is established, the text "Connecting..." is shown on the I2C display. First, a static IP address is set with set_ipv4_address(). You can edit the IP address ipv4 to change to the IP address of your choice. By default, it is 192.168.1.42.
Then, the Pico W connects to your SSID by accessing your SSID and SSID password in your .env file.
# connect to network
print()
print("Connecting to WiFi")
connect_text = "Connecting..."
connect_text_area = label.Label(
terminalio.FONT, text=connect_text, color=0xFFFFFF, x=0, y=offset_y
)
splash.append(connect_text_area)
# set static IP address
ipv4 = ipaddress.IPv4Address("192.168.1.42")
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(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
print("Connected to WiFi")
pool = socketpool.SocketPool(wifi.radio)
server = Server(pool, "/static", debug=True)
A few variables are created for the HTML script. The HTML is passed as an f-string, so variables from code.py can be included. temp_test holds the temperature reading from the DS18B20. You can adjust this to utilize either Celsius or Fahrenheit.
font_family is the font used for the CSS styling. You can change this to your preferred CSS font.
# variables for HTML # comment/uncomment desired temp unit # temp_test = str(ds18.temperature) # unit = "C" temp_test = str(c_to_f(ds18.temperature)) unit = "F" # font for HTML font_family = "monospace"
The HTML code is passed as an f-string in the webpage() function. The function returns a string that can be passed with HTTPResponse on a @server.route() function.
It's important to note that by using this method, you need to use double curly brackets ({{ and }}) if something in the HTML code actually needs to be inside curly brackets, such as certain parts of CSS style formatting.
# 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; padding: 2vh; font-size: 35px;}}
p{{font-size: 1.5rem;}}
.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; height: 50px;
width: 75%; font-size: 25px; text-align: center;}}
</style>
</head>
<body>
<title>Pico W HTTP Server</title>
<h1>Pico W HTTP Server</h1>
<p class="dotted">This is a Pico W running an HTTP server with CircuitPython.</p>
<p class="dotted">The current ambient temperature near the Pico W is
<span style="color: deeppink;">{temp_test}°{unit}</span></p>
<h1>Control the LED on the Pico W with these buttons:</h1>
<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" type="submit">PARTY!</button></a></p></form>
</body></html>
"""
return html
When the static IP address is accessed, the server serves the HTML code in webpage() with Response(). The content_type needs to be defined as "text/html".
The other routing option occurs when an HTTP POST request is sent to the server. When a POST request comes in, the text is decoded into raw_text. POST requests are sent when the buttons are pressed on the HTML site.
The buttons have names and values defined in the HTML code that are sent as a part of the POST request. When the POST request contains "ON", that means that the LED ON button was pressed on the site and the onboard LED on the Pico W is turned on. When a POST request contains "OFF", that means that the LED OFF button was pressed on the site and the onboard LED on the Pico W is turned off. Finally, when a POST request contains "party", it means that the PARTY! button was pressed on the site and the party_pin is toggled.
# route default static IP
@server.route("/")
def base(request: Request): # pylint: disable=unused-argument
# serve the HTML f string
# with content type text/html
return Response(request, f"{webpage()}", content_type='text/html')
# if a button is pressed on the site
@server.route("/", POST)
def buttonpress(request: Request):
# 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
parrot_pin.value = not parrot_pin.value
# reload site
return Response(request, f"{webpage()}", content_type='text/html')
The server starts up on the static IP address with server.start(). This is wrapped in a try/except statement in case of an OSError, causing the server to be unable to start. If this occurs, then the Pico W is reset with microcontroller.reset().
After the server has started up successfully, the text on the OLED is updated to say that it is connected along with your SSID name, IP address and the temperature reading from the DS18B20.
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()
ping_address = ipaddress.ip_address("8.8.4.4")
# text objects for screen
# connected to SSID text
connect_text_area.text = "Connected to:"
ssid_text = "%s" % os.getenv('WIFI_SSID')
ssid_text_area = label.Label(
terminalio.FONT, text=ssid_text, color=0xFFFFFF, x=0, y=offset_y+15
)
splash.append(ssid_text_area)
# display ip address
ip_text = "IP: %s" % wifi.radio.ipv4_address
ip_text_area = label.Label(
terminalio.FONT, text=ip_text, color=0xFFFFFF, x=0, y=offset_y+30
)
splash.append(ip_text_area)
# display temp reading
temp_text = "Temperature: %.02f F" % float(temp_test)
temp_text_area = label.Label(
terminalio.FONT, text=temp_text, color=0xFFFFFF, x=0, y=offset_y+45
)
splash.append(temp_text_area)
There is a party parrot sprite sheet that is shown as an animated sprite sequence when the PARTY! button is pressed on the HTML site. The sprite sheet is added to a second displayio.Group() called parrot_group. This lets you display either the default splash group with the server information text or the party parrot sprite sheet with parrot_group.
# party parrot display group
parrot_group = displayio.Group()
# load in party parrot bitmap
parrot_bit, parrot_pal = adafruit_imageload.load("/partyParrots64.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette)
parrot_grid = displayio.TileGrid(parrot_bit, pixel_shader=parrot_pal,
width=1, height=1,
tile_height=64, tile_width=64,
default_tile=1,
x=32, y=0)
parrot_group.append(parrot_grid)
The last portion before the loop are some variables. clock is a time.monotonic() device that will be used for timekeeping. parrot is a state machine to track if the party parrot animation should be playing. party is a time.monotonic() device for showing the party parrot animation and p is the variable for the party parrot tilegrid index.
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
In the loop, every 30 seconds, the Pico W sends out a ping to check the connection status. If the ping is not returned, then the OLED text is updated to Disconnected!. If a ping is returned, then the text remains as Connected to: with your SSID name. Additionally, the temperature reading from the DS18B20 is taken, updating the OLED text entry and the entry for the HTML f string.
while True:
try:
# every 30 seconds, ping server & update temp reading
if (clock + 30) < time.monotonic():
if wifi.radio.ping(ping_address) is None:
connect_text_area.text = "Disconnected!"
ssid_text_area.text = None
print("lost connection")
else:
connect_text_area.text = "Connected to:"
ssid_text_area.text = "%s" % os.getenv('WIFI_SSID')
print("connected")
clock = time.monotonic()
# comment/uncomment for desired units
# temp_test = str(ds18.temperature)
temp_test = str(c_to_f(ds18.temperature))
temp_text_area.text = "Temperature: %d F" % temp_test
If the parrot_pin.value is toggled to True by the PARTY! button on the HTML site, then the OLED shows the parrot_group tilegrid for the party parrot sprite sheet. Every 0.1 seconds, the tilegrid is advanced by 1 to play the animation. When the pin is toggled to False, then the display shows the splash group, displaying the server information.
#if parrot is True:
if parrot_pin.value is True:
# switch to party parrot display group
display.root_group = parrot_group
if (party + 0.1) < time.monotonic():
# the party parrot animation cycles
parrot_grid[0] = p
# p is the tilegrid index location
p = (p + 1) % 10
party = time.monotonic()
# if it isn't a party
else:
# show default display with info
display.root_group = splash
The final, and most important, part of the loop is server.poll(). This function polls the server for incoming and outgoing requests from both code.py and the HTML site.
# poll the server for incoming/outgoing requests server.poll()
Page last edited March 08, 2024
Text editor powered by tinymce.
Assembly
Plug the PiCowbell into the Pico W. The STEMMA port on the PiCowbell should be below the USB port on the Pico W.
Route the DS18B20 through the larger opening in the case. Slide the sensor through the clip on the top of the case.
Wrap the excess cable for the DS18B20 into the bottom of the case. Route the LED through the larger side opening.
Page last edited March 08, 2024
Text editor powered by tinymce.
Usage
After powering up your Pico W, your server will begin running on your static IP address. Access your HTML webpage by opening a browser and navigating to the IP address. You can confirm your IP address by checking the OLED connected to the Pico W.
You should see your HTML code displayed in your browser window. The temperature reading from the DS18B20 will be shown towards the top of the screen.
Click the PARTY! button on the webpage to start and stop the party parrot animation on the OLED. The slow change RGB LED will also turn on and off.
You can adapt this code to suit your project needs. You can change the look of the HTML page by changing the CSS styling or change the CircuitPython portion to control a relay or log multiple sensors around your home. For more information on the CircuitPython HTTPServer library, be sure to check out the library documentation on Read the Docs.
Page last edited March 08, 2024
Text editor powered by tinymce.