Neale Pickett
·
2025-06-29
main.py
1import gc
2import json
3import machine
4import network
5import requests
6import time
7
8import blinker
9
10Millisecond = 1
11Second = 1000 * Millisecond
12Minute = 60 * Second
13Hour = 60 * Minute
14
15black = (0, 0, 0)
16red = (255, 0, 0)
17green = (0, 255, 0)
18blue = (0, 0, 255)
19cyan = (0, 85, 160)
20yellow = (160, 85, 0)
21ok_colors = tuple((0, i, 0) for i in (191, 223, 255, 223))
22
23gc.enable()
24
25# Set up interrupt for pressing the button
26do_checkin = False
27def buttonPress(p):
28 global do_checkin
29 do_checkin = True
30 blinker.set(green, green, cyan, now=True)
31buttonPin = machine.Pin(4, machine.Pin.IN, machine.Pin.PULL_UP)
32buttonPin.irq(buttonPress, trigger=machine.Pin.IRQ_FALLING, wake=machine.SLEEP|machine.DEEPSLEEP)
33
34# Set up interrupt for pressing the on-board boot button
35running = True
36def bootPress(p):
37 global running
38 running = False
39bootPin = machine.Pin(9, machine.Pin.IN, machine.Pin.PULL_UP)
40bootPin.irq(bootPress, trigger=machine.Pin.IRQ_FALLING, wake=machine.SLEEP|machine.DEEPSLEEP)
41
42# Wireless connection
43wlan = network.WLAN(network.WLAN.IF_STA)
44wlan.active(True)
45wlan.config(reconnects=3)
46def connect(config):
47 if wlan.isconnected():
48 return
49 print("Trying to connect to wlan")
50 blinker.set(cyan, cyan, cyan, black)
51 networks = []
52 while running and not wlan.isconnected():
53 if not networks:
54 networks = list(config["networks"].items())
55 ssid, psk = networks.pop()
56 print(" ", ssid)
57 wlan.connect(ssid, psk)
58 time.sleep(10)
59 print("Connected!")
60
61def fetch(url, method="GET"):
62 print(method, url, end="")
63 code, reason, resp = -1, "Internal error", None
64 try:
65 if method == "GET":
66 resp = requests.get(url)
67 elif method == "POST":
68 resp = requests.post(url)
69 else:
70 reason = "Unsupported Method: %s" % method
71 except Exception as err:
72 reason = err
73
74 if not resp:
75 blinker.set(red, yellow, cyan, blue)
76 print("ERR", reason)
77 else:
78 code = resp.status_code
79 print(" ", resp.status_code, resp.reason.decode("ascii"))
80 return code
81
82
83def main():
84 global do_checkin
85
86 print("Loading configuration")
87 blinker.set(yellow, black)
88 with open("config.json") as f:
89 config = json.load(f)
90 network.hostname(config.get("hostname", "betsy-button"))
91
92 print("Running main loop")
93 next_update = 0
94 while running:
95 connect(config)
96 now = time.time() * Second
97
98 if do_checkin:
99 code = fetch(config["url"], method="POST")
100 if code == 200:
101 do_checkin = False
102 next_update = 0
103 elif now >= next_update:
104 code = fetch(config["url"])
105 if code == 200:
106 blinker.set(*ok_colors)
107 next_update = now + (2 * Hour)
108 elif code in (304, 404):
109 blinker.set(red, red, red, red, red, black, red, black)
110 next_update = now + (2 * Hour)
111 else:
112 next_update = now + (2 * Minute)
113 # Keep flashing the loading or error colors
114
115 # If the device has only been up a little while,
116 # poll more frequently.
117 # This allows users to test things out to make sure it all works.
118 if time.ticks_ms() < 5 * Minute:
119 next_update = now + (10 * Second)
120
121 gc.collect()
122 if next_update:
123 time.sleep(2)
124
125try:
126 main()
127finally:
128 print("Exiting to MicroPython REPL")
129 blinker.set((5, 5, 3))