13 Commits

View File

@@ -28,6 +28,16 @@ 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
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -524,46 +534,56 @@ _DEVICE = {
_wifi_check_interval_ms = 30000 _wifi_check_interval_ms = 30000
_last_wifi_check = 0 _last_wifi_check = 0
_wifi_sta = None _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): def connect_wifi(ssid, password, timeout_s=15, force_reconnect=False):
global _wifi_sta global _wifi_sta
_wifi_sta = network.WLAN(network.STA_IF) _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: if _wifi_sta.isconnected() and not force_reconnect:
ip, mask, gw, dns = _wifi_sta.ifconfig() ip, mask, gw, dns = _wifi_sta.ifconfig()
info("WiFi already connected") info("WiFi already connected")
info(f" IP:{ip} mask:{mask} gw:{gw} dns:{dns}") info(f" IP:{ip} mask:{mask} gw:{gw} dns:{dns}")
utime.sleep_ms(250) utime.sleep_ms(250)
return ip 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) _wifi_sta.connect(ssid, password)
deadline = utime.time() + timeout_s deadline = utime.time() + timeout_s
while not _wifi_sta.isconnected(): while not _wifi_sta.isconnected():
if utime.time() > deadline: if utime.time() > deadline:
log_err(f"WiFi connect timeout after {timeout_s}s")
raise OSError("WiFi connect timeout") raise OSError("WiFi connect timeout")
utime.sleep_ms(200) utime.sleep_ms(250)
ip, mask, gw, dns = _wifi_sta.ifconfig() ip, mask, gw, dns = _wifi_sta.ifconfig()
mac = ":".join(f"{b:02x}" for b in _wifi_sta.config("mac")) mac = ":".join(f"{b:02x}" for b in _wifi_sta.config("mac"))
info("WiFi connected!") info("WiFi connected!")
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(250) utime.sleep_ms(500)
return ip 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(): def check_wifi():
@@ -581,15 +601,7 @@ def check_wifi():
log_err("WiFi lost connection — attempting reconnect...") log_err("WiFi lost connection — attempting reconnect...")
try: try:
_wifi_sta.active(True) ip = connect_wifi(WIFI_SSID, WIFI_PASSWORD, timeout_s=15, force_reconnect=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()
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}")
@@ -874,6 +886,7 @@ def connect_mqtt():
info(f"Connecting to MQTT broker {MQTT_BROKER}:{MQTT_PORT} ...") info(f"Connecting to MQTT broker {MQTT_BROKER}:{MQTT_PORT} ...")
last_error = None last_error = None
for attempt in range(3): for attempt in range(3):
gc.collect()
try: try:
if client_ref is not None: if client_ref is not None:
try: try:
@@ -897,7 +910,12 @@ def connect_mqtt():
return return
except Exception as e: except Exception as e:
last_error = 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) utime.sleep_ms(1000)
_mqtt_connected = False _mqtt_connected = False
@@ -909,7 +927,7 @@ _last_mqtt_check = 0
_discovery_queue = [] _discovery_queue = []
_discovery_idx = 0 _discovery_idx = 0
_last_discovery_ms = 0 _last_discovery_ms = 0
_DISCOVERY_INTERVAL_MS = 200 _DISCOVERY_INTERVAL_MS = 350
def check_mqtt(): def check_mqtt():
@@ -953,6 +971,11 @@ 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()
utime.sleep_ms(2000) utime.sleep_ms(2000)
log_err("MQTT reconnection failed after 3 attempts") log_err("MQTT reconnection failed after 3 attempts")
@@ -960,6 +983,7 @@ def check_mqtt():
def _publish_discovery_entity(client, topic, payload, log_msg): def _publish_discovery_entity(client, topic, payload, log_msg):
gc.collect()
client.publish(topic, ujson.dumps(payload), retain=True) client.publish(topic, ujson.dumps(payload), retain=True)
info(log_msg) 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: if _last_discovery_ms and utime.ticks_diff(now, _last_discovery_ms) < _DISCOVERY_INTERVAL_MS:
return return
gc.collect()
topic, payload, log_msg = _discovery_queue[_discovery_idx] topic, payload, log_msg = _discovery_queue[_discovery_idx]
try: try:
if isinstance(payload, bytes): if isinstance(payload, bytes):
@@ -1243,7 +1268,6 @@ def service_discovery():
log_err(f"Discovery publish failed for {topic}: {e}") log_err(f"Discovery publish failed for {topic}: {e}")
_discovery_idx += 1 _discovery_idx += 1
_last_discovery_ms = utime.ticks_ms() _last_discovery_ms = utime.ticks_ms()
if (_discovery_idx & 3) == 0:
gc.collect() gc.collect()
@@ -1274,13 +1298,30 @@ def apply_motion_defaults():
def main(): def main():
gc.collect()
info("=" * 48) info("=" * 48)
info("Gauge MQTT controller starting") info("Gauge MQTT controller starting")
info(f"Heap free: {gc.mem_free()} bytes")
info("=" * 48) info("=" * 48)
gc.collect()
connect_wifi(WIFI_SSID, WIFI_PASSWORD, force_reconnect=True) connect_wifi(WIFI_SSID, WIFI_PASSWORD, force_reconnect=True)
mqtt_attempts = 0
while True:
try:
connect_mqtt() 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) _subscribe_all(client_ref)
schedule_discovery() schedule_discovery()