Skip to content

Commit 3b1ee3d

Browse files
code for v2
1 parent 76d26f3 commit 3b1ee3d

File tree

2 files changed

+303
-88
lines changed

2 files changed

+303
-88
lines changed

code/code.py

Lines changed: 225 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
"""
22
Code to serve up a webserver that controls LEDs and toggles modes. Relies on some environment variables set in the settings.toml.
33
"""
4+
import alarm
5+
from analogio import AnalogIn
6+
import digitalio
47
import os
58
import time
69
import board
@@ -16,29 +19,41 @@
1619
SSID_NAME = os.getenv("SSID_NAME")
1720
SSID_PASS = os.getenv("WLAN_PASS")
1821
NUM_PIXELS = 10
19-
LED_MODE = "rainbow"
22+
MODE_FORM_KEY = "mode"
23+
MODE_RAINBOW = "rainbow_mode"
24+
MODE_SLEEP = "sleep"
25+
MODE_COLOR_PULSE = "color_pulse_mode"
26+
RESET_VARS_FORM_KEY = "reset_variables"
27+
SETTINGS_URL = "/settings"
28+
RAINBOW_SETTINGS_URL = "/settings/rainbow"
29+
COLOR_PULSE_SETTINGS_URL = "/settings/color_pulse"
30+
31+
TARGET_LOOP_RATE = 60 # hz
32+
TARGET_LOOP_SECONDS = 1 / TARGET_LOOP_RATE
33+
LAST_LOOP_TIME = time.time()
34+
LED_MODE = MODE_RAINBOW
2035
BRIGHTNESS = 0.5
21-
BRIGHTNESS_LOWER_BOUND = 0.2
36+
BRIGHTNESS_LOWER_BOUND = 0.0
2237
BRIGHTNESS_UPPER_BOUND = 1.0
23-
BRIGHTNESS_INCREMENT = 0.1
38+
BRIGHTNESS_FORM_KEY = "brightness"
39+
2440
RAINBOW_SPEED = 0
2541
RAINBOW_MIN_SPEED = 0
2642
RAINBOW_MAX_SPEED = 0.1
27-
RAINBOW_SPEED_INCREMENT = 0.002
43+
RAINBOW_SPEED_FORM_KEY = "rainbow_speed"
2844
COLOR_R = 255
45+
COLOR_R_FORM_KEY = "r"
2946
COLOR_G = 0
47+
COLOR_G_FORM_KEY = "g"
3048
COLOR_B = 255
49+
COLOR_B_FORM_KEY = "b"
3150
COLOR_UPPER_BOUND = 255
3251
COLOR_LOWER_BOUND = 0
33-
COLOR_INCREMENT = 10
3452
# font for HTML
3553
FONT_FAMILY = "monospace"
3654

37-
# set static IP address
38-
ipv4 = ipaddress.IPv4Address("192.168.1.42")
39-
netmask = ipaddress.IPv4Address("255.255.255.0")
40-
gateway = ipaddress.IPv4Address("192.168.1.1")
41-
wifi.radio.set_ipv4_address(ipv4=ipv4,netmask=netmask,gateway=gateway)
55+
BATTERY_LEVEL = 3.7
56+
4257
wifi.radio.start_ap(ssid=SSID_NAME, password=SSID_PASS)
4358
print("started network")
4459
pool = socketpool.SocketPool(wifi.radio)
@@ -47,10 +62,28 @@
4762
# Update this to match the number of NeoPixel LEDs connected to your board.
4863
ORDER = neopixel.RGB
4964
PIXEL_PIN = board.D5
65+
BUTTON_PIN = board.BUTTON
66+
INBUILT_NEOPIXEL_PIN = board.NEOPIXEL_I2C_POWER
67+
INBUILT_NEOPIXEL = digitalio.DigitalInOut(INBUILT_NEOPIXEL_PIN)
68+
INBUILT_NEOPIXEL.direction = digitalio.Direction.OUTPUT
69+
INBUILT_NEOPIXEL.value = False
5070

5171
pixels = neopixel.NeoPixel(
5272
PIXEL_PIN, NUM_PIXELS, brightness=BRIGHTNESS, auto_write=False, pixel_order=ORDER)
5373

74+
BATTERY_PIN = board.VOLTAGE_MONITOR
75+
BATTERY_IN = AnalogIn(BATTERY_PIN)
76+
PIN_ALARM = alarm.pin.PinAlarm(pin=BUTTON_PIN, value=False)
77+
78+
def update_battery():
79+
global BATTERY_IN, BATTERY_LEVEL
80+
measured_voltage = BATTERY_IN.value
81+
# docs claim below, but it seems this isn't accurate
82+
# measured_voltage *= 2
83+
measured_voltage /= 1000
84+
BATTERY_LEVEL = measured_voltage
85+
86+
5487
def web_page():
5588
html = f"""<!DOCTYPE html>
5689
<html lang="en">
@@ -75,78 +108,199 @@ def web_page():
75108
<h1>Crystal Ball Web Server</h1>
76109
<div class="paragraph">
77110
<p class="dotted">This is a webserver to control the lights in the crystal ball.</p>
78-
</div>
111+
</div>
112+
<h1>Current Settings</h1>
113+
<div class="paragraph">
114+
<p class="dotted">Mode: <span style="color: deeppink;">{LED_MODE}</span></p>
115+
</div>
116+
<div class="paragraph">
117+
<p class="dotted">Battery: <span style="color: deeppink;">{BATTERY_LEVEL:.2f}</span></p>
118+
</div>
119+
<div class="paragraph">
120+
<p class="dotted">Brightness: <span style="color: deeppink;">{BRIGHTNESS:.2f}</span></p>
121+
</div>
122+
<div class="paragraph">
123+
<p class="dotted">Rainbow Speed: <span style="color: deeppink;">{RAINBOW_SPEED:.3f}</span></p>
124+
</div>
125+
<div class="paragraph">
126+
<p class="dotted">Red value: <span style="color: deeppink;">{COLOR_R}</span></p>
127+
</div>
128+
<div class="paragraph">
129+
<p class="dotted">Green value: <span style="color: deeppink;">{COLOR_G}</span></p>
130+
</div>
79131
<div class="paragraph">
80-
<p class="dotted">The current mode is <span style="color: deeppink;">{LED_MODE}</span></p>
132+
<p class="dotted">Blue value: <span style="color: deeppink;">{COLOR_B}</span></p>
81133
</div>
82-
<h1>Lights Control</h1>
83-
<form accept-charset="utf-8" method="POST">
84-
<button class="button" name="Set Rainbow Mode" value="rainbow_mode" type="submit">Set Rainbow</button></a></p></form>
85-
<p><form accept-charset="utf-8" method="POST">
86-
<button class="button" name="Set Color Pulse Mode" value="color_pulse_mode" type="submit">Set Color Pulse</button></a></p></form>
87-
<p><form accept-charset="utf-8" method="POST">
88-
<button class="button" name="Increment Brightness" value="increment_brightness" type="submit">Increment Brightness ({BRIGHTNESS_LOWER_BOUND} to {BRIGHTNESS_UPPER_BOUND}, increment: {BRIGHTNESS_INCREMENT}, currently: {BRIGHTNESS})</button></a></p></form>
89-
<p><form accept-charset="utf-8" method="POST">
90-
<button class="button" name="Increment Rainbow Speed" value="increment_rainbow_speed" type="submit">Increment Rainbow Speed ({RAINBOW_MIN_SPEED} to {RAINBOW_MAX_SPEED}, increment: {RAINBOW_SPEED_INCREMENT}, currently: {RAINBOW_SPEED})</button></a></p></form>
91-
<p><form accept-charset="utf-8" method="POST">
92-
<button class="button" name="Color Pulse: Red" value="red" type="submit">Increment Color Pulse Red ({COLOR_LOWER_BOUND} to {COLOR_UPPER_BOUND}, increment: {COLOR_INCREMENT}, currently: {COLOR_R})</button></a></p></form>
93-
<p><form accept-charset="utf-8" method="POST">
94-
<button class="button" name="Color Pulse: Green" value="green" type="submit">Increment Color Pulse Green ({COLOR_LOWER_BOUND} to {COLOR_UPPER_BOUND}, increment: {COLOR_INCREMENT}, currently: {COLOR_G})</button></a></p></form>
95-
<p><form accept-charset="utf-8" method="POST">
96-
<button class="button" name="Color Pulse: Blue" value="blue" type="submit">Increment Color Pulse Blue ({COLOR_LOWER_BOUND} to {COLOR_UPPER_BOUND}, increment: {COLOR_INCREMENT}, currently: {COLOR_B})</button></a></p></form>
97-
<p><form accept-charset="utf-8" method="POST">
98-
<button class="button" name="Reset Variables" value="reset_variables" type="submit">Reset All Variables to Defaults</button></a></p></form>
134+
135+
<h1>Controls</h1>
136+
<h2>General Controls</h2>
137+
<p>
138+
<form action="{SETTINGS_URL}" method="post" enctype="text/plain">
139+
<button class="button" name="{MODE_FORM_KEY}" value="{MODE_RAINBOW}" type="submit">Set Rainbow Mode</button>
140+
</form>
141+
</p>
142+
<p>
143+
<form action="{SETTINGS_URL}" method="post" enctype="text/plain">
144+
<button class="button" name="{MODE_FORM_KEY}" value="{MODE_COLOR_PULSE}" type="submit">Set Color Pulse Mode</button>
145+
</form>
146+
</p>
147+
<p>
148+
<form action="{SETTINGS_URL}" method="post" enctype="text/plain">
149+
<button class="button" name="{MODE_FORM_KEY}" value="{MODE_SLEEP}" type="submit">Set Deep Sleep</button>
150+
</form>
151+
</p>
152+
<p>
153+
<form action="{SETTINGS_URL}" method="post" enctype="text/plain">
154+
<input type="text" name="{BRIGHTNESS_FORM_KEY}" placeholder="Set brightness, {BRIGHTNESS_LOWER_BOUND} to {BRIGHTNESS_UPPER_BOUND}.">
155+
<input type="submit" value="Submit">
156+
</form>
157+
</p>
158+
<p>
159+
<form action="{SETTINGS_URL}" method="post" enctype="text/plain">
160+
<button class="button" name="{RESET_VARS_FORM_KEY}" value="yes" type="submit">Reset Variables</button>
161+
</form>
162+
</p>
163+
<h2>Rainbow Controls</h2>
164+
<p>
165+
<form action="{RAINBOW_SETTINGS_URL}" method="post" enctype="text/plain">
166+
<input type="text" name="{RAINBOW_SPEED_FORM_KEY}" placeholder="Set rainbow speed, {RAINBOW_MIN_SPEED} to {RAINBOW_MAX_SPEED}.">
167+
<input type="submit" value="Submit">
168+
</form>
169+
</p>
170+
<h2>Color Pulse Controls</h3>
171+
<p>
172+
<form action="{COLOR_PULSE_SETTINGS_URL}" method="post" enctype="text/plain">
173+
<input type="text" name="{COLOR_R_FORM_KEY}" placeholder="Set red value, {COLOR_LOWER_BOUND} to {COLOR_UPPER_BOUND}.">
174+
<input type="submit" value="Submit">
175+
</form>
176+
</p>
177+
<p>
178+
<form action="{COLOR_PULSE_SETTINGS_URL}" method="post" enctype="text/plain">
179+
<input type="text" name="{COLOR_G_FORM_KEY}" placeholder="Set green value, {COLOR_LOWER_BOUND} to {COLOR_UPPER_BOUND}.">
180+
<input type="submit" value="Submit">
181+
</form>
182+
</p>
183+
<p>
184+
<form action="{COLOR_PULSE_SETTINGS_URL}" method="post" enctype="text/plain">
185+
<input type="text" name="{COLOR_B_FORM_KEY}" placeholder="Set blue value, {COLOR_LOWER_BOUND} to {COLOR_UPPER_BOUND}.">
186+
<input type="submit" value="Submit">
187+
</form>
188+
</p>
99189
</body></html>
100190
"""
101191
return html
102192

103193

104-
# route default static IP
105-
@server.route("/")
106-
def base(request: Request): # pylint: disable=unused-argument
107-
# serve the HTML f string
108-
# with content type text/html
109-
return Response(request, f"{web_page()}", content_type='text/html')
110-
111194
def bound_number(cur, min_bound, max_bound, increment):
112195
new_val = cur + increment
113-
if new_val >= max_bound:
196+
if new_val > max_bound:
197+
new_val = max_bound
198+
if new_val < min_bound:
114199
new_val = min_bound
115200
return new_val
116201

117-
# if a button is pressed on the site
118-
@server.route("/", POST)
119-
def buttonpress(request: Request):
120-
global LED_MODE, BRIGHTNESS, RAINBOW_SPEED, COLOR_R, COLOR_G, COLOR_B
121-
# get the raw text
122-
raw_text = request.raw_request.decode("utf8")
123-
print(raw_text)
124-
if "rainbow_mode" in raw_text:
125-
LED_MODE = "rainbow"
126-
elif "color_pulse_mode" in raw_text:
127-
LED_MODE = "color_pulse"
128-
elif "increment_brightness" in raw_text:
129-
BRIGHTNESS = bound_number(BRIGHTNESS, BRIGHTNESS_LOWER_BOUND, BRIGHTNESS_UPPER_BOUND, BRIGHTNESS_INCREMENT)
202+
@server.route(SETTINGS_URL, [POST])
203+
def settings(request: Request):
204+
205+
brightness_req = request.form_data.get(BRIGHTNESS_FORM_KEY, None)
206+
if not brightness_req is None:
207+
global BRIGHTNESS
208+
brightness = BRIGHTNESS
209+
try:
210+
brightness = float(brightness_req.strip())
211+
except ValueError:
212+
pass
213+
BRIGHTNESS = bound_number(brightness, BRIGHTNESS_LOWER_BOUND, BRIGHTNESS_UPPER_BOUND, 0)
130214
pixels.brightness = BRIGHTNESS
131-
elif "increment_rainbow_speed" in raw_text:
132-
RAINBOW_SPEED = bound_number(RAINBOW_SPEED, RAINBOW_MIN_SPEED, RAINBOW_MAX_SPEED, RAINBOW_SPEED_INCREMENT)
133-
elif "red" in raw_text:
134-
COLOR_R = bound_number(COLOR_R, COLOR_LOWER_BOUND, COLOR_UPPER_BOUND, COLOR_INCREMENT)
135-
elif "green" in raw_text:
136-
COLOR_G = bound_number(COLOR_G, COLOR_LOWER_BOUND, COLOR_UPPER_BOUND, COLOR_INCREMENT)
137-
elif "blue" in raw_text:
138-
COLOR_B = bound_number(COLOR_B, COLOR_LOWER_BOUND, COLOR_UPPER_BOUND, COLOR_INCREMENT)
139-
elif "reset_variables" in raw_text:
140-
LED_MODE = "rainbow"
215+
216+
mode = request.form_data.get(MODE_FORM_KEY, None)
217+
if not mode is None:
218+
mode = mode.strip()
219+
global LED_MODE
220+
if mode == MODE_RAINBOW:
221+
LED_MODE = MODE_RAINBOW
222+
elif mode == MODE_COLOR_PULSE:
223+
LED_MODE = MODE_COLOR_PULSE
224+
elif mode == MODE_SLEEP:
225+
global server
226+
server.stop()
227+
wifi.radio.stop_ap()
228+
pixels.fill((0, 0, 0))
229+
pixels.show()
230+
alarm.exit_and_deep_sleep_until_alarms(PIN_ALARM)
231+
else:
232+
pass
233+
234+
reset_vars = request.form_data.get(RESET_VARS_FORM_KEY, None)
235+
if not reset_vars is None:
236+
global LED_MODE, BRIGHTNESS, RAINBOW_SPEED, COLOR_R, COLOR_B, COLOR_G
237+
LED_MODE = MODE_RAINBOW
141238
BRIGHTNESS = 0.5
142239
RAINBOW_SPEED = 0
143240
COLOR_R = 255
144241
COLOR_G = 0
145242
COLOR_B = 255
146-
else:
147-
pass
148243
return Response(request, f"{web_page()}", content_type='text/html')
149244

245+
246+
@server.route(RAINBOW_SETTINGS_URL, [POST])
247+
def settings_rainbow(request: Request):
248+
249+
rainbow_speed = request.form_data.get(RAINBOW_SPEED_FORM_KEY, None)
250+
if not rainbow_speed is None:
251+
global RAINBOW_SPEED
252+
speed = RAINBOW_SPEED
253+
try:
254+
speed = float(rainbow_speed.strip())
255+
except ValueError:
256+
pass
257+
RAINBOW_SPEED = bound_number(speed, RAINBOW_MIN_SPEED, RAINBOW_MAX_SPEED, 0)
258+
return Response(request, f"{web_page()}", content_type='text/html')
259+
260+
261+
@server.route(COLOR_PULSE_SETTINGS_URL, [POST])
262+
def settings_color_pulse(request: Request):
263+
264+
r_val = request.form_data.get(COLOR_R_FORM_KEY, None)
265+
if not r_val is None:
266+
global COLOR_R
267+
r = COLOR_R
268+
try:
269+
r = int(r_val.strip())
270+
except ValueError:
271+
pass
272+
COLOR_R = bound_number(r, COLOR_LOWER_BOUND, COLOR_UPPER_BOUND, 0)
273+
274+
g_val = request.form_data.get(COLOR_G_FORM_KEY, None)
275+
if not g_val is None:
276+
global COLOR_G
277+
g = COLOR_G
278+
try:
279+
g = int(g_val.strip())
280+
except ValueError:
281+
pass
282+
COLOR_G = bound_number(g, COLOR_LOWER_BOUND, COLOR_UPPER_BOUND, 0)
283+
284+
b_val = request.form_data.get(COLOR_B_FORM_KEY, None)
285+
if not b_val is None:
286+
global COLOR_B
287+
b = COLOR_B
288+
try:
289+
b = int(b_val.strip())
290+
except ValueError:
291+
pass
292+
COLOR_B = bound_number(b, COLOR_LOWER_BOUND, COLOR_UPPER_BOUND, 0)
293+
294+
return Response(request, f"{web_page()}", content_type='text/html')
295+
296+
# route default static IP
297+
@server.route("/")
298+
def base(request: Request): # pylint: disable=unused-argument
299+
# serve the HTML f string
300+
# with content type text/html
301+
return Response(request, f"{web_page()}", content_type='text/html')
302+
303+
150304
def rainbow(speed):
151305
for j in range(255):
152306
for i in range(NUM_PIXELS):
@@ -162,8 +316,6 @@ def color_pulse():
162316

163317

164318
def main():
165-
166-
167319
print("starting server..")
168320
# startup the server
169321
try:
@@ -174,13 +326,19 @@ def main():
174326
time.sleep(5)
175327
print("restarting..")
176328
microcontroller.reset()
177-
329+
global LAST_LOOP_TIME, TARGET_LOOP_SECONDS
178330
while True:
179-
if LED_MODE == "rainbow":
331+
seconds_before_loop = (time.time() - LAST_LOOP_TIME) - TARGET_LOOP_SECONDS
332+
if seconds_before_loop < 0:
333+
time.sleep(abs(seconds_before_loop))
334+
LAST_LOOP_TIME = time.time()
335+
336+
if LED_MODE == MODE_RAINBOW:
180337
rainbow(RAINBOW_SPEED)
181-
elif LED_MODE == "color_pulse":
338+
elif LED_MODE == MODE_COLOR_PULSE:
182339
color_pulse()
183340
# poll the server for incoming/outgoing requests
184341
server.poll()
342+
update_battery()
185343

186344
main()

0 commit comments

Comments
 (0)