HELP- IOT 100% custom en BLEa sur esp32

Bonjour à tous,
J’ai démarré dans la domotique, il y a quelques semaines et j’y suis plus pour le coté « bidouille » que le besoin de tout monitorer / controler à la maison.
Je planche sur un projet de création d’un device IOT 100% custom à base d’ESP32 que je souhaiterai intégrer à Jeedom avec le plugin BLEA.
Voilà donc où j’en suis et là ou je coince !

Coté ESP32

  • Configuration du serveur BLE OK
  • Configuration d’un service BLE OK (je peux lui donner le UUID souhaité sur 16 ou 128bits) - J’ai choisi « 0x1F10 - Environnement sensing »
  • Configuration d’un ou plusieurs characteristics OK (Temp, speed, heart rate…)
  • J’arrive à envoyer les valeurs que je souhaite sous plusieurs format (HEX - String - Float) et tout est décodé comme il faut par des clients BLE (type nRF connect sur android)

Coté JEEDOM-BLEA

  • J’ai pu créé un fichier json spécifique avec les propriétés, icones, unités, etc…
  • J’ai pu créé le pendant .py, à partir d’une copie d’un LY003MMC (code inchangé - voir ci-après)
  • Dans le log de blea (mode debug), je vois le device (recherche des inconnus), mais je n’arrive pas à avoir de « is valid data » qui reste désespérément vide

Ma question :
Quelqu’un a-t-il une info sur le format de donnée à envoyer ou une méthode pour afficher les datas envoyés par mon ESP dans le LOG de BLEA ? se serait mon point de départ pour customiser le fichier python.
Je ne sais pas si je ne vois pas les datas car mon fichiers python n’est pas encore OK ou si les datas ne sont pas bonnes, donc mon fichier python ne peut pas les afficher, même brutes…
Voici le log de BLEA pour référence

[2021-03-27 22:02:09.210][DEBUG] : SCANNER------[(1, 'Flags', '06'), (9, 'Complete Local Name', 'ESP32-25RR'), (10, 'Tx Power', '03'), (3, 'Complete 16b Services', '00001f10-0000-1000-8000-00805f9b34fb')] True public ac:67:b2:3b:c5:6a
[2021-03-27 22:02:09.212][DEBUG] : LYWSD03------isvalid data=, mac=ac:67:b2:3b:c5:6a, name=ESP32-25RR, manuf=
[2021-03-27 22:02:09.214][DEBUG] : SCANNER------It's a unknown packet and I known this device so I send ac:67:b2:3b:c5:6a
[2021-03-27 22:02:09.214][DEBUG] : {'id': 'AC:67:B2:3B:C5:6A', 'type': 'default', 'name': 'ESP32-25RR', 'rssi': -81, 'source': 'local', 'present': 1}
[2021-03-27 22:02:09.292][DEBUG] : Send to jeedom : {'devices': {'AC:67:B2:3B:C5:6A': {'id': 'AC:67:B2:3B:C5:6A', 'type': 'default', 'name': 'ESP32-25RR', 'rssi': -81, 'source': 'local', 'present': 1}}}

PS : Je n’ai aucune idée de la raison pour laquelle le fichier affiche une line avec LYWSD03 !!!

Merci à vous.

1 « J'aime »

Bonjour,

Très intéressant la démarche blea sur Esp32, je suis preneur d’un partage des étages que tu as accomplies.

OK.
En esperant avoir un peu d’aide sur la partie jeedom (La partie Arduino ESP me pose pas trop de soucis…)
Coté ESP, j’ai parcouru la DOC du protocol GATT. En résumé, un IOT BLE se construit comme çà :

  • 1 SERVEUR qui possède un nom : (chaine de charactères arbitraires)
  • Ce serveur peut contenir un ou plusieurs SERVICES chacun identifié par un UUID (Identifiant unique sur 16 ou 128 bits*)
  • Chaque SERVICE peut contenir à son tour 1 ou plusieurs CHARACTERISTIQUES chacun identifié par un UUID (16 ou 128 bits*) et pouvant être complété par un DESCRIPTEUR

*UUID : L’UUID est présent en 2 versions :

  • 16 bits en version courte pour des services / characteristiques déja identifiés par le protocol
    Exemple : 0x181A = environnemental Sensing / 0x1809 = Health Thermometer
  • 128 bits si c’est un élément Nouveau
    Exemple : Detection cyclique des frappes sur le clavier : 0x0001-0203-0405-0607-0809-0A0B-0C0D-0E0F

Exemple, dans le cas d’un LYWSD003 flashé (les capteurs XIAOMI thémo/hygro carrés), on a ;

  • 1 serveur qui contient 8 services
1. 0x1800 - GENERIC ACCESS - avec 3 characteristiques
        0x2A00 - Device Name (NOTIFY / READ)
        0x2A01 - Appearance (READ)
        0x2A04 - Prefed Connec° params ()
2. 0x1801 - GENERIC ATTRIBUTES - avec 2 chars
        0x2A05 - Service Changed (INDICATE)
        0x2902 - Client Char Config ()
3. 0x180A - DEVICE INFO - 6 chars all READ
        0x2A24 - Model Number
        0x2A25 - Serial Number
        0x2A26 - Firmware Rev
        0x2A27 - Hardware Rev
        0x2A28 - Software Rev
        0x2A29 - Manufacture Rec
4. 0x180F - BATTERY SERVICE - 1 char + descriptor
        0x2A19 - Battery level (NOTIFY / READ) + Descriptor 0x2902
5. 0x181A - ENVIRONNEMENT SENSING - 3 chars allr READ / NOTIFY
        0x2A1F - Temp Celcius
        0x2A6E - Temp
        0x2A6F - Humidity
6. 0x[128 bits UUID] - 1 Char 
        0x[128b UUID] - Unknown
7. 0x[128 bits UUID] - 1 Char 
        0x[128b UUID] - Unknown - DATA = 0x 55-27-00-00-00-00-A0-01-BF-7C-31-38-3C-00-00-00
8. 0xFE95 - XIAOMI (unknown when read by  nRF connect)
        EMPTY (aucune caharacteristique)
      

J’ai réussi à reproduire un shéma proche, mais c’est sur la partie JEEDOM que je bloque !

Bonjour,

Tu trouveras quelques explications ici qui pourront peut être t’aider :

N’hesites pas à poster ici le contenu de ton fichier python.

Bonjour,
je viens d’avancer sur mon problème. Après pas mal d’études et de lectures, j’ai fini par comprendre que les données RAW envoyées par le dispositif BLE devait avoir un code GAP 0x16 («Service Data»).
LA seule solution pour le faire est de passer par l’instruction suivante :
BLEAdvertisementData::setServiceData(uint16_t(0xFFFF), "123456789");

J’ai donc pas mal avancé et vérifié que ca fonctionnait. :smiley:
L’étape en cours, c’est la partie décodage dans le fichier PYTHON coté Jeedom. Ca ne devrait pas être trop long.
A voir si je vais changer le format d’échange de données pour gagner quelques bits.

Dès que tout est OK, j’ajoute les codes (ESP / .json / .py)

Ayé, J’ai fini…
Du coup, beaucoup de boulot pour réussir à faire ce que je voulais. Pouvoir utiliser le BLE advertisement pour envoyer des infos custom et les recevoir / décoder dans Jeedom via le plugin gratuit BLEA.
Le projet se compose de 5 fichiers en tout :

  • ESP32.ino qui est le fichier à transférer sur l’ESP32
  • ESP32.py qui est le fichier BLEA qui fait le tri des infos reçues en données lisibles par Jeedom
  • ESP32.json qui est le fichier de config BLEA qui défini les objets de type ESP32 et les infos que Jeedom/BLEA doit récupérer / afficher / historiser
  • BLEAdvertising.cpp et son pendant BLEAdvertising.h qui sont 2 fichiers issus de la librairie ESP32 pour faire de l’IOT BLE, mais auxquels ils manquaient une fonction que j’ai du rajouter pour les besoins du code.

ESP32.INO :
Ce fichier est un sketch assez classique pour arduino et ESP32. On y déclare les paramètres assez classiques pour du BLE (Serveur, advertising data, variables à transmettre). Les data sont fournies sous forme de chaine de caractères via la fonction sprintf(…) pour avoir les infos (entiers) sur 3 digits à la suite. (Le code est assez logique à comprendre quand on connait un peu arduino)
ATTENTION ! Ce code utilise une fonction que vous ne trouverez nul par ailleurs sur le net (pour l’instant :slight_smile:) voir plus loin les explications.

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
 
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
BLEAdvertising *pAdvertising;
BLEAdvertisementData advData;

// cData Structure
// CHAR  -  Value  - Description
//-------------------------------------
// 01-03     xxx     10x(temp+50)    in °C  (683 > (883-500)/10 = 18.3°C) [0>999 = -50>49.9°C]
// 04-06     xxx     10x humidity    in %   (475 > 47.5%)    [0>999 = 0>99.9%]
// 07-09     xxx     10x windspeed   in m/S (065 > 6.5 m/s) [0>999 = 0>99.9m/s : 359 km/h]
// 10-12     xxx      1x windOrigin  in °   (015 > 15.0° = NNE)
// 13-15     xxx     10x rainfall    in mm  (121 > 12.1 mm)

float i=0;
float temp=683;
float humi=475;
float wnds=065;
float wndd=015;
float rain=121;
char result[15];   // 5 parameters * 3 "digits" = 15 chars
 
void setup() {
  Serial.begin(115200);
  
  BLEDevice::init("ESP32");                     // Create the BLE Device
  pServer = BLEDevice::createServer();          // Create the BLE Server
}

void loop() {

// Compute data to send
    i+=0.1;
    int temp2=temp*10*cos(i); // this is a custom function to get the values to be sent
    int humi2=humi*10*cos(i); // Replace this part with your custom way to get data
    int wnds2=wnds*10*cos(i);
    int wndd2=wndd*10*cos(i);
    int rain2=rain*10*cos(i);

    // Format the values in triplets of digits (%03d) 
    sprintf(result, "%03d%03d%03d%03d%03d", temp2, humi2, wnds2, wndd2, rain2);

// create advertising data set (clean at each exec of loop)
   BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
   advData.clearAdvertisementData();
   advData.setFlags(0x06);
   advData.setServiceData(uint16_t(0xFE95), result);   // 95fe 363833343735303635303135313231
   //                                                           6 8 3 4 7 5 3 6 5 0 1 5 1 2 1  
   // each char in the data is replaced by his HEX value (ASCII) (0-9 : 30-39 _ a-z : 61-7a - A-Z : 41-5a)
  
  // Set advertisement data
   pAdvertising->setAdvertisementData(advData);
   pAdvertising->start();

    delay(5000);
}

esp32.json
Fichier assez classique pour qui bricole dans BLEA. A copier > coller via JeeXplorer dans :
(attention aux majuscules_minuscules

plugins \ blea \ core \ config \ devices \ esp32 (à créer) \ exp32.json

Vous pouvez rajouter une photo de votre choix et la nommer esp32.jpg
Là, c’est assez simple, on définit les différentes infos que Jeedom doit récupérer à partir du fichier python. (L’important c’est que les logicalId de ce fichier correspondent aux action[« variable »] = valeur du fichier esp32.py)

{
    "esp32": {
        "name": "esp32",
        "groupe" : "Capteurs",
		"configuration" : {
			"needsrefresh" : 0,
			"name" : "esp32",
			"battery_type" : "1x3V CR2032",
			"delay" : 900,
			"cancontrol" : 0
		},
        "commands": [
			{
                "name": "Température",
                "type": "info",
                "subtype": "numeric",
                "display": {
                    "icon": "<i class=\"fas fa-thermometer-empty\"><\/i>",
					"generic_type": "DONT"
                },
                "isVisible": 1,
                "isHistorized": 0,
                "unite": "°C",
                "logicalId": "temperature",
				"template": {
					"dashboard": "line",
					"mobile": "line"
				}
            },
			{
                "name": "Humidité",
                "type": "info",
                "subtype": "numeric",
                "display": {
					"icon": "<i class=\"fas fa-tint\"><\/i>",
					"generic_type": "DONT"
				},
                "isVisible": 1,
                "isHistorized": 0,
                "unite": "%",
                "logicalId": "moisture",
				"template": {
					"dashboard": "line",
					"mobile": "line"
				}
            },
			{
                "name": "Batterie",
                "type": "info",
                "subtype": "numeric",
                "display": {
					"icon": "<i class=\"fas fa-battery-full\"><\/i>",
					"generic_type": "DONT"
				},
                "isVisible": 0,
                "isHistorized": 0,
                "unite": "%",
                "logicalId": "battery",
				"template": {
					"dashboard": "line",
					"mobile": "line"
				}
            },
          {
                "name": "WindSpeed",
                "type": "info",
                "subtype": "numeric",
                "display": {
					"generic_type": "GENERIC"
                },
                "isVisible": 1,
                "isHistorized": 0,
                "unite": "m/s",
                "logicalId": "windspeed",
				"template": {
					"dashboard": "line",
					"mobile": "line"
				}
            },
                    {
                "name": "WindDirection",
                "type": "info",
                "subtype": "numeric",
                "display": {
					"generic_type": "GENERIC"
                },
                "isVisible": 1,
                "isHistorized": 0,
                "unite": "°",
                "logicalId": "winddirection",
				"template": {
					"dashboard": "line",
					"mobile": "line"
				}
            },
                    {
                "name": "RainFall",
                "type": "info",
                "subtype": "numeric",
                "display": {
					"generic_type": "GENERIC"
                },
                "isVisible": 1,
                "isHistorized": 0,
                "unite": "m/s",
                "logicalId": "rainfall",
				"template": {
					"dashboard": "line",
					"mobile": "line"
				}
            },
            {
                "name": "Refresh",
                "type": "action",
                "subtype": "other",
                "display": {
                    "generic_type": "GENERIC"
                },
                "isVisible": 1,
                "isHistorized": 0,
                "unite": "",
                "logicalId": "refresh"
            }
        ],
		"compatibility": [
            {
                "manufacturer": "esp32",
                "name": "Température Humidité",
				"doc": "",
				"type": "Capteurs",
				"battery_type": "1x3V CR2032",
				"ref" : "",
				"comlink": "",
				"remark": "Capteurs perso a base dun ESP32",
				"inclusion" : "NA",
            }
        ]
    }
}

esp32.py
Fichier assez classique pour qui bricole dans BLEA. A copier > coller via JeeXplorer dans :
(attention aux majuscules_minuscules

plugins \ blea \ ressources \ blead \ devices \ exp32.py

Ici, c’est un simple décodage des données qui sont découvertes par le daemon BLEA. La feinte se situe dans la découverte des données par le plugin. La function isvalid() s’appuie sur le début de l’adresse MAC de votre ESP32 (rien de bien dur à changer ou à trouver avec une appli cliente BLE). La partie data.lower().startswith("95fe") correspond à une inversion des octets de la partie déclarée dans le fichier esp32.ino advData.setServiceData(uint16_t(0xFE95), result); FE95 <> 95fe.
Si vous changez l’un, changez l’autre.
Le reste est assez classique en fait :slight_smile:

# coding: utf-8
from bluepy import btle
import time
import logging
import globals
import struct
from multiconnect import Connector
from notification import Notification

class esp32():
        def __init__(self):
                self.name = 'esp32'
                self.ignoreRepeat = False

        def isvalid(self,name,manuf='',data='',mac=''):
                logging.debug('esp32------isvalid data=%s, mac=%s, name=%s, manuf=%s' % (data, mac, name, manuf))
                if name.lower() in [self.name]:
                        return True
                if data.lower().startswith("95fe") or (mac.lower().startswith("ac:67:b2")):
                        #broadcasted advertising data
                        return True

        def parse(self,data,mac,name,manuf):
                logging.info('esp32------adv data=%s, mac=%s, name=%s, manuf=%s' % (data, mac, name, manuf))
                action={}
                action['present'] = 1
                temp = (int(bytes.fromhex(data[4:6])+bytes.fromhex(data[6:8])+bytes.fromhex(data[8:10]))-400)/10
                humi = int(bytes.fromhex(data[10:12])+bytes.fromhex(data[12:14])+bytes.fromhex(data[14:16]))/10
                wnds = int(bytes.fromhex(data[16:18])+bytes.fromhex(data[18:20])+bytes.fromhex(data[20:22]))/10
                wndd = int(bytes.fromhex(data[22:24])+bytes.fromhex(data[24:26])+bytes.fromhex(data[26:28]))
                rain = int(bytes.fromhex(data[28:30])+bytes.fromhex(data[30:32])+bytes.fromhex(data[32:34]))/10                
                #logging.info('ESP32-------temp=%s, humi=%s, wnds=%s, wndd=%s, rain=%s' % (temp, humi, wnds, wndd, rain))
                action["temperature"] = temp
                action["moisture"] = humi
                action["windspeed"] = wnds
                action["winddirection"] = wndd
                action["rainfall"] = rain                
                logging.info('ESP32-------mac=%s, temp=%s, humi=%s, wnds=%s, wndd=%s, rain=%s' % (mac, temp, humi, wnds, wndd, rain))
                #logging.info('esp32------mac=%s, temp=%s, humi=%s, batt=%s' % (mac, temp,humi,batt))
                return action

        def read(self,mac):
                result={}
                try:
                        conn = Connector(mac)
                        conn.connect()
                        if not conn.isconnected:
                                conn.connect()
                                if not conn.isconnected:
                                        return
                        batt = bytearray(conn.readCharacteristic('0x3a'))
                        battery = batt[0]
                        notification = Notification(conn,drkamp)
                        notification.subscribe(10)
                        result['battery'] = battery
                        result['id'] = mac
                        logging.debug('esp32test------'+str(result))
                        return result
                except Exception as e:
                        logging.error(str(e))
                return result

        def handlenotification(self,conn,handle,data,action={}):
                result={}
                if hex(handle) == '0x36':
                        received = bytearray(data)
                        temperature = float(received[1] * 256 + received[0]) / 100
                        moisture = received[2]
                        result['moisture'] = moisture
                        result['temperature'] = temperature
                        result['windspeed'] = temperature
                        result['winddir'] = temperature
                        result['rainfall'] = moisture
                        result['id'] = conn.mac
                        result['source'] = globals.daemonname
                        globals.JEEDOM_COM.add_changes('devices::'+conn.mac,result)

globals.COMPATIBILITY.append(esp32)

BLEAdvertising.cpp et BLEAdvertising.h

BLEAdvertising.cpp.pdf (16,5 Ko) BLEAdvertising.h.pdf (2,9 Ko)
Notes, les fichiers sont à récupérer en enlevant le .pdf bien sûr.

Ce sont les fichiers que j’ai du bidouiller un peu (pas grand chose) car il me manquait une fonction. Si votre fichier esp32.ino ne compile pas, c’est peut-être à cause de çà. Donc il faut remplacer les 2 fichiers fournis en faisant une copie des originaux. (Attention, vous ne pouvez pas les laisser dans le même dossier. J’ai tenté, j’ai eu 2000 lignes d’erreur à la compilation :slight_smile: )
En gros, je n’ai fais qu’une chose, c’est rajouté une fonction qui vide les données qui sont « advertisées » . En décortiquant les fonctions liées au advertisingData comme setFlags(), ou setName() on voit que les infos sont « ajoutée » et non pas définies et donc remplacées si on appelle la fonction une 2ème fois. A force de rajouter des infos, les données dépassent le nombre de bytes autorisé et KABOOM. C’est pourquoi il faut vider les datas et c’est ce que j’ai fait avec la fonction toute con clearAdvertisingData() qui fait en gros : payload="". (trop dur !!!)

Voila, SI vous avez tout lu, BRAVO !
Je reste dispo si vous avez des questions car j’ai bien kiffé plonger dans les tréfonds du plugin :slight_smile:

++ tout le monde

PS : C:\Users[USER]\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.6\libraries\BLE\src

1 « J'aime »

Bonjour,
Super boulot.
Je suis intéressé par ton développement car j’ai réalisé un capteur, à base d’ESP32, pour mesurer la hauteur d’eau dans une cuve. Cette cuve est alimentée par un puit à l’aide d’une pompe. J’aimerai dans un premier temps récupérer la hauteur d’eau dans Jeedom (plateforme RPI 4). Dans un second temps je souhaite pouvoir commander un relais relié à un Esp32 pour commander la pompe du puit et ainsi automatiser le remplissage de ma cuve à l’aide d’un scénario dans jeedom.
Pense tu qu’il est possible de réaliser tout cela en partant de ta réalisation ?
Cordialement

Hello @android57640,
Je pense que c’est tout à fait possible pour la partie récupération de l’info.
Il est aussi possible avec BLEA d’envoyer des infos vers l’ESP32 via la réponse au advertising.
Ca demande un peu de boulot, mais surtout des tests et des validations, plus que de la recherche et du bidouillage.
Tu connais un peu le langage arduino ? Tu sais comment utiliser les pins pour récupérer/envoyer de la data Analogique / Digitale ?

Hello @DrKamp
Merci pour ta réponse qui me motive pour continuer. Oui je connais un peu le langage Arduino et je sais comment récupérer/envoyer des données analogique/digitale. J’ai déjà réussi à faire des mesures de hauteur d’eau de ma cuve avec mon Esp32 et un capteur US étanche. J’ai aussi pu envoyer mes mesures dans le Cloud ThingView pour les afficher les sous forme de courbe sur mon smartphone android.

Cool.
Donc pour moi, tu as toutes les billes (avec les codes que je te donne) pour continuer sur ton projet.
N’hésite pas si tu as des questions.
Moi, je dois recevoir mon kit Station météo dans quelques jours… hâte :slight_smile:

Hello @DrKamp
J’ai en effet des questions de débutant sur BLEa que je découvre. Ma première question porte sur la communication entre le RPI et l’ESP32. Etant donné que la portée du Bluetooth est limitée, j’imagine que je vais communiquer en WiFi entre le RPI et l’ESP32, l’ESP32 étant vu comme une antenne dans Jeedom. Si c’est bien cela, cela veut dire que dans l’ESP32 je doit établir la connexion à mon réseau WIFI dans ton code
ESP32.ino. Est ce comme cela que tu a procédé dans ta réalisation ?

Hello @DrKamp

J’ai mis en œuvre tes programmes coté ESP32 et Jeedom, mon ESP32 est bien détecté par le plugin BLEA. Par contre je ne vois aucune donné s’afficher. Pourtant dans ton sketch ESP32.ino, j’ai vu que tu incrémentais les valeurs (température, humidité…) en boucle. Donc ces données devraient être transmises toutes les 5 s à Jeedom, et s’afficher dans le Dashboard. y-a-t-il un paramétrage particulier à faire côté Jeedom pour que les données s’affichent ?

Hello @android57640,
Cette construction est valable principalement pour du BLEA, vu qu’il est concu pour être décodé par ce plugin.
Je ne suis pas certain du tout que ce sketch fonctionne pour du wifi. Car le codage (sprintf) est concu pour être lisible par BLEA…
Désolé, mais je ne pense pas que quoi que ce soit de ce que j’ai créé ici te soit utiles en létat pour du wifi.

Il faut faire une recherche d’équipement comme pour n’importe quel équipement BLEA.
Tu peux aussi uiliser le mode LOG > DEBUG pour avoir plus d’infos et voir si c’est bien reconnu et que l’info change toutes les 5 secondes.
++

@DrKamp
Merci de tes réponses. Je croie que j’ai trouvé une solution plus prometteuse, il s’agit du protocole MQTT. J’ai réussi à récupérer des mesures émises par mon ESP32 dans Jeedom en WiFi. Il est aussi possible de communiquer dans l’autre sens et d’envoyer des commandes vers un relai par exemple pour démarrer la pompe de mon puit. Le débogage est en plus très facile avec ce protocole et il y a plein de tuto très bien faits sur le sujet [Jeedom + MQTT, intégrer un objet connecté DIY au Dashboard

Juste comme ça a tu tester tasmota esp32 ?
je l’ait fait en proto avec une sonde 18b20 nickel …

Non, je connais pas. Je vais regarder.
Merci

1 « J'aime »

Hello,
Je pense en effet que MQTT est très adapté à ce système. Mais pour mon utilisation, il présente cependant 2 défauts majeurs (je mets volontairement de coté tous les avantages :slight_smile: )

  • Il nécessite de passer par le wifi et de se connecter. Je voulais me concentrer sur du BLEA en mode broadcast (donc disponible pour 10 personnes à proximité au besoin (même si peu probable…)
  • MQTT sur ESP était déja pas mal documenté. Je cherchais un peu de challenge avec qqch qui n’avait pas été fait :smiley:

Mais si le but, c’est la fonctionnalité, alors je suis d’acord pour dire que MQTT est bien plus adapté.

Hello,
Je suis d’accord avec toi pour le wifi, c’est le point faible. Je dois me connecter à un répéteur wifi pour être sur que mon ESP se connecte bien. Un autre point faible que j’ai détecté sur l’ESP avec Arduino c’est la capacité d’enchainer la publication en MQTT suivi de la souscription à un topic sans perdre de message. En émission ça marche car l’ESP est maître, mais en réception il y a parfois des problèmes de synchronisation. Je ne maîtrise pas encore bien la réception des messages dans la boucle.
Je me demande s’il n’est pas préférable de dédier un ESP à l’émission de message et un autre à la réception.

Nope, ce serait dommage.
Dans MQTT, je crois que le protocole publie la date du message et que ton serveur a plusieur niveau de service (0-1-2 de mémoire).
Essaye d’enregistrer la date/heure de ton dernier message pour le conparer au suivant.
Coté serveur, tu dois aussi avoir l’option de « redire » le message tant qu’il n’est pas accusé.

Voilà des pistes à creuser ‹ comme ca › à brûle pourpoing.