18 Commits

2 changed files with 70 additions and 43 deletions

View File

@@ -5,7 +5,7 @@ Connects WiFi, runs OTA update, then hands off to main.py.
Keep this file as simple as possible — it is never OTA-updated itself Keep this file as simple as possible — it is never OTA-updated itself
(it lives outside the repo folder) so bugs here require USB to fix. (it lives outside the repo folder) so bugs here require USB to fix.
""" """
import gauge #import gauge
import network import network
import gc import gc
import utime import utime
@@ -60,5 +60,3 @@ else:
# main.py runs automatically after boot.py # main.py runs automatically after boot.py

101
gauge.py
View File

@@ -28,16 +28,6 @@ import gc
from umqtt.robust import MQTTClient from umqtt.robust import MQTTClient
from machine import UART from machine import UART
# Activate WiFi driver before any heavy heap allocation so it can claim its
# contiguous DRAM block before the Python heap fragments the address space.
# Only activate if not already running (e.g. boot.py may have started it).
gc.collect()
_early_wlan = network.WLAN(network.STA_IF)
if not _early_wlan.active():
_early_wlan.active(True)
del _early_wlan
gc.collect()
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Logging # Logging
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -161,19 +151,27 @@ ARDUINO_TX_PIN = int(_cfg.get("arduino_tx_pin", 17))
ARDUINO_RX_PIN = int(_cfg.get("arduino_rx_pin", 16)) ARDUINO_RX_PIN = int(_cfg.get("arduino_rx_pin", 16))
ARDUINO_BAUD = int(_cfg.get("arduino_baud", 115200)) ARDUINO_BAUD = int(_cfg.get("arduino_baud", 115200))
_arduino = None
def _ensure_arduino():
global _arduino
if _arduino is None:
_arduino = UART(ARDUINO_UART_ID, baudrate=ARDUINO_BAUD, tx=ARDUINO_TX_PIN, rx=ARDUINO_RX_PIN, timeout=10) _arduino = UART(ARDUINO_UART_ID, baudrate=ARDUINO_BAUD, tx=ARDUINO_TX_PIN, rx=ARDUINO_RX_PIN, timeout=10)
return _arduino
def arduino_send(cmd): def arduino_send(cmd):
"""Send a newline-terminated command to the Arduino.""" """Send a newline-terminated command to the Arduino."""
_arduino.write((cmd + "\n").encode()) _ensure_arduino().write((cmd + "\n").encode())
info(f"Arduino → {cmd}") info(f"Arduino → {cmd}")
def arduino_recv(): def arduino_recv():
"""Print any lines waiting in the Arduino RX buffer.""" """Print any lines waiting in the Arduino RX buffer."""
while _arduino.any(): uart = _ensure_arduino()
line = _arduino.readline() while uart.any():
line = uart.readline()
if line: if line:
print(f"[{_ts()}] ARDU {line.decode().strip()}") print(f"[{_ts()}] ARDU {line.decode().strip()}")
@@ -540,11 +538,14 @@ _WIFI_CONNECT_ATTEMPTS = 3
def _reset_wifi_interface(): def _reset_wifi_interface():
global _wifi_sta global _wifi_sta
_wifi_sta = network.WLAN(network.STA_IF) _wifi_sta = network.WLAN(network.STA_IF)
if _wifi_sta.active(): if not _wifi_sta.active():
_wifi_sta.active(False)
utime.sleep_ms(200)
_wifi_sta.active(True) _wifi_sta.active(True)
utime.sleep_ms(500) utime.sleep_ms(500)
try:
_wifi_sta.disconnect()
except Exception:
pass
utime.sleep_ms(1000)
def connect_wifi(ssid, password, timeout_s=15, force_reconnect=False): def connect_wifi(ssid, password, timeout_s=15, force_reconnect=False):
@@ -561,6 +562,7 @@ def connect_wifi(ssid, password, timeout_s=15, force_reconnect=False):
last_error = None last_error = None
for attempt in range(_WIFI_CONNECT_ATTEMPTS): for attempt in range(_WIFI_CONNECT_ATTEMPTS):
info(f"WiFi connecting to '{ssid}' (attempt {attempt + 1}/{_WIFI_CONNECT_ATTEMPTS}) ...") info(f"WiFi connecting to '{ssid}' (attempt {attempt + 1}/{_WIFI_CONNECT_ATTEMPTS}) ...")
if not _wifi_sta.isconnected():
_reset_wifi_interface() _reset_wifi_interface()
try: try:
_wifi_sta.connect(ssid, password) _wifi_sta.connect(ssid, password)
@@ -576,7 +578,7 @@ def connect_wifi(ssid, password, timeout_s=15, force_reconnect=False):
info(f" SSID : {ssid}") info(f" SSID : {ssid}")
info(f" MAC : {mac}") info(f" MAC : {mac}")
info(f" IP : {ip} mask:{mask} gw:{gw} dns:{dns}") info(f" IP : {ip} mask:{mask} gw:{gw} dns:{dns}")
utime.sleep_ms(500) utime.sleep_ms(2000)
return ip return ip
except Exception as e: except Exception as e:
last_error = e last_error = e
@@ -601,7 +603,7 @@ def check_wifi():
log_err("WiFi lost connection — attempting reconnect...") log_err("WiFi lost connection — attempting reconnect...")
try: try:
ip = connect_wifi(WIFI_SSID, WIFI_PASSWORD, timeout_s=15, force_reconnect=True) ip = connect_wifi(WIFI_SSID, WIFI_PASSWORD, timeout_s=15)
info(f"WiFi reconnected! IP:{ip}") info(f"WiFi reconnected! IP:{ip}")
except Exception as e: except Exception as e:
log_err(f"WiFi reconnect failed: {e}") log_err(f"WiFi reconnect failed: {e}")
@@ -911,10 +913,6 @@ def connect_mqtt():
except Exception as e: except Exception as e:
last_error = e last_error = e
log_err(f"MQTT connect attempt {attempt + 1} failed: {type(e).__name__}: {e}") log_err(f"MQTT connect attempt {attempt + 1} failed: {type(e).__name__}: {e}")
try:
client.sock.close()
except Exception:
pass
gc.collect() gc.collect()
utime.sleep_ms(1000) utime.sleep_ms(1000)
@@ -922,14 +920,6 @@ def connect_mqtt():
raise last_error raise last_error
_mqtt_check_interval_ms = 30000
_last_mqtt_check = 0
_discovery_queue = []
_discovery_idx = 0
_last_discovery_ms = 0
_DISCOVERY_INTERVAL_MS = 350
def check_mqtt(): def check_mqtt():
global client_ref, _mqtt_connected, _last_mqtt_check global client_ref, _mqtt_connected, _last_mqtt_check
now = utime.ticks_ms() now = utime.ticks_ms()
@@ -971,10 +961,6 @@ def check_mqtt():
return True return True
except Exception as e2: except Exception as e2:
log_err(f"MQTT reconnect attempt {attempt + 1} failed: {e2}") log_err(f"MQTT reconnect attempt {attempt + 1} failed: {e2}")
try:
client_ref.sock.close()
except Exception:
pass
gc.collect() gc.collect()
utime.sleep_ms(2000) utime.sleep_ms(2000)
@@ -982,6 +968,14 @@ def check_mqtt():
return False return False
_mqtt_check_interval_ms = 30000
_last_mqtt_check = 0
_discovery_queue = []
_discovery_idx = 0
_last_discovery_ms = 0
_DISCOVERY_INTERVAL_MS = 350
def _publish_discovery_entity(client, topic, payload, log_msg): def _publish_discovery_entity(client, topic, payload, log_msg):
gc.collect() gc.collect()
client.publish(topic, ujson.dumps(payload), retain=True) client.publish(topic, ujson.dumps(payload), retain=True)
@@ -1292,12 +1286,42 @@ def apply_motion_defaults():
send_vfd_state() send_vfd_state()
def _restore_led_states():
for i in range(num_gauges):
gt = gauge_topics[i]
info(f" red={_red_effect[i]} green={_green_effect[i]} status_r={_status_red_effect[i]} status_g={_status_green_effect[i]}")
for led_key, led_idx, color, effect_arr, state_topic in [
("red", _LED_RED, gauges[i]["ws2812_red"], _red_effect, gt["led_red_state"]),
("green", _LED_GREEN, gauges[i]["ws2812_green"], _green_effect, gt["led_green_state"]),
("status_red", _LED_STATUS_RED, gauges[i]["ws2812_red"], _status_red_effect, gt["status_red_state"]),
("status_green", _LED_STATUS_GREEN, gauges[i]["ws2812_green"], _status_green_effect, gt["status_green_state"]),
]:
if effect_arr[i]:
pub = {"state": "ON", "effect": effect_arr[i]}
_publish(state_topic, ujson.dumps(pub), retain=True)
if _red_effect[i]:
_apply_blink_or_led(i, _LED_RED, gauges[i]["ws2812_red"], _red_effect[i])
if _green_effect[i]:
_apply_blink_or_led(i, _LED_GREEN, gauges[i]["ws2812_green"], _green_effect[i])
if _status_red_effect[i]:
_apply_blink_or_led(i, _LED_STATUS_RED, gauges[i]["ws2812_red"], _status_red_effect[i])
if _status_green_effect[i]:
_apply_blink_or_led(i, _LED_STATUS_GREEN, gauges[i]["ws2812_green"], _status_green_effect[i])
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Main # Main
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def main(): def main():
gc.collect()
_w = network.WLAN(network.STA_IF)
if not _w.active():
_w.active(True)
del _w
gc.collect()
_ensure_arduino()
gc.collect() gc.collect()
info("=" * 48) info("=" * 48)
info("Gauge MQTT controller starting") info("Gauge MQTT controller starting")
@@ -1305,7 +1329,7 @@ def main():
info("=" * 48) info("=" * 48)
gc.collect() gc.collect()
connect_wifi(WIFI_SSID, WIFI_PASSWORD, force_reconnect=True) connect_wifi(WIFI_SSID, WIFI_PASSWORD)
mqtt_attempts = 0 mqtt_attempts = 0
while True: while True:
@@ -1318,13 +1342,14 @@ def main():
if mqtt_attempts % 3 == 0: if mqtt_attempts % 3 == 0:
log_err("WiFi may be stale — forcing reconnect...") log_err("WiFi may be stale — forcing reconnect...")
try: try:
connect_wifi(WIFI_SSID, WIFI_PASSWORD, force_reconnect=True) connect_wifi(WIFI_SSID, WIFI_PASSWORD)
except Exception as we: except Exception as we:
log_err(f"WiFi reconnect failed: {we}") log_err(f"WiFi reconnect failed: {we}")
utime.sleep_ms(5000) utime.sleep_ms(5000)
_subscribe_all(client_ref) _subscribe_all(client_ref)
schedule_discovery() schedule_discovery()
publish_backlight_states(client_ref)
apply_motion_defaults() apply_motion_defaults()
info("Draining initial retained messages...") info("Draining initial retained messages...")
for _ in range(50): for _ in range(50):
@@ -1337,6 +1362,10 @@ def main():
gauge_last_rezero[i] = utime.ticks_ms() gauge_last_rezero[i] = utime.ticks_ms()
info("Home command sent") info("Home command sent")
utime.sleep_ms(100)
_restore_led_states()
info("LED effects restored")
info("Publishing state...") info("Publishing state...")
publish_online(client_ref) publish_online(client_ref)
publish_state(client_ref) publish_state(client_ref)