Problème d'envoi de donnée en MQTT

Bonjour à tous,

Je suis fasse un problème d'envoi de donnée en MQTT avec la commande client.publish

Il s'agit d'un pluviomètre branché à un ESP32, la lecture sur le moniteur série fonctionne parfaitement par contre, je n'ai aucune réception avec mon serveur nodered hébergé sur une RaspberryPi 5 via le brocker MOSQUITO.

Voici le code de l'esp32 :

// --------------------------------------  Connection wifi et borker MOSQUITTO  ----------------------//

#include <WiFi.h>
#include <PubSubClient.h>

#define ssid "########" 
#define password "###########" 

#define mqtt_server "192.168.1.##"
const char* mqttUser = "#########";
const char* mqttPassword = "##########"; 

char message_buff[100];
long lastMsg = 0;   
// long lastRecu = 0;
bool debug = false;  
char msg[50];
int value = 0;
String consigne;

WiFiClient espClient;
PubSubClient client(espClient);




//---------------------------------  WIFI  -----------------------------------------//

void setupWifi() 
{ 
  Serial.println("\n");
  WiFi.begin(ssid, password);
  Serial.print("Tentative de connexion...");

    
  while(WiFi.status() != WL_CONNECTED)
  {
        Serial.print(".");
        delay(100);
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
    
  }
  payload[length]='\0';
  String cons = String((char*)payload);
  consigne = cons;
  Serial.println();
  }

  void reconnect(){
    //Boucle jusqu'à obtenur une reconnexion
     while (!client.connected()) {
     Serial.print("Connexion au serveur MQTT...");
    // si MQTT sécurisé alors
    if (client.connect("Esp32_abris_bassin_deux", mqttUser, mqttPassword )) {
      Serial.println("Esp32_abris_bassin_deux");
      //Une fois connecté, on peut s'inscrire à un Topic
      client.subscribe("Relais/eclairage/jardin");
      debug = true;
    }
    else {
      Serial.print("failed, erreur : ");
      Serial.print(client.state());
      debug = false;
      Serial.println("5 secondes avant de recommencer");
      delay(5000);
      }
    } 
  }
  
// --------------------  Déclaration pluviomètre  ---------------------

#include <CircularBuffer.hpp>

#define INTERVAL 120                

CircularBuffer<float, 3> h_fifo;   

const byte rainPin = 34;            
unsigned int raincnt = 0;           
unsigned long lastSend; 

float pluie;

ICACHE_RAM_ATTR void cntRain() {
  raincnt++;
}

void getSendRain() {
  float h_total = 0.00; 

lastSend = millis();

 pluie = raincnt * 0.2794;       
 raincnt = 0;                        

  Serial.print("Pluie = "); 
  Serial.print(String(pluie));
  Serial.println(" mm ");

  using index_h = decltype(h_fifo)::index_t;
  for (index_h i = 0; i < h_fifo.size(); i++) {
    h_total += h_fifo[i];
  }
 
  h_total = h_total * 10;

}

//  -----------------  Déclaration des abonnements MQTT  ----------------------------

#define sonde1_topic "Pluviometre/exterieure"


// ------------------------------------------------------------------   SETUP  -------------------------------------------------------------------

void setup() {
  
   Serial.begin(115200);

 // -------------------------  Mise en route pluviomètre  ---------------------------
  
  pinMode(rainPin, INPUT);          // Montage PullUp avec Condensateur pour éviter l’effet rebond.
  attachInterrupt(digitalPinToInterrupt(rainPin), cntRain, FALLING); 
  lastSend = millis();

// ----------------------------  Lancement du wifi  -----------------------------

setupWifi();

  delay(1000); 
  
  Serial.println("\n");
  Serial.println("Connexion etablie!");
  Serial.print("Adresse IP: ");
  Serial.println(WiFi.localIP());
  delay(5000);
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  delay(500);
  
}

//  --------------------------------------------------------------------  LOOP  ---------------------------------------------------------------------

void loop() {
  
  if ( millis() - lastSend > INTERVAL*1000 ) { 
 getSendRain();
  }
  

  //Publication des mesures:
  
   if(debug) {

    client.publish(sonde1_topic, String(pluie).c_str(), true);
    
 }
}

J'ai déclaré la valeur pluie en : float pluie; et je la transforme en String pour que la chaine de caractère soit transmis via la commande publish : client.publish(sonde1_topic, String(pluie).c_str(), true);

A noter que le serveur fonctionne bien, j'ai plusieurs autres ESP32 avec d'autres sondes et tout remonte niquel se qui me dit que je dois mal manipuler la donnée du pluviomètre.

Quelqu’un a une idée pour me filer un coup de pouce ?

Merci

raincnt devrait être déclaré volatile et vous devriez mettre un mutex lors du traitement dans la loop car l'accès à des variables partagées peut poser un souci de cohérence. (une interruption pourrait survenir pendant que vous êtes en train de manipuler les octets de votre compteur).


PS/ attention aux calculs avec des #define

#define INTERVAL 120
...
  if ( millis() - lastSend > INTERVAL * 1000 ) {

sur ESP32 ça va fonctionner car un int est par défaut sur 32 bits mais sur AVR 120 x 1000 ne fait pas 120000 car on déborde ce qu'un entier peut représenter (32767)

il vaudrait mieux faire

const uint32_t INTERVAL = 120ul * 1000ul; // le ul dit au compilateur qu'on est en unsigned long
...
  if ( millis() - lastSend > INTERVAL ) {

J'ai modifié comme ceci :

// --------------------  Déclaration pluviomètre  ---------------------

#include <CircularBuffer.hpp>

const uint32_t INTERVAL = 120ul * 1000ul;                // Interval en secondes entre 2 envoi à Domoticz (.

CircularBuffer<float, 3> h_fifo;    // On initialise la taille du buffer pour 6 mn glissantes. (3 = 1 relevé toutes les 2 min)

const byte rainPin = 34;            // PIN de connexion de capteur.
volatile unsigned int raincnt = 0;           // Initialisation du compteur.
unsigned long lastSend; 

float pluie;

ICACHE_RAM_ATTR void cntRain() {
  raincnt++;
}

void loop() {
  
  
  if ( millis() - lastSend > INTERVAL ) { 
 getSendRain();
  }

Ca ne marche pas, faudrait il que la variable (pluie); soit volatile également ?

non, mais il faudrait protéger l'accès avec un mutex (ou détacher l' interruption si vous voulez faire simple) car lors des opérations

 pluie = raincnt * 0.2794;       
 raincnt = 0;                        

vous n'êtes pas à l'abri d'une modification de raincnt

je ne sais pas si c'est lié au souci que vous avez mais c'est un souci potentiel


sinon ajoutez des infos de débug et partagez ce que vous voyez dans le terminal série (mettre un Serial.flush(); après chaque message de debug)

j'ai donné un "coup de propre" au code

si vous essayez cela, ça dit quoi ?

// --------------------------------------  Connexion WiFi et Broker MQTT  ----------------------//

#include <WiFi.h>
#include <PubSubClient.h>

#define DEBUG 1 // METTRE À 0 POUR SUPPRIMER LES TRACES

#if DEBUG
#define D_SerialBegin(...) Serial.begin(__VA_ARGS__)
#define D_print(...)       Serial.print(__VA_ARGS__)
#define D_write(...)       Serial.write(__VA_ARGS__)
#define D_println(...)     Serial.println(__VA_ARGS__)
#else
#define D_SerialBegin(...)
#define D_print(...)
#define D_write(...)
#define D_println(...)
#endif

constexpr const char* ssid = "########";
constexpr const char* motDePasseWifi = "###########";

constexpr const char* serveurMqtt = "192.168.1.##";
constexpr const char* utilisateurMqtt = "#########";
constexpr const char* motDePasseMqtt = "##########";

constexpr const char* idClientMqtt = "esp32_abris_bassin_deux";
constexpr const char* topicAbonnement = "Relais/eclairage/jardin";
constexpr const char* topicPublication = "Pluviometre/exterieure";

WiFiClient clientWifi;
PubSubClient clientMqtt(clientWifi);

volatile unsigned long compteurPluie = 0;
unsigned long dernierEnvoi;
float quantitePluie;

constexpr byte pinPluie = 34;
constexpr unsigned long intervalleEnvoi = 120000ul; // Intervalle de 120 secondes


ICACHE_RAM_ATTR void interruptionPluie() {
  compteurPluie++;
}

void connexionWifi() {
  D_println("\nConnexion au WiFi...");
  WiFi.begin(ssid, motDePasseWifi);

  while (WiFi.status() != WL_CONNECTED) {
    D_print(".");
    delay(100);
  }
  D_println("\nConnexion WiFi établie!");
  D_print("Adresse IP: ");
  D_println(WiFi.localIP());
}

void callbackMqtt(char* topic, byte* payload, unsigned int length) {
  D_print("Message reçu [");
  D_print(topic);
  D_print("] ");
  for (unsigned int i = 0; i < length; i++)D_write(payload[i]);
  D_println();
}

void connexionMqtt() {
  while (!clientMqtt.connected()) {
    D_print("Connexion au serveur MQTT...");
    if (clientMqtt.connect(idClientMqtt, utilisateurMqtt, motDePasseMqtt)) {
      D_println(" connecté!");
      clientMqtt.subscribe(topicAbonnement);
    } else {
      D_print("échec, erreur : ");
      D_print(clientMqtt.state());
      D_println(" - Réessai dans 5 secondes");
      delay(5000);
    }
  }
}

void envoyerDonneesPluie() {
  clientMqtt.publish(topicPublication, String(quantitePluie).c_str(), true);
  dernierEnvoi = millis();
}

void setup() {
  pinMode(pinPluie, INPUT);
  D_SerialBegin(115200);
  connexionWifi();
  clientMqtt.setServer(serveurMqtt, 1883);
  clientMqtt.setCallback(callbackMqtt);
  attachInterrupt(digitalPinToInterrupt(pinPluie), interruptionPluie, FALLING);
}

void loop() {
  if (!clientMqtt.connected()) {
    connexionMqtt();
  }
  clientMqtt.loop();

  if (millis() - dernierEnvoi >= intervalleEnvoi) {
    
    // -------------------------------------------------
    // section critique, on désactive les interruptions
    // -------------------------------------------------
    detachInterrupt(digitalPinToInterrupt(pinPluie));
    unsigned long copieCompteurPluie = compteurPluie;
    compteurPluie = 0;
    attachInterrupt(digitalPinToInterrupt(pinPluie), interruptionPluie, FALLING);
    // -------------------------------------------------
    // fin de section critique
    // -------------------------------------------------
 
    quantitePluie = copieCompteurPluie * 0.2794;

    D_print("Pluie = ");
    D_print(quantitePluie);
    D_println(" mm");
    envoyerDonneesPluie();
  }
}

J’essaie ce soir, merci pour l'aide

Salut,
J'ai testé le code et ça marche bien, la valeur remonte bien à NodeRed.
Arriverais tu as me détailler un peu les lignes suivantes ?

#define DEBUG 1 // METTRE À 0 POUR SUPPRIMER LES TRACES

#if DEBUG
#define D_SerialBegin(...) Serial.begin(__VA_ARGS__)
#define D_print(...)       Serial.print(__VA_ARGS__)
#define D_write(...)       Serial.write(__VA_ARGS__)
#define D_println(...)     Serial.println(__VA_ARGS__)
#else
#define D_SerialBegin(...)
#define D_print(...)
#define D_write(...)
#define D_println(...)
#endif 

constexpr qui est devant chaque variable ?

 detachInterrupt(digitalPinToInterrupt(pinPluie));
    unsigned long copieCompteurPluie = compteurPluie;
    compteurPluie = 0;
    attachInterrupt(digitalPinToInterrupt(pinPluie), interruptionPluie, FALLING);

D_print Quelle différence avec un Serial.print ?

Merci par avance

Content que ça fonctionne !

Ce sont des macros pour le debug. En utilisant D_print("coucou");

  • si le debug est activé c’est comme si j’avais mis Serial.print("coucou");
  • ou ça enlève la commande en entier si débug est désactivé. (La macro est remplacée par rien du tout, pas de code)

Les veulent dire « n’importe quelle liste de paramètres » en entrée de la macro et __VA_ARGS__ dans ce par quoi on remplace la macro veut dire « mettre tous les paramètres ici ».

Ainsi, en utilisant ces macros et en modifiant juste une valeur de DEBUG, vous pouvez enlever toutes les traces de debug du code quand vous compilez. C’est plus simple que d’avoir a parcourir le code et mettre partout des //

constexpr ça veut dire qu’on veut s’assurer que ce soit définit à la compilation et que la valeur ne changera pas. Ce n’est pas obligatoire mais c’est une indication pour le compilateur. Vous pouvez juste conserver le type avec const tout seul.

La partie en section critique fait la copie du compteur et le remet à zéro avec les interruptions désactivées. Ça ne prend que quelques microsecondes donc on ne ratera pas grand chose même s’il pleut fort. De cette façon je suis sûr qu’aucune interruption va venir mettre le bazar dans mes 2 lignes pendant que j’utilise le compteur. Une fois sorti de la section critique j’utilise la copie pour les calculs - celle ci n’étant pas modifiable par une interruption, on est tranquille.

Super, merci à toi.
A tout hasard, connaitrais tu un ouvrage en français ludique et bien fait pour affuter mes connaissances en C ?

Autant apprendre le C++

Les cours openclassroom (intro et approfondissement) sont pas mal. Ils ont aussi un cours sur le C

1 Like