MQTT Callback Problem

Guten Abend zusammen,

ich verzweifel so langsam und hoffe ihr könnt Licht ins Dunkel bringen.

Ich bin gerade dabei eine Heizung zu automatisieren.

Aufbau:

Arduino Uno + EthernetShield → MQTT → FHEM–> Anzeige/Auswertung

Aktuell lese ich 14xDS18B20 Sensor mit dem Arduino Uno ein und schicke diese via MQTT an Fhem.

Funktioniert soweit.

Nun möchte ich noch ein 4 Kanal Relais benutzen, welches mir das Magentventil meiner Fussbodenheizung einschaltet. Dazu messe ich die Raumtemperatur und möchte den Sollwert über Fhem vorgeben.

Fhem schickt per MQTT den Soll-Wert an meinen Arduino. So war die Idee.

Dennoch funktioniert das Einschalten und Ausschalten nur unregelmäßig.

Meine Vermutung ist das die Callback Funktion nicht die Antwort von Fhem sieht, da der Arduino nur mit dem Auslesen der Sensoren beschäftigt ist.

Könnt ihr mir Vorschläge geben wie ich das Problem lösen kann? Client.loop sollte 1 mal in der Void aufgerufen werden.

Wie kann ich meinen Code optimieren da der interne Speicher bei 85% Auslastung liegt.

Würde gerne die Abfrage der Sensoren über eine If/While Schleife vornehmen und einfach ein Array durchlaufen. Würde mir das später dann anschauen, wie ich die Hex Werte der Sensoren vernünftig in ein Array bekomme.

Wichtig ist mir erstmal das meine Sensorabfrage als auch die Ansteuerung der Magnetventile zuverlässig funktioniert.

Zudem würde ich gerne die Abfrage der Sensoren auf ein Intervall von 30 Minuten ausweiten. Dennoch muss der Sollwert von FHEM jeder Zeit gelesen werden. Wie könnte ich das im Programm trennen?

Bin über jede Äußerung erfreut.

Anbei mein Quellcode:

#include <PubSubClient.h>
#include <SPI.h>
#include <Ethernet.h>
#include <OneWire.h> // http://www.arduino.cc/playground/Learning/OneWire
#include <DallasTemperature.h> // http://milesburton.com/index.php?title=Dallas_Temperature_Control_Library
 
#define ONE_WIRE_BUS 7
#define TEMPERATURE_PRECISION 12

OneWire oneWire(ONE_WIRE_BUS); // Einrichten des OneWire Bus um die Daten der Temperaturfühler abzurufen
DallasTemperature sensors(&oneWire); // Bindung der Sensoren an den OneWire Bus
 
DeviceAddress tempDeviceAddress; // Verzeichniss zum Speichern von Sensor Adressen

DeviceAddress tempSensor_1 = {0x28, 0xff, 0xf7, 0x56, 0xa1, 0x16, 0x03, 0x97};
DeviceAddress tempSensor_2 = {0x28, 0xff, 0xdb, 0x62, 0xa1, 0x16, 0x03, 0x08};
DeviceAddress tempSensor_3 = {0x28, 0x30, 0x35, 0x9d, 0x1e, 0x13, 0x01, 0x80};
DeviceAddress tempSensor_4 = {0x28, 0xaa, 0xc1, 0x95, 0x16, 0x13, 0x02, 0x9c};
DeviceAddress tempSensor_5 = {0x28, 0xaa, 0xe5, 0x91, 0x16, 0x13, 0x02, 0x75};
DeviceAddress tempSensor_6 = {0x28, 0x2b, 0x05, 0x81, 0x1e, 0x13, 0x01, 0x8d};
DeviceAddress tempSensor_7 = {0x28, 0x5b, 0x04, 0x95, 0x1e, 0x13, 0x01, 0xee};
DeviceAddress tempSensor_8 = {0x28, 0x2f, 0xa4, 0x8a, 0x1e, 0x13, 0x01, 0x3b};
DeviceAddress tempSensor_9 = {0x28, 0xff, 0xf2, 0x4b, 0xa1, 0x16, 0x03, 0xe0};
DeviceAddress tempSensor_10 = {0x28, 0xff, 0xf8, 0x62, 0xa1, 0x16, 0x03, 0xbe};
DeviceAddress tempSensor_11 = {0x28, 0xAA, 0xFC, 0x94, 0x16, 0x13, 0x02, 0x7B};
DeviceAddress tempSensor_12 = {0x28, 0xAA, 0xA7, 0x90, 0x16, 0x13, 0x02, 0x90};
DeviceAddress tempSensor_13 = {0x28, 0xAA, 0xEB, 0x94, 0x16, 0x13, 0x02, 0x56};
DeviceAddress tempSensor_14 = {0x28, 0xBF, 0xEB, 0x8D, 0x1E, 0x13, 0x01, 0x8A};


float tempC_1,tempC_2,tempC_3,tempC_4,tempC_5,tempC_6,tempC_7,tempC_8,tempC_9,tempC_10,tempC_11,tempC_12,tempC_13,tempC_14 =0;

byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(192, 168, 2, 135);
IPAddress server(192, 168, 2, 105);

int Offset = 1;

char tempBuffer[100];
char message_buff[100];
EthernetClient ethClient;
PubSubClient client(ethClient);

void callback(char* topic, byte* payload, unsigned int length) {
  float b = atof(payload);
  String msgString = String((char*)topic);
  if (msgString =="fhem/keller/temp_vorgabe/gaestezimmer") {
    if (tempC_12 <= (b - Offset)){
      digitalWrite(3, LOW);    
      client.publish("fhem/heizung/temp_vorgabe/gaestezimmer","ON");
     } 
  }
  if (msgString =="fhem/keller/temp_vorgabe/gaestezimmer") {
    if (tempC_12 > b){
      digitalWrite(3, HIGH);
      client.publish("fhem/heizung/temp_vorgabe/gaestezimmer","OFF");
      }
  }
  if (msgString =="fhem/keller/temp_vorgabe/gaeste_bad") {
    if (tempC_11 <= (b - Offset)){
      digitalWrite(4, LOW);    
      client.publish("fhem/heizung/temp_vorgabe/gaeste_bad","ON");
      } 
  }
  if (msgString =="fhem/keller/temp_vorgabe/gaeste_bad") {
    if (tempC_11 > b){
      digitalWrite(4, HIGH);
      client.publish("fhem/heizung/temp_vorgabe/gaeste_bad","OFF");
     }
  }
  if (msgString =="fhem/keller/temp_vorgabe/flur_keller") {
    if (tempC_14 <= (b - Offset)){
      digitalWrite(5, LOW);    
      client.publish("fhem/heizung/temp_vorgabe/flur_keller","ON");
     } 
  }
  if (msgString =="fhem/keller/temp_vorgabe/flur_keller") {
    if (tempC_14 > b){
      digitalWrite(5, HIGH);
      client.publish("fhem/heizung/temp_vorgabe/flur_keller","OFF");
      }
  }
  if (msgString =="fhem/keller/temp_vorgabe/spielezimmer") {
    if (tempC_13 <= (b - Offset)){
      digitalWrite(6, LOW);    
      client.publish("fhem/heizung/temp_vorgabe/spielezimmer","ON");
      }
  }
  if (msgString =="fhem/keller/temp_vorgabe/spielezimmer") {
    if (tempC_13 > b){
      digitalWrite(6, HIGH);
      client.publish("fhem/heizung/temp_vorgabe/spielezimmer","OFF");
     } 
  }
}


void reconnect() {
  while (!client.connected()) {
    if (client.connect("arduinoClient")) {
    } else {
      delay(5000);
    }
  }
}

void setup()
{
  Serial.begin(9600);
  client.setServer(server, 1883);
  client.setCallback(callback);
  Ethernet.begin(mac, ip);
  delay(500);
  pinMode(3, OUTPUT); 
  pinMode(4, OUTPUT); 
  pinMode(5, OUTPUT); 
  pinMode(6, OUTPUT); 
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(6, HIGH);
  client.subscribe("fhem/keller/temp_vorgabe/#");
}

void loop()
{  
  if (!client.connected()) {
    reconnect();
  }
     
   sensors.requestTemperatures();
   
   delay(300);
   tempC_1 = sensors.getTempC(tempSensor_1);
   client.publish("fhem/keller/FBH_Vorlauf", dtostrf(tempC_1, 1, 2, tempBuffer));
   delay(300);   
   tempC_2 = sensors.getTempC(tempSensor_2);
   client.publish("fhem/keller/FBH_Ruecklauf", dtostrf(tempC_2, 1, 2, tempBuffer));
   delay(300);   
   tempC_3 = sensors.getTempC(tempSensor_3);
   client.publish("fhem/keller/FBH_Gaeste_Ruecklauf", dtostrf(tempC_3, 1, 2, tempBuffer));
   delay(300);      
   tempC_4 = sensors.getTempC(tempSensor_4);
   client.publish("fhem/keller/FHB_Gaeste_Vorlauf", dtostrf(tempC_4, 1, 2, tempBuffer));
   delay(300);     
   tempC_5 = sensors.getTempC(tempSensor_5);
   client.publish("fhem/keller/FBH_Flur_Treppe_Ruecklauf", dtostrf(tempC_5, 1, 2, tempBuffer));
   delay(300);   
   tempC_6 = sensors.getTempC(tempSensor_6);
   client.publish("fhem/keller/FBH_Flur_Treppe_Vorlauf", dtostrf(tempC_6, 1, 2, tempBuffer));
   delay(300);      
   tempC_7 = sensors.getTempC(tempSensor_7);
   client.publish("fhem/keller/FBH_Gaeste_WC_Ruecklauf", dtostrf(tempC_7, 1, 2, tempBuffer));
   delay(300);      
   tempC_8 = sensors.getTempC(tempSensor_8);
   client.publish("fhem/keller/FBH_Gaeste_WC_Vorlauf", dtostrf(tempC_8, 1, 2, tempBuffer));
   delay(300);      
   tempC_9 = sensors.getTempC(tempSensor_9);
   client.publish("fhem/keller/FBH_Spielezimmer_Ruecklauf", dtostrf(tempC_9, 1, 2, tempBuffer));
   delay(300);      
   tempC_10 = sensors.getTempC(tempSensor_10);
   client.publish("fhem/keller/FBH_Spielezimmer_Vorlauf", dtostrf(tempC_10, 1, 2, tempBuffer));
   delay(300);    
   tempC_11 = sensors.getTempC(tempSensor_11);
   client.publish("fhem/keller/Raum_Temp_Gaeste_WC", dtostrf(tempC_11, 1, 2, tempBuffer));
   delay(300);      
   tempC_12 = sensors.getTempC(tempSensor_12);
   client.publish("fhem/keller/Raum_Temp_Gaeste", dtostrf(tempC_12, 1, 2, tempBuffer));
   delay(300);      
   tempC_13 = sensors.getTempC(tempSensor_13);
   client.publish("fhem/keller/Raum_Temp_Spielezimmer", dtostrf(tempC_13, 1, 2, tempBuffer));
   delay(300);      
   tempC_14 = sensors.getTempC(tempSensor_14);   
   client.publish("fhem/keller/Raum_Temp_Flur", dtostrf(tempC_14, 1, 2, tempBuffer));
   delay(300);   
   client.loop();
   delay(30000);
  //delay(1800000);
}

Grüße

Hi

Dein Arduino wartet eigentlich nur die ganze Zeit darauf, daß das aktuelle delay() endlich abgelaufen ist, damit Er wieder ein/zwei Happen tun kann.

Also von 'ist so beschäftigt mit den Sensoren' sehe ich Da nicht viel!

Auch kannst Du ALLE DS18B20 auf ein Mal beauftragen, eine neue Temperatur zu generieren. Dann solltest Du zumindest 750ms lang nicht mit den Sensoren schwätzen - bei parasitärer Versorgung eh nicht möglich, bei Normaler schon - bekommst halt keine aktuelle Temperatur. Denke, in Deinem Sketch wird mindestens der erste Sensor falsche Werte zurück geben, da ein delay(300); nun Mal keine 750ms überbrückt, Die auch der erste Sensor zum Wandeln braucht. Der 2.te könnte mit knapp über 600ms gerade so fertig geworden sein - das Datenblatt sagt was von 750ms - Die solltest Du einhalten, auch, wenn 600 schon Mal geklappt hat.

Du spielst viel mit Strings rum (mit großen S) - muß Das? Die Pfade sehen mir danach aus, daß FEHM Diese so gerne hätte - könnte FEHM diese Daten nicht für die einzelnen Sensoren generieren, damit der µC nur Das macht, wofür Er da ist? Also Sensorwerte liefern und nicht den ganzen Kram mit Bezeichner, Komma hier, Sternchen da, aufzubereiten? Das könnte auch eine PHP übernehmen - Die wäre auch rechenstärker, als der 'fast ausgelastete' Arduino - zumindest ist dort Speicherplatz kein Problem, wie beim Arduino.

Wie macht sich FEHM im Normalfall bemerkbar, also wie wird das Magnet-Ventil 'normal' geschaltet - also bei Anderen?

MfG

Hallo und vielen Dank postmaster-ino!

Das mit der 750ms Wartezeit hatte ich gar nicht mehr im Kopf.

Danke für den Hinweis.

Wenn du davon redest alle auf ein Mal abzufragen, verstehe ich das so, dass ich 14 Zeilen habe, wo ich direkt den Wert abfrage und kein client.publish habe`?

Oder redest du von einer anderen Funktion?

Bzgl deiner Frage ob FHEM das nicht könnte. Diese Frage verstehe ich noch nicht so ganz. Kann st du dies genauer erläutern?

Der Pi ist bei mir im Werkkeller. Der Arduino mit dem restlichen Aufbau im Fußbodenverteilerkasten. Dort befinden sich auch die Magnetventile.

Vorher wurden die Magnetventile über das Raumthermostat (Bimetall) in den jeweiligen Räumen ein und ausgeschaltet. Diese habe ich nun abgeklemmt und einen DS18B20 ins Gehäuse verbaut und lese den nun über die Installationsleitung aus. Anders ist das halt nicht umsetzbar, da ich kein BUS etc habe und ich kein WLAN mehr nutzen will.

Ich hoffe das ich nun über den Arduino und das 4 Kanal-Relais dann eine bessere Steuerung bekomme.

Zum Thema String, Komma, Stern etc, habe ich erstmal genutzt da es von der Callback Funktion so erwartet wird.

Hättest du eine andere Idee?

Vielen Dank,

Gruß

Hi

Du liest aktuell 14 DS18B20 ein, Die in der ganzen Bude verteilt sind? Wow, hätte hier mit Problemen gerechnet, da der 1-Wire-Bus doch nicht ganz so störungs-resistend ist - wenn's klappt, soll mir Das aber auch recht sein :) Ggf. solltest Du die erhaltenen Messwerte auf Sinnhaftigkeit überprüfen, wenn doch Mal Müll gelesen wird.

'Alle Sensoren gleichzeitig' - Du kannst, wenn Du keinen Sensor einzeln ansprechen willst, auch ein SkipRom senden, dadurch wird JEDER Sensor angesprochen. Sinnig ist Das aber nur, wenn Du 'generiere neue Temperatur' oder 'Genauigkeit auf 9 Bit' auslösen willst. Die eigentliche Temperatur musst Du wieder einzeln auslesen - das mit dem SkipRom klappt nur, wenn die Sensoren NICHT antworten - hier nach würden Diese 'durcheinander quasseln' und man könnte Nichts verstehen.

Dein Aufbau ist akut so, daß der Arduino die Temperatur-Werte per WLan an den RPi sendet? Wenn JA, dann kannst Du doch auf dem RPi auch eine PHP erstellen, Die Dir den String (ja, trotzdem ein böser String) "1:20.345" in den Aufruf umbaut, Den Du für FEHM brauchst. Dann ruft halt die PHP auf dem RPi die entsprechende Seite selber auf, Er bastelt die Temperatur in den entsprechenden String (die Sensoren sind durch 1...9,A...E gekennzeichnet ... oder so) und ruft damit die Seite (wohl auch eine PHP) von FEHM auf.

Deine 'Magnet-Ventile' dürften normale Heizungs-Stellantriebe sein - gibt's in 230VAC, bei Hausautomatisierungen gerne 24DDC. Darin enthalten ist eine Wachsblase, Die aufgeheizt wird - dadurch wird der Stift dann heraus gedrückt und deshalb dauert das Fahren damit auch immer so lange - der ganze Klumpen muß erst 'auf Temperatur' gebracht werden.

Wenn Dir die Störungen auf dem 1-Wire-Bus zu viel werden (hoffe ich jetzt nicht), kannst Du auch die Installations-Leitungen für einen robusteren Bus wie RS485 oder CAN benutzen - bräuchtest dann aber in jedem Thermostat einen eigenen µC. Wenn Du nur vereinzelt Störungen bekommst, lasse die Sensoren ruhig sekündlich messen (750ms bei 12 Bit, je Bit weniger halbiert sich die Zeit) - so kannst Du 'die guten Messungen' raus picken.

MfG

Hallo,

ich habe gestern 3h an dem Aufbau weiter gearbeitet und dabei auch verstanden, was du meintest mit "Lasse doch den PI (FHEM) die Arbeit machen".

Gestern hat es dann klick bei mir gemacht :). Daher habe ich mein Programm umgeändert.

Ich lese nun die 14x Sensoren ein, mit Einhaltung der 750ms.

Aktuell lese ich die Sensoren alle 5 Minuten aus bei 12Bit Auflösung. Werde das nach der Testphase auf alle 30 Minuten reduzieren.

FHEM liest die Temperaturen vom Arduino ein und übernimmt die Auswertung.

Je nach Auswertung sendet dann FHEM nur noch einschalten oder ausschalten an den Arduino und dieser schaltet das Magnetventil.

Habe nun nur noch 75% Speicherauslastung und das Schalten hat bis dato zuverlässig funktioniert.

Die Magnetventile laufen mit 230V AC. Werde den Aufbau nun erstmal 1-2 Wochen laufen lassen, um zu verstehen, wie robust dieser ist und wie die Steuerung funktioniert.

Die Sensoren sind aktuell noch nicht alle im Haus verteilt. Aktuell bin ich nur im Keller tätig. Wir haben in jeder Etage einen Heizungsverteiler für die FBH.

Das Auslesen klappt bis jetzt. Die längste Leitung im Keller zum Sensor beträgt ca 6m. Habe hier über 4 Tage, die gemessenen Werte mit meinem lokalen Aufbau im jeweiligen Raum verglichen und da waren keine Abweichungen. Hatte erst befürchtet das ggf. das Gehäuse wo der Sensor eingebaut wurde, eventuell eine Veränderung der Temperatur herbeiruft von ca 0.5°C bis 1°C. Aber dies war gott sei dank nicht der Fall.

Würde diesen Aufbau dann für die anderen Etagen übernehmen und dort im jeweiligen Verteilerkasten verstauen.

Die max. Kabellänge bleibt bei ca 6m in den anderen Etagen.

Was ich dann noch optimieren muss, ist eine vernünftige Hysterese, damit ich eine gute Steuerung bekomme. (passende Ein und Ausschalt-Kurven)

Um nochmal auf die Callback Funktion zurückzukommen. Wird diese zu jeder Zeit im Programm aufgerufen oder bekommt der Arduino das nur mit wenn er die client.loop() Funktion aufruft? Das ist mir noch nicht klar.

Ist aber wichtig für mich dies zu Verstehen, denn wenn ich eine delay verwende, würde das ja bedeuten das in dieser Zeit der Arduino keine Topics empfängt.

Viele Grüße,