Exception MQTT non traitée?
Posted: Fri 1 Jan 2021 20:36
Bonsoir,
Toujours dans la lecture de "Python, Raspberry pi et Flask", après quelques tests, j'essaie de passer aux choses sérieuses et je bloque sur un problème de communication MQTT, à mon avis.
L'objet: un ESP8266 muni d'un DHT22 et d'un contacteur de porte.
L'idée est de le placer dans un abri chauffé, dans le jardin. Le DTH22 mesure la température etc. toutes les 10 minutes et publie les valeurs sur le serveur MQTT.
La température mesurée servira à un autre module pour allumer/éteindre un chauffage électrique.
Lorsque le chauffage fonctionne, la mesure de température se fait toutes les minutes.
En parallèle, un contacteur de porte permet d'annoncer sur MQTT l'ouverture intempestive de la porte.
Le boot.py est verbatim celui du livre.
Mon main.py, écrit au départ de plusieurs projets du livre est le suivant:
(j'ai décommenté certaines impressions pour essayer de comprendre ce qui bloquait)
Tout démarre bien et un souscripteur MQTT tournant sur un autre PC lit bien les messages de connection de l'objet au système de messagerie puis les informations de température, humidité etc., ainsi que les ouvertures ou fermetures de portes.
De même, si un message est publié, correspondant à l'allumage du chauffage, la fréquence de mesure passe bien de 10 à 1 minute.
Par contre, au bout d'un temps variable, le système se bloque et plus rien n'est publié.
La led passe à un niveau de clignotement "7", ce qui correspond à une activation par la fonction "check_mqtt_sub()"
A ce moment, dans le terminal, j'ai les messages suivants:
Cette version de "check_mqtt_sub()" est la deuxième.
La première n'avait pas de section try/exception et à ce moment, les messages étaient les suivants:
C'est la mention du problème de gestion des exception qui m'avait incité à modifier le code.
Voyez-vous ce qui cloche?
Merci d'avance pour toute piste.
Toujours dans la lecture de "Python, Raspberry pi et Flask", après quelques tests, j'essaie de passer aux choses sérieuses et je bloque sur un problème de communication MQTT, à mon avis.
L'objet: un ESP8266 muni d'un DHT22 et d'un contacteur de porte.
L'idée est de le placer dans un abri chauffé, dans le jardin. Le DTH22 mesure la température etc. toutes les 10 minutes et publie les valeurs sur le serveur MQTT.
La température mesurée servira à un autre module pour allumer/éteindre un chauffage électrique.
Lorsque le chauffage fonctionne, la mesure de température se fait toutes les minutes.
En parallèle, un contacteur de porte permet d'annoncer sur MQTT l'ouverture intempestive de la porte.
Le boot.py est verbatim celui du livre.
Mon main.py, écrit au départ de plusieurs projets du livre est le suivant:
(j'ai décommenté certaines impressions pour essayer de comprendre ce qui bloquait)
Code: Select all
# coding: utf8
""" La Sorcière Pythonic - Object Abri-Sud v0.1
Relève et envoie la température, l'humidité, le point de rosée et un indice de satisfaction
pour l'Abri Sud, via le serveur MQTT
Le capteur effectue une mesure toutes les 10 minutes, sauf si le chauffage de l'Abri Sud est
fonctionnement. A ce moment, il effectue une mesure à chaque minute.
L'objet gère également un contacteur vérifiant l'état de la porte de l'abri des chats.
"""
from machine import Pin, reset
import time
from ubinascii import hexlify
from network import WLAN
CLIENT_ID = 'AbriSud'
MQTT_SERVER = "192.168.1.xx"
MQTT_USER = 'Pxxxxxxxs'
MQTT_PSWD = 'xxxxxxxx'
# redemarrage auto après erreur
ERROR_REBOOT_TIME = 3600 # 1 h = 3600 sec
# Broches utilisées et variables
Etat_Chauf = "OFF"
# Senseur Temp. DHT22
DHT_PIN = 13
CONTACT_PIN = 14
last_contact_state = 0 # 0 = Fermé - 1 = Ouvert
# --- Demarrage conditionnel ---
runapp = Pin( 12, Pin.IN, Pin.PULL_UP )
led = Pin( 2, Pin.OUT )
led.value( 1 ) # eteindre
def led_error( step ):
global led
t = time.time()
while ( time.time()-t ) < ERROR_REBOOT_TIME:
for i in range( 20 ):
led.value(not(led.value()))
time.sleep(0.100)
led.value( 1 ) # eteindre
time.sleep( 1 )
# clignote nbr fois
for i in range( step ):
led.value( 0 )
time.sleep( 0.5 )
led.value( 1 )
time.sleep( 0.5 )
time.sleep( 1 )
# Re-start the ESP
reset()
if runapp.value() != 1:
from sys import exit
exit(0)
led.value( 0 ) # allumer
# --- Programme Pincipal ---
def sub_cb( topic, msg ):
""" fonction de rappel pour souscriptions MQTT """
# debogage
print( '-'*20 )
print ('réception message')
print( topic )
print( msg )
global Etat_Chauf
# bytes -> str
t = topic.decode( 'utf8' )
m = msg.decode('utf8')
try:
if t == "Maison/Abri/Sud/Etat_chauf":
Etat_Chauf = m
print("Etat du chauffage:")
print(Etat_Chauf)
except Exception as e:
# Capturer TOUTE exception sur souscription
# Ne pas crasher check_mqtt_sub et
# asyncio.run_until_complete et l'ESP!
# Info debug sur REPL
print( "="*20 )
print( "Subscriber callback (sub_cb) catch an exception:" )
print( e )
print( "for topic and message" )
print( t )
print( m )
print( "="*20 )
from umqtt.simple import MQTTClient
try:
q = MQTTClient( client_id = CLIENT_ID,
server = MQTT_SERVER,
user = MQTT_USER, password = MQTT_PSWD )
q.set_callback( sub_cb )
if q.connect() != 0:
led_error( step=1 )
q.subscribe( 'Maison/Abri/Sud/Etat_chauf' )
except Exception as e:
print( e )
# check MQTT_SERVER, MQTT_USER, MQTT_PSWD
led_error( step=2 )
# chargement des bibliotheques
try:
from dht import DHT22
from machine import Pin
from math import log
except Exception as e:
print( e )
led_error( step=3 )
# declare le bus i2c (if any)
#
# i2c = I2C( sda=Pin(4), scl=Pin(5) )
# créer les senseurs
try:
# Senseur temp&Co DHT22
DHT = DHT22(Pin(DHT_PIN))
# Contact de porte et lecture
contact = Pin( CONTACT_PIN, Pin.IN, Pin.PULL_UP )
last_contact_state = contact.value()
except Exception as e:
print( e )
led_error( step=4 )
try:
# annonce connexion objet
sMac = hexlify( WLAN().config( 'mac' ) ).decode()
q.publish( "connect/%s" % CLIENT_ID , sMac )
# Annonce l'état
except Exception as e:
print( e )
led_error( step=5 )
import uasyncio as asyncio
def capture_10min():
""" Executé pour capturer la temperature toutes les 10 minutes """
global DHT
DHT.measure()
temp = DHT.temperature()
hum = DHT.humidity()
#print('Temperature: %3.1f °C' %temp)
#print('Humidité: %3.1f%%' %hum)
if hum < 30:
feel = 2
# feel = "Sec"
elif hum < 45:
feel= 0
# feel = "Normal"
elif hum < 70:
feel = 1
# feel = "Agréable"
else:
feel = 3
#feel = "Humide"
alpha = 17.27 * temp/(237.7+temp)+log(hum/100)
dp = 237.7 * alpha / (17.27 - alpha)
dew = "{0:.2f}".format(dp)
t = "{0:.2f}".format(temp)
h = "{0:.2f}".format(hum)
#print('Feel ' + str(feel))
#print('point de rosée : ' + dew)
#message = '{ "idx" : '+str(sensor_idx)+', "nvalue" : 0, "svalue" : "'+ t +';'+ h +';'+str(feel)+'"}'
#q.publish( "domoticz/in", message )
q.publish("Maison/Abri/Sud/Temp", t)
time.sleep(0.5)
q.publish("Maison/Abri/Sud/Hum", h)
time.sleep(0.5)
q.publish("Maison/Abri/Sud/Feel", str(feel))
time.sleep(0.5)
q.publish ("Maison/Abri/Sud/DewP", dew)
def capture_1min():
""" Capture de la temperature toutes les minutes (mais
dans lorsque le chauffage est ON) """
global Etat_Chauf
if Etat_Chauf == "ON":
# execution par la routine de capture
capture_10min()
def heartbeat():
""" Led eteinte 200ms toutes les 10 sec """
# PS: LED déjà éteinte par run_every!
time.sleep( 0.2 )
def check_mqtt_sub():
global q
# Non-Blocking wait_msg(). Will call mqtt callback
# (sub_cb) when a message is received for subscription
try:
q.check_msg()
except Exception as e:
print(e)
led_error(step=7)
def check_contact():
""" Publie un message chaque fois que le contact change d'état """
global q
global last_contact_state
# si rien n'a changé
if contact.value()==last_contact_state:
return
# état différent -> deparasitage logiciel
time.sleep( 0.100 )
# relire l'état et s'assurer qu'il n'a pas changé
valeur = contact.value()
if valeur != last_contact_state:
q.publish( "Maison/Abri/Sud/Porte",
"OUVERTE" if valeur==1 else "FERMEE" )
last_contact_state = valeur
async def run_every( fn, min= 1, sec=None):
""" Execute a function fn every min minutes
or sec secondes"""
global led
wait_sec = sec if sec else min*60
while True:
led.value( 1 ) # eteindre pendant envoi/traitement
try:
fn()
except Exception:
print( "run_every catch exception for %s" % fn)
raise # quitter loop
led.value( 0 ) # allumer
await asyncio.sleep( wait_sec )
async def run_app_exit():
""" fin d'execution lorsque quitte la fonction """
global runapp
while runapp.value()==1:
await asyncio.sleep( 10 )
return
loop = asyncio.get_event_loop()
loop.create_task( run_every(capture_10min , min=10) )
loop.create_task( run_every(capture_1min , min=1) )
loop.create_task( run_every(heartbeat , sec=10 ) )
loop.create_task( run_every(check_contact, sec=2 ) )
loop.create_task( run_every(check_mqtt_sub, sec=2.5) )
try:
# Annonce l'etat initial
q.publish( "Maison/Abri/Sud/Porte", "OUVERTE" if last_contact_state==1 else "FERMEE" )
# Execution du scheduler
loop.run_until_complete( run_app_exit() )
except Exception as e :
print( e )
led_error( step=6 )
loop.close()
led.value( 1 ) # eteindre
print( "Fin!")
De même, si un message est publié, correspondant à l'allumage du chauffage, la fréquence de mesure passe bien de 10 à 1 minute.
Par contre, au bout d'un temps variable, le système se bloque et plus rien n'est publié.
La led passe à un niveau de clignotement "7", ce qui correspond à une activation par la fonction "check_mqtt_sub()"
A ce moment, dans le terminal, j'ai les messages suivants:
Code: Select all
[Errno 103] ECONNABORTED
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "main_AbriSud.py", line 266, in <module>
File "uasyncio/core.py", line 1, in run_until_complete
File "uasyncio/core.py", line 1, in run_until_complete
File "main_AbriSud.py", line 240, in run_every
File "main_AbriSud.py", line 214, in check_mqtt_sub
File "main_AbriSud.py", line 57, in led_error
La première n'avait pas de section try/exception et à ce moment, les messages étaient les suivants:
Code: Select all
run_every catch exception for <function check_mqtt_sub at 0x3fff04d0>
Task exception wasn't retrieved
future: <Task> coro= <generator object 'run_every' at 3fff0eb0>
Traceback (most recent call last):
File "uasyncio/core.py", line 1, in run_until_complete
File "main_AbriSud.py", line 237, in run_every
File "main_AbriSud.py", line 211, in check_mqtt_sub
File "umqtt/simple.py", line 215, in check_msg
File "umqtt/simple.py", line 179, in wait_msg
OSError: [Errno 103] ECONNABORTED
Voyez-vous ce qui cloche?
Merci d'avance pour toute piste.