Skip to content

Description de l'implémentation de la surveillance interne

Ce script implique la connexion à un broker MQTT et l'envoi de données à partir d'un capteur DHT22 à l'aide d'un microcontrôleur. Il utilise la bibliothèque umqttsimple pour la communication MQTT et une bibliothèque de journalisation personnalisée pour enregistrer les événements.

Connexion MQTT

La bibliothèque umqttsimple est une bibliothèque officielle MicroPython permettant de communiquer avec un broker MQTT (Message Queuing Telemetry Transport). Voici le lien vers cette bibliothèque : umqttsimple.

Logging

La bibliothèque de journalisation que j'ai créée en Python sert à créer et à écrire des logs dans un fichier, fournissant des informations sur ce que fait le microcontrôleur en temps réel. Mon log est constitué de trois parties :

  1. Niveau de gravité : Il y a quatre possibilités :
    • [Info] : Pour les informations générales envoyées au serveur.
    • [Data] : Pour les données envoyées au serveur.
    • [Warning] : Pour les avertissements indiquant que certaines exécutions n'ont pas fonctionné.
    • [Error] : Pour toutes les erreurs d'exécution.
  2. Date et heure : Une chaîne de caractères composée de la date avec l'heure, les minutes et les secondes, récupérée grâce à la fonction time.localtime().
  3. Message : Une chaîne de caractères qui informe de ce qu'il s'est produit.

Voici un exemple d'une information retournée par un module de surveillance interne :

[Info] 2024-04-04 14:21:55 : Network config: ('192.168.1.191', '255.255.255.0', '192.168.1.1', '192.168.1.1')

Pour plus de détails voici le lien de la documentation de la bibliothèque.

Fonctionnement asynchrone

Le code utilise la bibliothèque uasyncio pour gérer les opérations de manière asynchrone, ce qui permet au microcontrôleur de traiter plusieurs tâches sans bloquer l'exécution. Voici comment cela fonctionne dans ce projet :

Tâche MQTT

La tâche mqtt_task vérifie périodiquement les nouveaux messages MQTT. Elle est définie comme suit :

async def mqtt_task():
    while True:
        client.check_msg()
        await asyncio.sleep(0.1)

Cette tâche utilise une boucle infinie pour appeler client.check_msg(), qui vérifie les nouveaux messages MQTT. La fonction await asyncio.sleep(0.1) permets de faire une pause de 0,1 seconde avant de vérifier à nouveau les messages, permettant ainsi à d'autres tâches de s'exécuter.

Boucle principale

La boucle principale main_loop lit les données du capteur DHT22 et les envoie au broker MQTT. Elle est définie comme suit :

async def main_loop():
    while True:
        try:
            sensor.measure()
            time.sleep(5)
            voltage = adc.read_u16() * 3.3 / (65535 * 3)
            battery_level = voltage / 3.3 * 100
            data = {
                "id_mac": mac_address,
                "temperature": sensor.temperature(),
                "humidity": sensor.humidity(),
                "voltage": battery_level
            }
            client.publish(topic_pub_data, json.dumps(data))
            logging.info(f"Data: {data}")
            gc.collect()
            await asyncio.sleep(600)
        except Exception as e:
            logging.error(f"Error reading data from DHT22: {e}")

Cette boucle lit les données du capteur DHT22, calcule le niveau de batterie, et publie ces données au broker MQTT avec comme identifiant ça mac adresse. La fonction await asyncio.sleep(600) permets de faire une pause de 10 minutes après chaque cycle, permettant aux autres tâches de s'exécuter.

Gestion des commandes

La fonction callback gère les messages MQTT reçus et effectue des actions en conséquence. Elle est définie comme suit :

def callback(topic, msg):
    global task_main_loop
    message = msg.decode("utf-8")

    if message == "is_alive":
        logging.receive("Is alive")
        client.publish(topic_pub_response, msg='Alive')
    elif message == "status":
        logging.receive("status")
        client.publish(topic_pub_response, msg=ssid + ' & ' + key)
    elif message == "clean_log":
        logging.receive("Clean log")
        logging.clean()
        client.publish(topic_pub_response, 'Log file has been cleaned')
    elif message == "show_log":
        logging.receive("Show last 10 log")
        last_10_log = logging.get_10_last_lines()
        client.publish(topic_pub_response, json.dumps(last_10_log))
    elif message == "start":
        logging.receive("Start measurement")
        asyncio.create_task(start_sensor())
        client.publish(topic_pub_response, 'Main loop started')
    elif message == "stop":
        logging.receive("Stop measurement")
        asyncio.create_task(stop_sensor())
        client.publish(topic_pub_response, 'Main loop stopped')
    elif message == "restart":
        logging.receive("Restart measurement")
        asyncio.create_task(restart_sensor())
        client.publish(topic_pub_response, 'Main loop restarted')

En fonction du message reçu, la fonction callback effectue diverses actions telles que répondre à des commandes de statut, nettoyer les logs, ou démarrer/arrêter/reprendre la boucle principale.

Fonction principale

La fonction principale main gère la connexion Wi-Fi, la synchronisation de l'heure et la connexion au broker MQTT, et lance les tâches asynchrones. Elle est définie comme suit :

async def main():
    connect_wifi(ssid, key)
    sync_rtc_with_ntp()
    connect_mqtt()
    await asyncio.gather(
        mqtt_task(),
        start_sensor()
    )

La fonction asyncio.gather permet d'exécuter plusieurs tâches asynchrones en parallèle : mqtt_task et start_sensor. Cela permet au microcontrôleur de vérifier les messages MQTT tout en lisant les données du capteur simultanément.

Exécution de la boucle principale

Enfin, la boucle principale est lancée avec asyncio.run(main()) :

asyncio.run(main())

Cela démarre l'exécution de la fonction main, initiant ainsi la connexion Wi-Fi, la synchronisation de l'heure, la connexion MQTT, et les tâches asynchrones définies.