Add deep sleep and single-button UX with heartbeat LED
Replace the dual-switch handler with one button: short press cycles patterns, long press arms deep sleep with ext1 GPIO wake. Add a heartbeat LED task and fix the IMU test I2C pins to GPIO21/GPIO6. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
99
src/main.py
99
src/main.py
@@ -8,6 +8,8 @@ from patterns import Patterns
|
||||
import gc
|
||||
import utime
|
||||
import machine
|
||||
import esp32
|
||||
import network
|
||||
import time
|
||||
import wifi
|
||||
import json
|
||||
@@ -15,8 +17,21 @@ from p2p import p2p
|
||||
from machine import ADC, Pin, I2C
|
||||
from lsm6ds3 import LSM6DS3, is_lsm6ds3
|
||||
|
||||
# XIAO ESP32-C3: deep-sleep GPIO wake only on GPIO 0-5 (header D0-D3).
|
||||
SW_PIN = 3 # D1 — switch to GND (short press cycle, long press sleep/wake)
|
||||
LED_PIN = 8 # D8 — onboard LED (active low)
|
||||
SLEEP_HOLD_MS = 2000
|
||||
SLEEP_RELEASE_WAIT_MS = 500 # wait after release before arming wake
|
||||
|
||||
async def main():
|
||||
sw = Pin(SW_PIN, Pin.IN, Pin.PULL_UP)
|
||||
if machine.reset_cause() == machine.DEEPSLEEP_RESET:
|
||||
print("woke from deep sleep, reason:", esp32.wake_reason())
|
||||
while sw.value() == 0:
|
||||
utime.sleep_ms(50)
|
||||
|
||||
settings = Settings()
|
||||
led = Pin(LED_PIN, Pin.OUT, value=1)
|
||||
|
||||
patterns = Patterns(settings["led_pin"], settings["num_leds"], selected=settings["pattern"])
|
||||
if settings["color_order"] == "rbg": color_order = (1, 5, 3)
|
||||
@@ -33,6 +48,14 @@ async def main():
|
||||
patterns.tick()
|
||||
await asyncio.sleep_ms(0)
|
||||
|
||||
async def heartbeat():
|
||||
while True:
|
||||
wdt.feed()
|
||||
led.value(0)
|
||||
await asyncio.sleep_ms(80)
|
||||
led.value(1)
|
||||
await asyncio.sleep_ms(920)
|
||||
|
||||
async def system():
|
||||
adc = ADC(Pin(0), atten=ADC.ATTN_11DB)
|
||||
while True:
|
||||
@@ -45,30 +68,66 @@ async def main():
|
||||
print("%.3f V" % voltage)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def sw():
|
||||
sw1 = machine.Pin(8, machine.Pin.IN, machine.Pin.PULL_UP)
|
||||
sw2 = machine.Pin(9, machine.Pin.IN, machine.Pin.PULL_UP)
|
||||
def enter_deep_sleep():
|
||||
patterns.select("off")
|
||||
patterns.sync()
|
||||
utime.sleep_ms(100)
|
||||
led.value(1)
|
||||
for iface in (network.STA_IF, network.AP_IF):
|
||||
wl = network.WLAN(iface)
|
||||
if wl.active():
|
||||
wl.active(False)
|
||||
if sw.value() == 0:
|
||||
print("button still pressed, not sleeping")
|
||||
return
|
||||
esp32.wake_on_ext1(pins=(sw,), level=esp32.WAKEUP_ANY_LOW)
|
||||
print("deep sleep — press GPIO %d to wake" % SW_PIN)
|
||||
machine.deepsleep()
|
||||
|
||||
async def prepare_deep_sleep():
|
||||
print("release button to sleep")
|
||||
while sw.value() == 0:
|
||||
wdt.feed()
|
||||
await asyncio.sleep_ms(50)
|
||||
await asyncio.sleep_ms(SLEEP_RELEASE_WAIT_MS)
|
||||
if sw.value() == 0:
|
||||
print("button pressed again, sleep cancelled")
|
||||
return
|
||||
enter_deep_sleep()
|
||||
|
||||
async def switch_loop():
|
||||
p = ["on", "rainbow_cycle", "theater_chase",
|
||||
"color_transition", "flicker",
|
||||
"bidirectional_scanner"]
|
||||
|
||||
|
||||
pattern_index = 0
|
||||
press_start = None
|
||||
sleep_pending = False
|
||||
|
||||
print(p)
|
||||
pressed1 = 0
|
||||
pressed2 = 0
|
||||
while True:
|
||||
if sw1.value() == 0:
|
||||
patterns.select(p[pattern_index])
|
||||
print(p[pattern_index])
|
||||
pattern_index = pattern_index + 1
|
||||
if pattern_index > len(p)-1:
|
||||
pattern_index = 0
|
||||
print("pressed 1")
|
||||
if sw2.value() == 0:
|
||||
patterns.select("off")
|
||||
await asyncio.sleep_ms(200)
|
||||
if sleep_pending:
|
||||
await prepare_deep_sleep()
|
||||
sleep_pending = False
|
||||
press_start = None
|
||||
continue
|
||||
|
||||
if sw.value() == 0:
|
||||
if press_start is None:
|
||||
press_start = utime.ticks_ms()
|
||||
elif utime.ticks_diff(utime.ticks_ms(), press_start) >= SLEEP_HOLD_MS:
|
||||
sleep_pending = True
|
||||
press_start = None
|
||||
else:
|
||||
if press_start is not None:
|
||||
held = utime.ticks_diff(utime.ticks_ms(), press_start)
|
||||
if 50 < held < SLEEP_HOLD_MS:
|
||||
patterns.select(p[pattern_index])
|
||||
print(p[pattern_index])
|
||||
pattern_index = (pattern_index + 1) % len(p)
|
||||
press_start = None
|
||||
|
||||
await asyncio.sleep_ms(50)
|
||||
|
||||
telemetry = {
|
||||
"voltage": 0.0,
|
||||
@@ -171,18 +230,20 @@ async def main():
|
||||
print("IMU read error:", e)
|
||||
await asyncio.sleep_ms(200)
|
||||
|
||||
wdt = machine.WDT(timeout=10000)
|
||||
wdt.feed()
|
||||
|
||||
w = web(settings, patterns, telemetry)
|
||||
print(settings)
|
||||
# start the server in a bacakground task
|
||||
print("Starting")
|
||||
server = asyncio.create_task(w.start_server(host="0.0.0.0", port=80))
|
||||
wdt = machine.WDT(timeout=10000)
|
||||
wdt.feed()
|
||||
|
||||
asyncio.create_task(tick())
|
||||
asyncio.create_task(heartbeat())
|
||||
asyncio.create_task(p2p(settings, patterns))
|
||||
asyncio.create_task(system())
|
||||
asyncio.create_task(sw())
|
||||
asyncio.create_task(switch_loop())
|
||||
if imu_sensor is not None:
|
||||
asyncio.create_task(imu_loop())
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from lsm6ds3 import LSM6DS3, is_lsm6ds3
|
||||
|
||||
|
||||
# XIAO ESP32C3 default I2C pins: SDA=GPIO6 (D4), SCL=GPIO7 (D5)
|
||||
i2c = I2C(0, scl=Pin(23), sda=Pin(22), freq=400000)
|
||||
i2c = I2C(0, scl=Pin(20), sda=Pin(21), freq=400000)
|
||||
|
||||
|
||||
def scan_i2c():
|
||||
|
||||
Reference in New Issue
Block a user