Compare commits
14 Commits
81099d9887
...
review
| Author | SHA1 | Date | |
|---|---|---|---|
| d1e6d3dfe6 | |||
| 84449e5235 | |||
| ad50fd2ee5 | |||
| ae6d72b292 | |||
| 2ea19b14f8 | |||
| 1f8ba45685 | |||
| 5a98a3c63b | |||
| afe70da24d | |||
| 0d7ae5cedc | |||
| b3e2ef0f81 | |||
| 6068628d13 | |||
| 64b0aa482f | |||
| 2e5e410897 | |||
| 4045da8964 |
64
boot.py
Normal file
64
boot.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""
|
||||
boot.py — runs before main.py on every ESP32 boot
|
||||
|
||||
Connects WiFi, runs OTA update, then hands off to main.py.
|
||||
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.
|
||||
"""
|
||||
import gauge
|
||||
import network
|
||||
import gc
|
||||
import utime
|
||||
import sys
|
||||
|
||||
import ota
|
||||
|
||||
ota.load_config()
|
||||
WIFI_SSID, WIFI_PASSWORD = ota.WIFI_SSID, ota.WIFI_PASSWORD
|
||||
|
||||
def _connect_wifi(timeout_s=20):
|
||||
sta = network.WLAN(network.STA_IF)
|
||||
sta.active(True)
|
||||
sta.config(txpower=15)
|
||||
if sta.isconnected():
|
||||
return True
|
||||
sta.connect(WIFI_SSID, WIFI_PASSWORD)
|
||||
deadline = utime.time() + timeout_s
|
||||
while not sta.isconnected():
|
||||
if utime.time() > deadline:
|
||||
return False
|
||||
utime.sleep_ms(300)
|
||||
return True
|
||||
|
||||
if WIFI_SSID is None:
|
||||
print("[boot] No WiFi credentials — cannot connect, skipping OTA")
|
||||
elif _connect_wifi():
|
||||
ip = network.WLAN(network.STA_IF).ifconfig()[0]
|
||||
print(f"[boot] WiFi connected — {ip}")
|
||||
|
||||
try:
|
||||
ota.update()
|
||||
except Exception as e:
|
||||
print(f"[boot] OTA error: {e} — continuing with existing files")
|
||||
sys.print_exception(e)
|
||||
utime.sleep_ms(5000)
|
||||
ota._fetch_commit_sha = None
|
||||
ota._fetch_manifest = None
|
||||
ota._fetch_dir = None
|
||||
ota._api_get = None
|
||||
ota._download = None
|
||||
ota.urequests = None
|
||||
del ota.urequests
|
||||
del ota
|
||||
gc.collect()
|
||||
del sys.modules["ota"]
|
||||
gc.collect()
|
||||
|
||||
else:
|
||||
print("[boot] WiFi failed — skipping OTA, booting with existing files")
|
||||
|
||||
# main.py runs automatically after boot.py
|
||||
|
||||
|
||||
|
||||
|
||||
97
gauge.py
97
gauge.py
@@ -28,6 +28,16 @@ import gc
|
||||
from umqtt.robust import MQTTClient
|
||||
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
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -524,46 +534,56 @@ _DEVICE = {
|
||||
_wifi_check_interval_ms = 30000
|
||||
_last_wifi_check = 0
|
||||
_wifi_sta = None
|
||||
_WIFI_CONNECT_ATTEMPTS = 3
|
||||
|
||||
|
||||
def _reset_wifi_interface():
|
||||
global _wifi_sta
|
||||
_wifi_sta = network.WLAN(network.STA_IF)
|
||||
if _wifi_sta.active():
|
||||
_wifi_sta.active(False)
|
||||
utime.sleep_ms(200)
|
||||
_wifi_sta.active(True)
|
||||
utime.sleep_ms(500)
|
||||
|
||||
|
||||
def connect_wifi(ssid, password, timeout_s=15, force_reconnect=False):
|
||||
global _wifi_sta
|
||||
_wifi_sta = network.WLAN(network.STA_IF)
|
||||
|
||||
if force_reconnect:
|
||||
try:
|
||||
_wifi_sta.disconnect()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
_wifi_sta.active(False)
|
||||
utime.sleep_ms(250)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
_wifi_sta.active(True)
|
||||
if _wifi_sta.isconnected() and not force_reconnect:
|
||||
ip, mask, gw, dns = _wifi_sta.ifconfig()
|
||||
info("WiFi already connected")
|
||||
info(f" IP:{ip} mask:{mask} gw:{gw} dns:{dns}")
|
||||
utime.sleep_ms(250)
|
||||
return ip
|
||||
info(f"WiFi connecting to '{ssid}' ...")
|
||||
|
||||
last_error = None
|
||||
for attempt in range(_WIFI_CONNECT_ATTEMPTS):
|
||||
info(f"WiFi connecting to '{ssid}' (attempt {attempt + 1}/{_WIFI_CONNECT_ATTEMPTS}) ...")
|
||||
_reset_wifi_interface()
|
||||
try:
|
||||
_wifi_sta.connect(ssid, password)
|
||||
deadline = utime.time() + timeout_s
|
||||
while not _wifi_sta.isconnected():
|
||||
if utime.time() > deadline:
|
||||
log_err(f"WiFi connect timeout after {timeout_s}s")
|
||||
raise OSError("WiFi connect timeout")
|
||||
utime.sleep_ms(200)
|
||||
utime.sleep_ms(250)
|
||||
|
||||
ip, mask, gw, dns = _wifi_sta.ifconfig()
|
||||
mac = ":".join(f"{b:02x}" for b in _wifi_sta.config("mac"))
|
||||
info("WiFi connected!")
|
||||
info(f" SSID : {ssid}")
|
||||
info(f" MAC : {mac}")
|
||||
info(f" IP : {ip} mask:{mask} gw:{gw} dns:{dns}")
|
||||
utime.sleep_ms(250)
|
||||
utime.sleep_ms(500)
|
||||
return ip
|
||||
except Exception as e:
|
||||
last_error = e
|
||||
log_err(f"WiFi connect attempt {attempt + 1} failed: {e}")
|
||||
utime.sleep_ms(1000)
|
||||
|
||||
raise last_error
|
||||
|
||||
|
||||
def check_wifi():
|
||||
@@ -581,15 +601,7 @@ def check_wifi():
|
||||
|
||||
log_err("WiFi lost connection — attempting reconnect...")
|
||||
try:
|
||||
_wifi_sta.active(True)
|
||||
_wifi_sta.connect(WIFI_SSID, WIFI_PASSWORD)
|
||||
deadline = utime.time() + 15
|
||||
while not _wifi_sta.isconnected():
|
||||
if utime.time() > deadline:
|
||||
log_err("WiFi reconnect timeout")
|
||||
return
|
||||
utime.sleep_ms(200)
|
||||
ip, mask, gw, dns = _wifi_sta.ifconfig()
|
||||
ip = connect_wifi(WIFI_SSID, WIFI_PASSWORD, timeout_s=15, force_reconnect=True)
|
||||
info(f"WiFi reconnected! IP:{ip}")
|
||||
except Exception as e:
|
||||
log_err(f"WiFi reconnect failed: {e}")
|
||||
@@ -874,6 +886,7 @@ def connect_mqtt():
|
||||
info(f"Connecting to MQTT broker {MQTT_BROKER}:{MQTT_PORT} ...")
|
||||
last_error = None
|
||||
for attempt in range(3):
|
||||
gc.collect()
|
||||
try:
|
||||
if client_ref is not None:
|
||||
try:
|
||||
@@ -897,7 +910,12 @@ def connect_mqtt():
|
||||
return
|
||||
except Exception as e:
|
||||
last_error = e
|
||||
log_err(f"MQTT connect attempt {attempt + 1} failed: {e}")
|
||||
log_err(f"MQTT connect attempt {attempt + 1} failed: {type(e).__name__}: {e}")
|
||||
try:
|
||||
client.sock.close()
|
||||
except Exception:
|
||||
pass
|
||||
gc.collect()
|
||||
utime.sleep_ms(1000)
|
||||
|
||||
_mqtt_connected = False
|
||||
@@ -909,7 +927,7 @@ _last_mqtt_check = 0
|
||||
_discovery_queue = []
|
||||
_discovery_idx = 0
|
||||
_last_discovery_ms = 0
|
||||
_DISCOVERY_INTERVAL_MS = 200
|
||||
_DISCOVERY_INTERVAL_MS = 350
|
||||
|
||||
|
||||
def check_mqtt():
|
||||
@@ -953,6 +971,11 @@ def check_mqtt():
|
||||
return True
|
||||
except Exception as e2:
|
||||
log_err(f"MQTT reconnect attempt {attempt + 1} failed: {e2}")
|
||||
try:
|
||||
client_ref.sock.close()
|
||||
except Exception:
|
||||
pass
|
||||
gc.collect()
|
||||
utime.sleep_ms(2000)
|
||||
|
||||
log_err("MQTT reconnection failed after 3 attempts")
|
||||
@@ -960,6 +983,7 @@ def check_mqtt():
|
||||
|
||||
|
||||
def _publish_discovery_entity(client, topic, payload, log_msg):
|
||||
gc.collect()
|
||||
client.publish(topic, ujson.dumps(payload), retain=True)
|
||||
info(log_msg)
|
||||
|
||||
@@ -1233,6 +1257,7 @@ def service_discovery():
|
||||
if _last_discovery_ms and utime.ticks_diff(now, _last_discovery_ms) < _DISCOVERY_INTERVAL_MS:
|
||||
return
|
||||
|
||||
gc.collect()
|
||||
topic, payload, log_msg = _discovery_queue[_discovery_idx]
|
||||
try:
|
||||
if isinstance(payload, bytes):
|
||||
@@ -1243,7 +1268,6 @@ def service_discovery():
|
||||
log_err(f"Discovery publish failed for {topic}: {e}")
|
||||
_discovery_idx += 1
|
||||
_last_discovery_ms = utime.ticks_ms()
|
||||
if (_discovery_idx & 3) == 0:
|
||||
gc.collect()
|
||||
|
||||
|
||||
@@ -1274,13 +1298,30 @@ def apply_motion_defaults():
|
||||
|
||||
|
||||
def main():
|
||||
gc.collect()
|
||||
info("=" * 48)
|
||||
info("Gauge MQTT controller starting")
|
||||
info(f"Heap free: {gc.mem_free()} bytes")
|
||||
info("=" * 48)
|
||||
|
||||
gc.collect()
|
||||
connect_wifi(WIFI_SSID, WIFI_PASSWORD, force_reconnect=True)
|
||||
|
||||
mqtt_attempts = 0
|
||||
while True:
|
||||
try:
|
||||
connect_mqtt()
|
||||
break
|
||||
except Exception as e:
|
||||
mqtt_attempts += 1
|
||||
log_err(f"MQTT connect failed: {e} (attempt {mqtt_attempts})")
|
||||
if mqtt_attempts % 3 == 0:
|
||||
log_err("WiFi may be stale — forcing reconnect...")
|
||||
try:
|
||||
connect_wifi(WIFI_SSID, WIFI_PASSWORD, force_reconnect=True)
|
||||
except Exception as we:
|
||||
log_err(f"WiFi reconnect failed: {we}")
|
||||
utime.sleep_ms(5000)
|
||||
_subscribe_all(client_ref)
|
||||
schedule_discovery()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user