Problème pour publier en Mqtt une mesure de température reçue par Lora

Bonjour,

J'essaie tant bien que mal de transférer les données reçues d'un capteurs de température qui est situé hors zone wifi. J'ai donc opté pour la technologie Lora entre un ESP32 équipé d'un SX1278 et d'un TTGO Lilygo lora.

Jusque-là tout fonctionne correctement. C'est à partir du moment où je tente de publier la donnée via mosquitto mqtt afin d'y avoir accès depuis home assistant. J'utilise également Mqtt explorer pour visualiser les échanges avec mosquitto. Je monitore également sur le port série les données.

Tout fonctionne correctement tant que je ne publie pas la données de température Mais a partir du moment ou je la publie après changement en string pour passer en mqtt, j'ai quelques soucis :

  • le port série affiche des valeurs incohérente du string (enfin je pense) et défiles en continue

  • les données envoyées au mqtt s'affichent bien en cohérence avec le port série mais restent incohérentes et le nombre de publication monte en flèche

  • a partir d'un certain moment, plus rien ne bouge (je pense)

J'ai l'impression donc qu'il y a au moins deux problèmes et j'ai pourtant regardé beaucoup d'exemples mais comme je ne suis pas expert, j'ai du mal a tout saisir...

Je vous joins le code que j'ai chargé dans le récepteur Lilygo en espérant que cela puisse m'aider a ce que vous trouviez mon/mes problèmes :

#include <SPI.h>
#include <LoRa.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Streaming.h>
#include <Ticker.h>


#define OLED_SDA 21     // Attention: Les broches SDA et SCL de l'affichage OLED sont inversées sur le dessin de LilyGO et sur GitHub.
#define OLED_SCL 22 
#define OLED_RST 12     // Note: Le TTGO Lora32 v2 n'utilise pas le signal reset, mais la librairie Adafruit_SSD1306, oui.
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

const int csPin = 18;          // LoRa radio chip select-TTGO
const int resetPin = 23;       // LoRa radio reset-TTGO
const int irqPin = 26;         // change for your board; must be a hardware interrupt pin-TTGO
const int sckPin = 5;          //-TTGO
const int misosPin = 19;       //-TTGO
const int mosiPin = 27;        //-TTGO


const char* ssid = "xxxxxx";      // Nom du wifi
const char* password = "xxxxxx";    // Mot de passe wifi

const char* mqtt_server = "xxx.xxx.x.xx";   // Adresse IP du brovker MQTT
const char* mqtt_username = "xxxxx";     // User MQTT
const char* mqtt_password = "xxxxxx";    // Password MQTT
const char* mqtt_topic = "ESP32/DS18B20/temp_piscine";   // Nom du topic MQTT envoyé par home assistant

char incoming ;
char msg[4];

WiFiClient espClient;
PubSubClient client(espClient);

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RST);

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

  // setup WiFi
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  //initialize OLED
  Wire.begin(OLED_SDA, OLED_SCL);
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false)) 
  { // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
  }
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("LORA RECEIVER ");
  display.display();

  Serial.println("LoRa Receiver");

  // override the default CS, reset, and IRQ pins (optional)
    SPI.begin(sckPin, misosPin, mosiPin, csPin);
    LoRa.setPins(csPin, resetPin, irqPin); // set CS, reset, IRQ pin

  if (!LoRa.begin(433E6)) 
  {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
}
//Fonction connexion au wifi appelé dans le setup 
void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  display.setCursor(0,0);

  WiFi.begin(ssid, password);

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

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

//Fonction connexion au wifi appelé dans le setup 
void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();
}

//Fonction connexion au serveur Mqtt  dans le loop 
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client", mqtt_username, mqtt_password)) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}


void loop() 
{
  client.loop();
  // try to parse packet
  int packetSize = LoRa.parsePacket();
  if (packetSize) 
{
    // received a packet
    Serial.print("Received packet ");
    // read and print packet
    display.clearDisplay();
    display.setCursor(0,0);
    while (LoRa.available())
    {
      incoming = ((char)LoRa.read());
      //Serial.print((char) LoRa.read());
      Serial.print(incoming);
      display.print(incoming);  
    }
    // print RSSI of packet
    Serial.print("' with RSSI ");
    Serial.println(LoRa.packetRssi());    
    //display.setCursor(0,0);
    display.print("' with RSSI ");
    display.println(LoRa.packetRssi());
    display.display();
      }
  if (!client.connected()) 
  {
    reconnect();
  }
  dtostrf(incoming, 6, 2, msg);
  Serial.print (msg);
  client.publish(mqtt_topic,msg,false);
  //client.publish(mqtt_topic,dtostrf(LoRa.packetRssi(),6,2,msg),false);
  
}

la variable incoming est de type char, pas double.
Tu as aucun warning à la compilation?
tu est sur sur tu n'envoi pas des chaines sur la connexion Lora, plutôt que des double, puisque la fonction read renvoi un char?
D'ailleurs, comme incoming est un char, si sur le serial tu vois les bons caractères c'est que tu as bien une chaine, car sinon ton char représenterais un octet sur 4 d'un double, qui n'a pas de représentation sans décodage.

Pas sûr d'être clair :slight_smile:

oui, quand vous faites cela

    while (LoRa.available())
    {
      incoming = ((char)LoRa.read());
      //Serial.print((char) LoRa.read());
      Serial.print(incoming);
      display.print(incoming);
    }

vous affichez les caractères un à un.

à la fin de cette boucle while, incoming ne contient que le dernier octet reçu donc aucune chance que

  dtostrf(incoming, 6, 2, msg);

ne fasse quoi que ce soit d'interessant

➜ il faut mettre les caractères reçus dans un tableau (de taille au moins packetSize + 1 car il faut un caractère nul à la fin pour pouvoir ensuite appeler dtostrf() (EDIT) pour appeler strtod()

Tu es sûr, la fonction ne prend pas un double pour le convertir dans un buffer(char*)

euh si vous avez raison !! j'ai lu ça trop vite

(si l'affichage est correct, c'est qu'il reçoit sans doute de l'ASCII donc ce n'est pas la fonction appropriée, j'ai corrigé dans le post ci dessus merci)

Bonjour terwal et merci pour ton retour, pour aider dans la compréhension, je joins également la partie émetteur :


#include <SPI.h>
#include <LoRa.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Streaming.h>
#include <Ticker.h>

#define SLEEP_DELAY_IN_SECONDS  30



const int csPin = 5;             // LoRa radio chip select-ESP32
const int resetPin = 14;         // LoRa radio reset-ESP32
const int irqPin = 2;            // change for your board; must be a hardware interrupt pin-ESP32
const int sckPin = 18;           //-ESP32
const int misosPin = 19;         //-ESP32
const int mosiPin = 23;          //-ESP32
const int ONE_WIRE_BUS = 15;    // DS18B20 data

const char* mqtt_server = "xxx.xxx.x.xx";   // Adresse IP du brovker MQTT
const char* mqtt_username = "xxxxxxx";     // User MQTT
const char* mqtt_password = "xxxxxxxxx";    // Password MQTT
//const char* mqtt_topic = "ESP32/DS18B20/temp_piscine";   // Nom du topic MQTT envoyé par home assistant




OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);

char temperatureString[6];

void setup() {
  Serial.begin(115200);
  
  // setup OneWire bus
  DS18B20.begin();
  
  while (!Serial);

  Serial.println("LoRa Sender");

  // override the default CS, reset, and IRQ pins (optional)
  SPI.begin(sckPin, misosPin, mosiPin, csPin);
  LoRa.setPins(csPin, resetPin, irqPin); // set CS, reset, IRQ pin

  if (!LoRa.begin(433E6)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
}

float getTemperature() {
  Serial << "Requesting DS18B20 temperature..." << endl;
  float temp;
  do {
    DS18B20.requestTemperatures();
    temp = DS18B20.getTempCByIndex(0);
    delay(5000);
  } while (temp == 85.0 || temp == (-127.0));
  return temp;
}

void loop() {
  
  float temperature = getTemperature();
      // convert temperature to a string with two digits before the comma and 2 digits for precision
      dtostrf(temperature, 2, 2, temperatureString);
      // send temperature to the serial console
      Serial << "Sending temperature: " << temperatureString << endl;
      // send temperature to the MQTT topic
      // test raté #uint16_t packetIdPub1 = mqttClient.publish(mqtt_topic, 1, true, String(temp).c_str());
      //client.publish(mqtt_topic,temperatureString,false);

 /*
  Serial.print("Sending packet: ");
  Serial.println(counter);
  Serial.println(temperatureString);
  send packet
*/

  LoRa.beginPacket();
  LoRa.print(" Temp.piscine ");
  //LoRa.print(counter);
  LoRa.print(temperatureString);
  LoRa.print("*C");
  LoRa.endPacket();

  //counter++;

  

  delay(5000);
}

C'est bien un char que je transmet par lora au recepteur que j'affiche ensuite sur le port série sous la variable incoming. Comme indiqué, la ou ou ça se complique c'est quand j'active le mqtt et là... c'est le drame sur le port série et sur le display. Je dois avouer que j'ai du mal à comprendre ce qu'est un double ou comment faire un tableau mais j'ai une grande envie d'apprendre via beaucoup de lecture et des vidéo mais malheureusement ça vient doucement. Ce que je comprend de vos message c'est que dtostrf n'est pas la bonne solution car je reçois les données caractère par caractère et qu'il faut que je les range pour pouvoir recréer une chaine affichable comme fait sur l'émetteur. Je vais creuser sur ça mais d'ici là si le fichier pour l'émetteur vous aide a comprendre si je me suis trompé plus tôt, je suis preneur.
Merci encore !

Bon j'ai bien trouvé le tableau dans mon fichier "char msg[ ]", reste encore la notion de double ainsi que de savoir manipuler "string to float"... Et je confirme que je n'ai pas de warning à la compilation.

Ce n’est pas un char mais tout un texte !

aye, ok, cela pose un soucis ducoup ? désolé te passer pour un ignorant :wink:

Ce n’est pas un souci mais il faut en tenir compte

Vous envoyez un truc du genre

" Temp.piscine 28.45*C"

Donc côté réception il faut ignorer le texte et analyser le 28.45 pour le transformer en valeur numérique

Pourquoi tu ne passe passe simplement la température fini par un \n?
Tu stocke la chaine jusqu'au \n, et du coup tu pourrais passer cette valeur à ton broker.

Bon, voici alors là ou j’en suis

J’ai supprimé le texte sur le transmetteur afin de ne n’envoyer par lora que la température

J’ai essayé avec strdot () sans succès que ce soit en incoming char ou double, j’ai à la compilation le message suivant :
cannot convert 'double' to 'const char*' for argument '1' to 'double strtod(const char*, char**)'

J’ai essayé de passer incoming en variable double et pas char et là sur le display, le serial et le mqtt j’ai les mêmes éléments mais pas cohérents :
image

J’ai également modifier la variable char msg[5]; à 5 pour être en phase avec dtostrf(incoming, 2, 2, msg);

Et pour rappel j’envoi sur le port série et le mqtt la même donnée

dtostrf(incoming, 2, 2, msg);
client.publish(mqtt_topic,msg,false);
Serial.print (msg);

J’obtiens en résultat un défilement rapide que ce soit sur le port série ou le mqtt mais le display est ok

Comment fait t’on ducoup pour stocker en buffer les données reçue de lora pour créer une nouvelle string ?

Si vous voulez etudier comment bien écouter le port série (ou gérer un flux asynchrone genre keypad) vous pouvez jeter un oeil à mon petit tuto sur le sujet

Merci bien je vais m'y plonger !

pourquoi tu veux absolument convertir la chaine reçu du Lora en autre chose?

client.publish(mqtt_topic,incoming,false);
Serial.print (incoming);

le code ci-dessus, donne quoi sur ton broker ?

Bonjour, J'ai essayer suivant ta proposition mais je n'arrive pas au compileur...
image

Je vais essayer d'utiliser la méthode de stockage proposée par Jackson et je reviens vers vous.
En tout cas merci pour le support. j'espère bien y arriver tout de même !

Oui, c'est ma faute j'ai écrit des conneries.
Il faut stocker les caractères que tu reçois dans la variable incoming un par un, pour avoir un tableau de caractère, attendus par la fonction publish.

Là je suis sûr mon téléphone, donc jene peut pas vraiment écrire ducode et le tester.
Si personne ne T'a proposer un bout de code d'ici là, j'essayerai de le faire ce soir :smiley:

Ne pas oublier de rajouter le à caractère nul à la fin du tableau

Bien compris, merci a vous deux, je vais essayer de faire ça ce soir et profiter du beau temps cet après midi pour prendre l'air :wink:

Oui ou plus simplement, initialiser le tableau/buffer, en mettant chaque élément à 0.
Mais il faut faire attention de ne pas dépasser la taille moins un.
Le plus simple reste d'envoyer un \n a là fin de la chaîne dans le module lora, lors de la réception tu sais que la chaîne est fini et tu peux placer un caractère nulle à la place du \n.
Mais comme le dit très justement @J-M-L , Il faut bien faire attention que la chaîne que tu donne à la fonction de publication soit terminé par un caractère de valeur nulle(0)