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