MQTT Werte wieder richtig zuordnen

Hallo
Ich habe mit Hilfe vom IOBroker mir ein kleines Netz aufzubauen können.
Wo die ganzen Daten gesammelt und verarbeitet werden so weit so gut.

Jetzt hab ich schon ein paar Beispiele getestet mit der ArduinoMqttClient Bibliothek wo ich auch ausgewählte werte zurück bekomme und weiter verarbeiten kann.

Wo ich aber den Wald vor lauter bäumen nicht mehr sehe ist,

Wie kann ich sagen wir mal 5-10 werte empfangen und diese auch richtig zuordnen ?
Mit dem WiFiEcho.ino Beispiel geht das mit einen wert schon mal aber ich verstehe nicht wie die Sortierung erfolgen könnte.

unterschiedliche werte abzufragen geht ja mit mqttClient.subscribe(Name); aber wie Zuordnen welcher wert jetzt grade gesendet wird?

Wäre Super wenn mich da jemand erleuchten könnte.
Vielen dank.

Schau mal hier: https://www.baldengineer.com/multiple-mqtt-topics-pubsubclient.html

Das stimmt so nicht. Damit abonnierst du nur ein Topic beim Broker.

Du erhältst aber das Topic über die callback Funktion und könntest anhand daran deine Werte verteilen.

Einfacher aber anders geht das natürlich über JSON.

Also danke euch beiden mit dem link kahm ich schon sehr viel weiter.
Bin aber gleich zum nächsten Problem gekommen.

PubSubClient.h eignet sich wohl gut zum empfangen aber kaum zum senden.
Int und float werte senden ist wohl nicht so ohne weiteres möglich.

ArduinoMqttClient.h Eignet sich gut zum senden von beliebigen Dabei typen aber kaum zum empfangen von werten.

Habe beide versucht mehrfach und unterschiedlich zusammen zulegen aber die wollen wohl nicht zusammen arbeiten.

const char ssid[] = "Wlanname";      
const char pass[] = "123456789";  
const char broker[] = "192.168.2.1xx";
int        port     = 1883;
const char topic1[]  = "AmpereSV";
const char topic2[]  = "Wattverbrauch";
float Watt;
float Ampere;
int count = 0;
#include <PubSubClient.h>
#include <ArduinoMqttClient.h>
#include <ESP8266WiFi.h>

WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
PubSubClient client(wifiClient);

void setup() {
  Serial.begin(115200);
  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1500);
  }
  mqttClient.setId("Jetzt_Aber");

  WiFiClient client;
  if (!mqttClient.connect(broker, port)) {
    while (1);
  }
  client.setCallback(callback);
 client.subscribe("Wattverbrauch");
  client.subscribe("AmpereSV");
}

void loop() {
  //delay(1000);
  mqttClient.poll();
  mqttClient.beginMessage("TestBeides");
  mqttClient.print(count); // Int oder Float
  mqttClient.endMessage();
  count++;
}

void callback(char *topic, byte *payload, unsigned int length) {
  String strTopic;
  strTopic = String((char*)topic);
  if (strTopic  == "Wattverbrauch") {
    char buffer[2];
    memcpy(buffer, payload, length); 
    buffer[length] = '\0'; 
    Watt = String((char*)payload).toFloat();
    Serial.print("Wattverbrauch: ");
    Serial.println(Watt);
  }

  strTopic = String((char*)topic);
  if (strTopic  == "AmpereSV") {
    char buffer[2];
    memcpy(buffer, payload, length); 
    buffer[length] = '\0'; 
    Ampere = String((char*)payload).toFloat();
    Serial.print("AmpereSV: ");
    Serial.println(Ampere);
  }
}

Kann noch mal jemand weiter helfen wie ich senden UND empfangen unter einen Hut bekomme?
Vielen dank.

Payload von MQTT ist immer eine Zeichenkette.

Du kannst am Empfänger auch Zeichenketten in andere Datentypen wandeln.

JSON unterstützt dies automatisch.

Vielleicht zeigst du dazu ein kurzes Beispiel.
Ich habe keine Probleme beim Senden und Empfangen.

Ok Danke für den Denkanstoß.

Setzte mich heute Abend noch mal dran.
Zurzeit weiß ich nur das ich mit ArduinoMqttClient.h ganz normal int und float senden kann ähnlich wie bei Serial auch.

 mqttClient.beginMessage("TestBeides");
 mqttClient.print(intwert); 
 mqttClient.endMessage();

Das Läuft fehlerfrei seit Wochen.

Wenn ich aber mit PubSubClient.h einen int oder float wert senden will bekomme ich nur Die Meldung das ich es umwandeln soll weil Int /Float nicht zulässig sind.

 client.publish("TestBeides","intwert");

Das geht nicht.

Irgendwo hänge ich in Gedanken noch fest.

Mache dir ein Paylaod als CharArray und nutze sprintf um deine Werte ins Payload zu bekommen.
ArduinoMQTTclient scheint das automatisch zu machen. Nutze ich aber nicht.

Ok Danke
Ich hab das mal versucht um zusetzen und hoffe du meinst das so.
Aber so recht läuft das immer noch nicht.
Mit

char string_zu_senden[2];

string_zu_senden[0] = intwert;
string_zu_senden[1] = '\0';
client.publish("TestBeides",string_zu_senden);

Kann ich es hochladen, übertragen wird aber nur Daten Müll.

und mit
client.sprintf("TestBeides",string_zu_senden);
Bekomme ich nur:
class PubSubClient' has no member named 'sprintf

Und mit :
client.sprintf ("TestBeides",(char)intwert);
invalid conversion from 'char' to 'const char*' [-fpermissive]

Ich glaube das wird heute nichts mehr.

Nein, das hast du falsch verstanden.

ist der Integer nur einstellig?

sprintf ist kein Bestandteil einer MQTT Bibliothek.
Es ist Bestandteil von C++. Es "kopiert" Werte in ein CharArray.

Du legst dir ein CharArray an, was so groß ist, das es alle Zeichen der Zeichenkette aufnehmen kann, +1.

char payload[51] = ""; kann 50 Zeichen aufnehmen. Ob 50 ausreicht, musst du wissen. (Man könnte dies auch dynamisch berechnen. Da du es aber auch nur als Buffer verwenden kannst, verwirfst du es anschließend einfach wieder, und der Speicher ist frei.)

Beispiel:

#include "secret.h"               // includes SSID, PW, etc.
#include <ESP8266WiFi.h>                
#include <PubSubClient.h>               

WiFiClient espClient;
PubSubClient client(mqtt_server,port,0,espClient);

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  client.connect(clientName);
}

void loop() {
  while (WiFi.status() != WL_CONNECTED) {
    Serial.println("Wifi connection failed");
    delay(5000);
  }
  if (!client.connected()){                 
    Serial.println("Connect to MQTT");
    delay(5000);
    client.connect(clientName);
  }
  client.loop();
  delay(1000);                              // einfaches blockierendes Delay für sendeintervall
  send_int(random(0,100));
}

void send_int(int x){
  char payload[51] = "";
  sprintf(payload,"%d",x);
  client.publish("TestTopic", payload);
}

1 Like

Vielen dank für die tolle Hilfe.
Habe wieder was gelernt.

Zb den Befehl der zufallszahlen kannte ich noch gar nicht hatte das immer mit Analog Read gemacht.

Aber mal für mich und auch für andere wie man senden UND empfangen kann.

#include <PubSubClient.h>
#include <ESP8266WiFi.h>

const char ssid[] = "xxxxxxxxx";       
const char pass[] = "xxxxxxx";   

float wert = -0.1;
int wert2 = 10;
char payload[5] = "";
const char broker[] = "192.168.2.117";
int port = 1883;
float WertA;
float WertB;

WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
  Serial.begin(115200);
  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1500);
  }
  client.setServer(broker , port);
  client.setCallback(callback);
  while (!client.connected()) {
    if (client.connect("Test_Wlan")) {// MQTT Name für den Broker
      Serial.println("broker connected");
    } else {
       Serial.print("Keine Verbindung");
      Serial.print(client.state());
      delay(2000);
    }
  }
  client.subscribe("Wattverbrauch");
  client.subscribe("AmpereSV");
  Serial.println("Neu Start");
}
void loop() {
  client.loop();
  delay(2000); //soll hier zum Test nur den Controller ausbremsen.

  //Float Werte
  wert--;
  payload [5]= NULL;
  dtostrf(wert, 5, 1,payload); // der float Wert mit 5 Stellen mit Punkt / davon 1 hinter dem komma
  client.publish("TestBeides", payload);
 
 // und hier nur Int werte
 
  wert2++;
  payload [5]= NULL;
  sprintf(payload, "%d", wert2);// Nur Int werte
  client.publish("TestBeides2", payload);
}

void callback(char *topic, byte *payload, unsigned int length) { 
  String strTopic;
  strTopic = String((char*)topic);
  if (strTopic  == "Wattverbrauch") {
    char buffer[2];
    memcpy(buffer, payload, length); 
    buffer[length] = '\0'; 
    WertA = String((char*)payload).toFloat();
    Serial.print("Wattverbrauch: ");
    Serial.println(WertA);
  }

  strTopic = String((char*)topic);
  if (strTopic  == "AmpereSV") {
    char buffer[2];
    memcpy(buffer, payload, length);  
    buffer[length] = '\0';  
    WertB = String((char*)payload).toFloat();
    Serial.print("AmpereSV: ");
    Serial.println(WertB);
  }
}

Hat Platz für 4 Zeichen. Plus ein Endzeichen

Passt nicht zusammen mit

sprintf kann auch Float.

void send_float(float x){
  char payload[51] = "";
  sprintf(payload,"%.1f",x);
  client.publish("TestTopic", payload);
}

Wenn du dein Payload nicht global, sondern lokal hältst, ist es relativ egal wie groß Payload ist, da es beim verlassen der Funktion wieder frei gegeben wird.

Danke für die hinweise.
Ja habe mich wohl verschreiben hast voll und ganz recht.

sprintf konnte ich leider kein float entlocken es wurde immer nur 0 übertragen.
Nach einiger suche fand ich einen Beitrag in dem stand das sprintf kein float könne und es dafür dtostrf gebe welches aber wohl mehr Speicherplatz braucht.
Was mir beim DUE (Mein eigentliches Ziel) aber egal ist.

Und Ja ich habe in MEIN fall mal auf global verzichtet.
weil ich für mein eigentliches Projekt vermeiden will für die ganzen Übertragungen je eine Funktion zuschreiben. (sind fast 80 werte die insgesamt nach und nach übertragen werden)
Damit spielt die Größe der Payload zwar eine rolle aber die ist ja nebensächlich weil sie eh immer gelöscht wird.

Auf dem ESP8266/ESP32 kann sprintf mit float umgehen.
Nur auf den kleinen AVR ist das im Default nicht eingebaut.

Gruß Tommy

1 Like

Da WLAN genutzt wird, ging ich von einem ESP aus.

Im gezeigten Beispiel von dir nicht.

Gerade dafür bieten sich doch Funktionen an.
Spart ne Menge an Code

Nicht für jede Übertragung eine Funktion, sondern wenige Funktionen gemeinsam für alle Übertragungen.
Das erfordert halt eine grundlegende Planung des Vorgehens.

Gruß Tommy

Ich dachte hätte ich mit:

Gut lösen können.

Wie sollte das noch einfacher gehen? sind 3 Zeilen für eine Übertragung.
Bin für bessere Vorschläge offen.

Ich hatte den versuch jetzt nur mit wlan gemacht weil grade ein ESP8266 hier lag.
Aber ob Lan oder Wlan sollte keinen unterschied machen. Der Due ist ja auch ein 32-bit Chip.

Schau Dir mal diese Vorgehensweise an. Ob der Due mit dem F-Macro was bringt, habe ich nicht getestet.
Eine Senderoutine für float, eine für int (wenn Du nur float hast, kannst Du die für int weg lassen. Die Sachen fürs F-Macro kannst Du ja mal für den due kompilieren, ob es was bringt.

Gruß Tommy

Dies in eine Funktion packen. Dann hast du einmal diese 3 Zeilen. Und pro Übertragung nur eine Zeile. Hinzu kommen bessere Anpassbarkeit und weniger Fehler trächtig.

Vielen dank für Euere Hilfe.
Zur zeit habe ich im Due noch ca 50-60% Platz Frei es besteht also aktuell noch keine Notwendigkeit platz um biegen und brechen zu sparen das kommt wie üblich nach und nach.
Dazu kommt das ich da noch nicht ganz Durch blicke.

Wie dem auch sei.

Leider scheine ich auf dem schlauch zu stehen oder ich habe mich etwas unklar ausgedruckt.

Plumps ich weis auf was du hinaus willst:

Springe zu dempunkt ();

void dempunkt ()
{
sende die selbe Übertragung....
}

Da es aber unterschiedliche werte zu unterschiedlichen Themen sind musste ich ja dennoch für jede Übertragung einen eigene sende Routine schreiben und verlinken das ist doch eher mehr aufwand.
Als es gleich an dem punkt zu machen wo es gesendet werden soll.

Was sich immer ändert sind die beiden punkte:

  dtostrf(wert, 5, 1,payload); //  Wert konvertierten und neu speichern
  client.publish("TestBeides", payload); //Absenden

Ich könnte jetzt rein fürs löschen eine verlinkung schreiben.

loeschen ();

void loeschen ()
{
payload [5]= NULL;  //Speicher löschen
}

Muss ich mal Testen ob mir das platzmäßig auf Dauer was bringt.

Du hast den Sinn von Funktionen nicht verstanden. Funktionen kann man auch Parameter übergeben, z.B. den Float-Wert und den Text.
Das wird alles in dem von mir in #17 verlinkten Beispiel gezeigt, das Du Dir augenscheinlich nicht angeschaut hast.
Wenn Du Dir also das Leben schwer machen willst, dann mache weiter so. Es ist Deine Entscheidung.

Gruß Tommy