MQTT esp_wifi
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
# This file is executed on every boot (including wake-boot from deepsleep)
|
||||||
|
#import esp
|
||||||
|
#esp.osdebug(None)
|
||||||
|
#import webrepl
|
||||||
|
#webrepl.start()
|
||||||
|
|
||||||
|
def do_connect(ssid, pwd):
|
||||||
|
import network
|
||||||
|
sta_if = network.WLAN(network.STA_IF)
|
||||||
|
if not sta_if.isconnected():
|
||||||
|
print('connecting to network...')
|
||||||
|
sta_if.active(True)
|
||||||
|
sta_if.connect(ssid, pwd)
|
||||||
|
while not sta_if.isconnected():
|
||||||
|
pass
|
||||||
|
print('network config:', sta_if.ifconfig())
|
||||||
|
|
||||||
|
# Attempt to connect to WiFi network
|
||||||
|
do_connect("Smartwave-1", 'Smartwave-prot-1')
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import _thread
|
||||||
|
import select # <--- Built-in module to handle TLS timeouts
|
||||||
|
from machine import Pin
|
||||||
|
from shared import get_mqtt_client
|
||||||
|
from shared import config
|
||||||
|
import time
|
||||||
|
|
||||||
|
# --- Hardware Configuration ---
|
||||||
|
vext = Pin(19, Pin.OUT)
|
||||||
|
vext.value(0)
|
||||||
|
time.sleep_ms(100)
|
||||||
|
|
||||||
|
# --- Read Unique Device ID ---
|
||||||
|
try:
|
||||||
|
with open("device_id.txt", "r") as f:
|
||||||
|
DEVICE_ID = f.read().strip()
|
||||||
|
except Exception:
|
||||||
|
DEVICE_ID = "ESP32_Inconnu"
|
||||||
|
|
||||||
|
MQTT_CA_FILE = "/certs/ca.crt"
|
||||||
|
|
||||||
|
# --- Setup MQTT Client ---
|
||||||
|
mqtt_client = get_mqtt_client(
|
||||||
|
host=config.MQTT_BROKER_HOST,
|
||||||
|
client_id="smartwave-esp32-" + DEVICE_ID,
|
||||||
|
use_tls=config.USE_TLS,
|
||||||
|
cafile=MQTT_CA_FILE,
|
||||||
|
keepalive=config.MQTT_KEEPALIVE, # Can safely be 30 now
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_mqtt_message(message):
|
||||||
|
print("[MQTT Thread] Received message:", message)
|
||||||
|
|
||||||
|
mqtt_client.set_callback(on_mqtt_message)
|
||||||
|
|
||||||
|
|
||||||
|
def mqtt_background_thread():
|
||||||
|
"""Background MQTT worker using select.poll() for keepalive tracking."""
|
||||||
|
print("[Thread] Background MQTT worker started.")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
print("[Thread] Attempting connection to MQTT broker...")
|
||||||
|
mqtt_client.connect()
|
||||||
|
print("[Thread] Connected! Subscribing to topic...")
|
||||||
|
mqtt_client.subscribe(config.MQTT_TOPIC, qos=config.MQTT_QOS)
|
||||||
|
print("[Thread] Successfully subscribed. Setting up poller...")
|
||||||
|
|
||||||
|
# --- THE SELECT POLLER SETUP ---
|
||||||
|
# Create a poller and register our active TLS socket to look for incoming data (POLLIN)
|
||||||
|
poller = select.poll()
|
||||||
|
poller.register(mqtt_client._client.sock, select.POLLIN)
|
||||||
|
|
||||||
|
# Listening loop
|
||||||
|
while True:
|
||||||
|
# Wait for network events for a maximum of 15000 milliseconds (15 seconds)
|
||||||
|
events = poller.poll(15000)
|
||||||
|
|
||||||
|
if not events:
|
||||||
|
# The 15 seconds expired with zero network traffic!
|
||||||
|
# Send a keepalive ping to Mosquitto.
|
||||||
|
print("[Thread] No data for 15s. Sending keepalive ping...")
|
||||||
|
mqtt_client._client.ping()
|
||||||
|
else:
|
||||||
|
# Data has physically arrived on the socket!
|
||||||
|
# Calling wait() now is completely safe and won't block indefinitely.
|
||||||
|
mqtt_client.wait()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("[Thread] Connection dropped or error encountered:", e)
|
||||||
|
print("[Thread] Cleaning up socket context. Retrying in 5 seconds...")
|
||||||
|
try:
|
||||||
|
mqtt_client.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
# --- Launch background worker ---
|
||||||
|
_thread.start_new_thread(mqtt_background_thread, ())
|
||||||
|
|
||||||
|
|
||||||
|
# --- MAIN APPLICATION THREAD (Core 0) ---
|
||||||
|
print("[Main] Main execution path active.")
|
||||||
|
while True:
|
||||||
|
# Your main physical loop runs completely unhindered here
|
||||||
|
time.sleep(1)
|
||||||
@@ -3,6 +3,7 @@ services:
|
|||||||
image: eclipse-mosquitto:2.0
|
image: eclipse-mosquitto:2.0
|
||||||
environment:
|
environment:
|
||||||
MQTT_TLS_ENABLED: ${MQTT_TLS_ENABLED:-true}
|
MQTT_TLS_ENABLED: ${MQTT_TLS_ENABLED:-true}
|
||||||
|
# restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "192.168.50.1:8884:8884"
|
- "192.168.50.1:8884:8884"
|
||||||
volumes:
|
volumes:
|
||||||
@@ -13,6 +14,7 @@ services:
|
|||||||
- mqtt-data:/mosquitto/data
|
- mqtt-data:/mosquitto/data
|
||||||
- mqtt-log:/mosquitto/log
|
- mqtt-log:/mosquitto/log
|
||||||
command: ["/bin/sh", "/scripts/start-broker.sh"]
|
command: ["/bin/sh", "/scripts/start-broker.sh"]
|
||||||
|
# command: ["tail", "-f", "/dev/null"] # Do nothing
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
mqtt-data:
|
mqtt-data:
|
||||||
|
|||||||
+69
-24
@@ -1,69 +1,114 @@
|
|||||||
import threading
|
import threading
|
||||||
import queue
|
import queue
|
||||||
import time
|
import time
|
||||||
from shared import get_lora, deviceTypes
|
from shared import get_lora, get_mqtt_client, deviceTypes, config
|
||||||
|
|
||||||
# --- Lecture de l'ID unique du Raspberry Pi ---
|
# --- Read Unique Device ID ---
|
||||||
try:
|
try:
|
||||||
with open("device_id.txt", "r") as f:
|
with open("device_id.txt", "r") as f:
|
||||||
DEVICE_ID = f.read().strip()
|
DEVICE_ID = f.read().strip()
|
||||||
except Exception:
|
except Exception:
|
||||||
# Alternative si le script est lancé depuis un autre dossier
|
|
||||||
try:
|
try:
|
||||||
with open("/home/pi/SmartWave/orchestrateur/device_id.txt", "r") as f:
|
with open("/home/pi/SmartWave/orchestrateur/device_id.txt", "r") as f:
|
||||||
DEVICE_ID = f.read().strip()
|
DEVICE_ID = f.read().strip()
|
||||||
except Exception:
|
except Exception:
|
||||||
DEVICE_ID = "RPI_Orchestrateur_Default"
|
DEVICE_ID = "RPI_Orchestrateur_Default"
|
||||||
|
|
||||||
# Création de la file d'attente pour les messages (thread-safe)
|
# Thread-safe queue for application messages
|
||||||
data_queue = queue.Queue()
|
data_queue = queue.Queue()
|
||||||
|
|
||||||
lora = get_lora()
|
lora = get_lora()
|
||||||
lora.configure()
|
lora.configure()
|
||||||
|
|
||||||
def lora_listener():
|
def lora_listener():
|
||||||
"""Thread de fond : écoute en permanence et répond aux Heartbeats."""
|
"""Background Thread: Listens to LoRa traffic and responds to Heartbeats."""
|
||||||
print("Thread Écouteur démarré.")
|
print("Thread Écouteur LoRa démarré.")
|
||||||
while True:
|
while True:
|
||||||
# On attend un paquet (timeout court pour rester réactif)
|
|
||||||
paquet = lora.receive_packet(timeout_ms=1000)
|
paquet = lora.receive_packet(timeout_ms=1000)
|
||||||
|
|
||||||
if paquet:
|
if paquet:
|
||||||
donnees = paquet["data"]
|
donnees = paquet["data"]
|
||||||
expediteur_type = donnees.get("type")
|
expediteur_type = donnees.get("type")
|
||||||
|
|
||||||
# --- Cas 1 : Gestion automatique du Heartbeat ---
|
|
||||||
if expediteur_type == deviceTypes.DEVICE_TYPES["MICROWAVE"]:
|
if expediteur_type == deviceTypes.DEVICE_TYPES["MICROWAVE"]:
|
||||||
print(f"\n[Thread Fond] Heartbeat reçu de {donnees.get('id')}")
|
print(f"\n[Thread LoRa] Heartbeat reçu de {donnees.get('id')}")
|
||||||
|
|
||||||
reponse = {
|
reponse = {
|
||||||
"id": DEVICE_ID, # Ou lecture de ton fichier device_id.txt
|
"id": DEVICE_ID,
|
||||||
"type": deviceTypes.DEVICE_TYPES["ORCHESTRATOR"]
|
"type": deviceTypes.DEVICE_TYPES["ORCHESTRATOR"]
|
||||||
}
|
}
|
||||||
lora.send(reponse)
|
lora.send(reponse)
|
||||||
|
|
||||||
# --- Cas 2 : Donnée applicative, on l'envoie vers le thread principal ---
|
|
||||||
else:
|
else:
|
||||||
data_queue.put(paquet)
|
data_queue.put({"source": "LoRa", "data": paquet})
|
||||||
|
|
||||||
# 2. Lancement du thread d'écoute
|
# --- Setup & Connect MQTT ---
|
||||||
listener_thread = threading.Thread(target=lora_listener, daemon=True)
|
mqtt_client = get_mqtt_client(
|
||||||
listener_thread.start()
|
host="192.168.50.1", # Using explicit gateway IP to dodge Docker loopback blocks
|
||||||
|
client_id="smartwave-orchestrateur-"+DEVICE_ID,
|
||||||
|
use_tls=config.USE_TLS,
|
||||||
|
cafile="/home/pi/SmartWave/orchestrateur/mqtt/certs/ca.crt",
|
||||||
|
keepalive=config.MQTT_KEEPALIVE,
|
||||||
|
)
|
||||||
|
mqtt_client.connect()
|
||||||
|
|
||||||
|
# --- THE CRUCIAL PAHO FIX ---
|
||||||
|
# Start Paho's internal background thread. This handles all network packets,
|
||||||
|
# automatic keepalive pings, and delivery receipts cleanly.
|
||||||
|
if hasattr(mqtt_client._client, "loop_start"):
|
||||||
|
mqtt_client._client.loop_start()
|
||||||
|
print("Paho MQTT asynchronous network loop started.")
|
||||||
|
|
||||||
|
|
||||||
|
def mqtt_listener():
|
||||||
|
"""Background Thread: Constantly inspects incoming MQTT message cache."""
|
||||||
|
print("Thread MQTT démarré.")
|
||||||
|
while True:
|
||||||
|
message = mqtt_client.get_message()
|
||||||
|
|
||||||
|
if message:
|
||||||
|
print(f"\n[Thread MQTT] Message reçu : {message}")
|
||||||
|
data_queue.put({"source": "MQTT", "data": message})
|
||||||
|
|
||||||
|
# --- THE CPU FIX ---
|
||||||
|
# Sleep for 100ms. Prevents the thread from turning into an infinite 100% CPU hog.
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
|
# Launch background monitoring workers
|
||||||
|
threading.Thread(target=lora_listener, daemon=True).start()
|
||||||
|
threading.Thread(target=mqtt_listener, daemon=True).start()
|
||||||
|
|
||||||
# 3. Boucle principale (Main Thread) : tu es libre de faire autre chose !
|
|
||||||
print("Orchestrateur prêt. Le main loop est libre.")
|
print("Orchestrateur prêt. Le main loop est libre.")
|
||||||
|
|
||||||
|
# --- MAIN EXECUTION LOOP ---
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
# On regarde si on a reçu des données applicatives (non-heartbeat)
|
# Check for non-heartbeat data safely
|
||||||
# On utilise block=False pour ne pas bloquer si la queue est vide
|
|
||||||
try:
|
try:
|
||||||
msg = data_queue.get(block=False)
|
msg = data_queue.get(block=False)
|
||||||
print(f"\n[Main Loop] Données traitées : {msg['data']}")
|
print(f"\n[Main Loop] Données traitées : {msg['data']}")
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
pass # Rien à traiter, on fait autre chose...
|
pass
|
||||||
|
|
||||||
# Ici tu peux faire tes autres tâches
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Publish debug telemetry message
|
||||||
|
print("[Main Loop] Envoi d'un message de debug sur MQTT...")
|
||||||
|
response = mqtt_client.publish(
|
||||||
|
config.MQTT_TOPIC,
|
||||||
|
f"Orchestrateur actif, ID: {DEVICE_ID}",
|
||||||
|
qos=config.MQTT_QOS
|
||||||
|
)
|
||||||
|
|
||||||
|
# This will now unblock instantly because loop_start() handles the delivery confirmation!
|
||||||
|
response.wait_for_publish()
|
||||||
|
print("[Main Loop] Message de debug publié avec succès.")
|
||||||
|
|
||||||
|
time.sleep(9)
|
||||||
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Clean termination
|
||||||
|
if hasattr(mqtt_client._client, "loop_stop"):
|
||||||
|
mqtt_client._client.loop_stop()
|
||||||
|
mqtt_client.close()
|
||||||
@@ -8,5 +8,5 @@ log_type notice
|
|||||||
log_type information
|
log_type information
|
||||||
allow_anonymous true
|
allow_anonymous true
|
||||||
|
|
||||||
listener 8884 192.168.50.1
|
listener 8884
|
||||||
protocol mqtt
|
protocol mqtt
|
||||||
@@ -8,7 +8,7 @@ log_type notice
|
|||||||
log_type information
|
log_type information
|
||||||
allow_anonymous true
|
allow_anonymous true
|
||||||
|
|
||||||
listener 8884 192.168.50.1
|
listener 8884
|
||||||
protocol mqtt
|
protocol mqtt
|
||||||
cafile /mosquitto/certs/ca.crt
|
cafile /mosquitto/certs/ca.crt
|
||||||
certfile /mosquitto/certs/server.crt
|
certfile /mosquitto/certs/server.crt
|
||||||
|
|||||||
@@ -1,2 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# LoRa
|
||||||
HEARTBEAT_INTERVAL = 10
|
HEARTBEAT_INTERVAL = 10
|
||||||
|
|
||||||
|
# MQTT
|
||||||
|
MQTT_BROKER_HOST = "192.168.50.1"
|
||||||
|
MQTT_TOPIC = b"smartwave/demo"
|
||||||
|
MQTT_KEEPALIVE = 30
|
||||||
|
USE_TLS = True
|
||||||
|
MQTT_QOS = 2
|
||||||
+19
-3
@@ -104,9 +104,17 @@ class BrokerClient:
|
|||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
if IS_MICROPYTHON:
|
if IS_MICROPYTHON:
|
||||||
|
import ssl
|
||||||
ssl_params = self.ssl_params
|
ssl_params = self.ssl_params
|
||||||
if self.use_tls and ssl_params is None and self.cafile is not None:
|
|
||||||
ssl_params = {"cadata": _read_file_bytes(self.cafile)}
|
if self.use_tls and ssl_params is None:
|
||||||
|
# MicroPython uses context-less structures.
|
||||||
|
# If your CA is self-signed, validation can fail without a valid hostname match.
|
||||||
|
ssl_params = {
|
||||||
|
"cert_reqs": ssl.CERT_NONE, # Temporarily change to NONE to test if validation is the culprit
|
||||||
|
"cadata": _read_file_bytes(self.cafile)
|
||||||
|
}
|
||||||
|
|
||||||
client = _MQTTClient(
|
client = _MQTTClient(
|
||||||
self.client_id or "smartWave-client",
|
self.client_id or "smartWave-client",
|
||||||
self.host,
|
self.host,
|
||||||
@@ -114,7 +122,7 @@ class BrokerClient:
|
|||||||
user=self.username,
|
user=self.username,
|
||||||
password=self.password,
|
password=self.password,
|
||||||
keepalive=self.keepalive,
|
keepalive=self.keepalive,
|
||||||
ssl=self.use_tls or ssl_params is not None,
|
ssl=self.use_tls,
|
||||||
ssl_params=ssl_params,
|
ssl_params=ssl_params,
|
||||||
)
|
)
|
||||||
self._client = client
|
self._client = client
|
||||||
@@ -155,6 +163,10 @@ class BrokerClient:
|
|||||||
payload_bytes = _ensure_bytes(payload)
|
payload_bytes = _ensure_bytes(payload)
|
||||||
if IS_MICROPYTHON:
|
if IS_MICROPYTHON:
|
||||||
return client.publish(topic, payload_bytes, retain=retain, qos=qos)
|
return client.publish(topic, payload_bytes, retain=retain, qos=qos)
|
||||||
|
|
||||||
|
if isinstance(topic, bytes):
|
||||||
|
topic = topic.decode('utf-8')
|
||||||
|
|
||||||
return client.publish(topic, payload_bytes, qos=qos, retain=retain)
|
return client.publish(topic, payload_bytes, qos=qos, retain=retain)
|
||||||
|
|
||||||
def subscribe(self, topic, qos=2):
|
def subscribe(self, topic, qos=2):
|
||||||
@@ -162,6 +174,10 @@ class BrokerClient:
|
|||||||
if IS_MICROPYTHON:
|
if IS_MICROPYTHON:
|
||||||
client.set_callback(self._on_micropython_message)
|
client.set_callback(self._on_micropython_message)
|
||||||
return client.subscribe(topic, qos=qos)
|
return client.subscribe(topic, qos=qos)
|
||||||
|
|
||||||
|
if isinstance(topic, bytes):
|
||||||
|
topic = topic.decode('utf-8')
|
||||||
|
|
||||||
return client.subscribe(topic, qos=qos)
|
return client.subscribe(topic, qos=qos)
|
||||||
|
|
||||||
def _on_micropython_message(self, topic, payload):
|
def _on_micropython_message(self, topic, payload):
|
||||||
|
|||||||
Reference in New Issue
Block a user