Heizungssteuerung & "Datenprobleme" 433Mhz

Hallo!

Ich habe eine Wohnung mit Nachtspeicherheizungen. Die Ladesteuerung dieser Heizungen möchte ich mittels Arduinos (1 Stück / Heizung) "automatisieren".
Ich will einfach einen Abgleich von Aussentemperatur zu Heizungstemperatur durchführen.
Als Temp-Sensoren nutze ich DS18B20-Modelle.
Dafür habe ich einen Funkt-Aussentemperatur-Sender mit folgendem Code gebaut:

#include <VirtualWire.h>
#include <OneWire.h>

#include <DallasTemperature.h>

#define ONE_WIRE_BUS 7

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

char msg[6];

void setup() {

sensors.begin();

vw_setup(2000);

vw_set_tx_pin(3);

}

void loop() {

sensors.requestTemperatures();

float temperature = sensors.getTempCByIndex(0);

dtostrf(temperature, 6, 2, msg);

vw_send((uint8_t *)msg, strlen(msg));

vw_wait_tx();

delay(200);

}

Und jetzt die Baustelle:

Am Empfänger habe ich ein Display angeschlossen.
Der Aussentemperaturwert soll in die Variable "aussentemp" geschrieben werden -> Dabei entseht aber nur "Datenmüll"!

Der untere Bereich mit den ganzen Schleifen dient dazu, bei sinkender Aussentemperatur die Heizung stärker aufladen zu lassen. Das funktioniert auch, wenn ich die Aussentemperatur manuell vorgebe und die Innentemperatur "mittels lötkolben" messe :wink:
Eine Anzeige der Aussentemperatur auf dem LCD-Display funktioniert auch (auch im negativen Bereich der Kühltruhe)!
Wenn ich aber die Temperatur vom Funkempfänger in die Variable "aussentemp" schiebe, kommt nur Müll raus!

Kann mir dabei jemand helfen, wie ich das hin bekomme?

DANKE!

/*

ALLES FUNKTIONIERT MIT EINER AUSNAHME
Der Aussentemperaturwert wird FALSCH in den Wert "aussentemp" übertragen!

Übergangsweise ist aussentemp auf 12 gesetzt zum Test der Schleifenwerte!
*/

#include <LiquidCrystal.h>

#include <VirtualWire.h>

#include <OneWire.h>

#include <DallasTemperature.h>

#define ONE_WIRE_BUS 3

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

int i;

int heizungtemp; // Temperatur in C der Heizung selber über internen Fühler

int aussentemp; //Temperatur in C von draussen per Funksender

int heizungsoll; // Solltemperatur der Heizung

int ladungein = 11; // Kontakt-Pin, an den Last Lastrelais angeschlossn wird

LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Ansteuerkontakte für LCD-Display MIT TASTEN!!

void setup(){

pinMode (ladungein, OUTPUT); // Deklarierung des Ansteuerkontaktes für Laderelais als Ausgang
sensors.begin();

Serial.begin(9600);

lcd.begin(16, 2);

vw_setup(2000);

vw_rx_start();

vw_set_rx_pin(2); //Zuweisung Anschlusspin 2 Funkempfänger

}

void loop(){
digitalWrite(ladungein, LOW);
lcd.setCursor(0, 1); // Displayanzeige Innentemperatur der Heizung

lcd.print("Indoor: ");

sensors.requestTemperatures();

heizungtemp = sensors.getTempCByIndex(0);

//lcd.print(sensors.getTempCByIndex(0)); // DisplayAusgabe der Heizungstemperaur über direkte Messung vom Sensor zum Display
lcd.print(heizungtemp); // DisplayAusgabe über variable "heizungstemp" zum Display
lcd.setCursor(14, 1);

lcd.print((char)223);

lcd.print("C");
//Serial.println("heizungtemp:");
//Serial.println(sensors.getTempCByIndex(0));

uint8_t buf[VW_MAX_MESSAGE_LEN];

uint8_t buflen = VW_MAX_MESSAGE_LEN;

if( vw_get_message(buf, &buflen) )

{

lcd.setCursor(0, 0);

lcd.print("Outdoor:");

for (i = 0; i < buflen; i++)

{

//aussentemp = buf*; // Hier entsteht der Datenmüll[/b]*
lcd.write(buf*);*
}
lcd.setCursor(14, 0);
lcd.print((char)223);
lcd.print("C");
}
aussentemp = 13.8; //Nur zu Testzwecken manuell vorgegeben!
if (aussentemp < 16.5 )
{

* heizungsoll < 24;*

}
if (aussentemp < 14.5 )
{

* heizungsoll = 28;*

}
if (aussentemp < 12.5 )
{

* heizungsoll = 32;*

}
if (aussentemp < 10.5 )
{

* heizungsoll = 36;*

}
if (aussentemp < 8.5 )
{

* heizungsoll = 40;*

}
if (aussentemp < 6.5 )
{

* heizungsoll = 44;*

}
if (aussentemp < 4.5 )
{

* heizungsoll = 48;*

}
if (aussentemp < 2.5 )
{

* heizungsoll = 52;*

}
if (aussentemp < 0.5 )
{

* heizungsoll = 56;*

}
if (aussentemp < -1.5 )
{

* heizungsoll = 60;*

}
if (aussentemp < -3.5 )
{

* heizungsoll = 64;*

}
if (aussentemp < -5.5 )
{

* heizungsoll = 68;*

}
if (aussentemp < -7.5 )
{

* heizungsoll = 72;*

}
if (aussentemp < -9.5 )
{

* heizungsoll = 76;*

}
if (aussentemp < -11.5 )
{

* heizungsoll = 80;*

}
if (aussentemp < -13.5 )
{

* heizungsoll = 84;*

}
if (aussentemp < -15.5 )
{

* heizungsoll = 88;*

}
if (aussentemp < -17.5 )
{

* heizungsoll = 92;*

}
if (aussentemp > 16.5 )
{

* heizungsoll = 0;*

}

if (heizungtemp < heizungsoll)
{

* digitalWrite(ladungein, HIGH);*
* Serial.println("Ladung EIN");*
}
if (heizungtemp > heizungsoll)
{

* digitalWrite(ladungein, LOW);*
* Serial.println("Ladung AUS");*
}
Serial.println("heizungtemp:"); //Displayausgabe der Heizungstemperatur, Heizungssoll-Temperatur & Außentemperatur
Serial.println(heizungtemp);
Serial.println("heizungsoll:");
Serial.println(heizungsoll);
Serial.println("aussentemp:");
Serial.println(aussentemp);
Serial.println("------------");
delay(5000); // Verlangsamte Messung im 5-Sekunden Takt
}
[/quote]

Jensii:

//aussentemp = buf;  // Hier entsteht der Datenmüll

lcd.write(buf);

Da sollte in Deinem Code ja wohl auch was anderes stehen:

lcd.write(buf[i]);

Kleine Ursache, große Auswirkung.

Im übrigen bist Du heute bereits der Zweite, der von einem Temperatursensor, der intern eine Temperatur als 12-Bit Wert liefert, was locker in eine 16-Bit 'int' Variable passen würde, erst mit Hilfe einer Library einen 'float' Wert macht und dadurch auf 32-Bits aufbläht, und der dann aus dem 'float' nochmal einen menschenlesbaren Roman machen möchte, bevor dieser per Funk verschickt wird.

Die von Dir festgelegte "Romanlänge" ist mit 6 Bytes = 48 Bits übrigens auch noch zu kurz gegriffen, wenn die Temperaturen unterhalb -10°C fallen:

char msg[6];

Unterhalb -10.00 Grad brauchst Du mit zwei Nachkommastellen 7 Zeichen für einen String:
1 Zeichen für das Vorzeichen, 2 Zeichen für die Stellen vor dem Punkt, 1 Zeichen für den Punkt, 2 Zeichen für die beiden Nachkommastellen plus ein abschließende Nullzeichen '\0' im String = summa summarum 7 Zeichen. Also lieber entweder nur eine Nachkommastelle bei der Temperatur senden. Oder:

char msg[7];

Es wäre natürlich voll Panne, wenn Deine Heizungsanwendung gerade im strengsten Winter bei Temperaturen unterhalb -10.00 Grad (und nur dann) einen "Buffer Overflow" mit Fehlfunktion produzieren würde und dann nicht mehr das macht, was sie machen soll.

Aber so programmierst Du eben: Offensiv fehlerträchtig mit Ausfall der Anwendung in bestimmten Temperaturbereichen, insbesondere unterhalb -10.00 Grad Celsius.

Aber das was Du da zusammenprogrammierst ist sowieso alles viel zu komplizierter Hokuspokus, was Du da veranstalstest, vor allem, wenn Dich auf der Empfängerseite nicht der übertragene Romantext an sich interessiert, sondern wenn Du den Wert wieder als Variablenwert in numerischer Form vorliegen haben möchtest.

Vorschlag für die Datenübertragung: Wandele die Temperatur in einen 'Zehntelgradwert' um:

int tempZehntel = round(10*sensors.getTempCByIndex(0));

Dann überträgst Du den int-Wert 'tempZehntel' in Form von 2 Bytes, den Du auch auf der Empfängerseite wieder als 2-Byte 'int' Wert ausliest. Und dann hast Du auch gleich einen Wert zum Vergleichen in der if-Abfrageorgie. Und wenn Du auf der Empfängerseite dann doch lieber 'float' bevorzugst, dann teilst Du den übertragenen Zehntelgradwert eben wieder durch 10.0.

Ich persönlich würde Temperaturen jedenfalls nicht per Funk als Strings mit wechselnder Länge übertragen, sondern stets als binäre numerische Variable mit fester Bitbreite der Variablen.

Wenn es unbedingt Floats sein sollen kann man die 4 Bytes eines Floats so wie sie sind übertragen. Mit etwas Zeiger-Trickserie und Casts kein Problem. Die Adresse eines Wertes oder eine Array Variable kann man direkt auf byte* casten.

Weshalb da sehr viele Leute bei C Strings landen liegt wahrscheinlich an den Beispielen in der Library. Hier wäre es vom Autor vielleicht auch zu Überlegen gewesen nicht einen byte* sondern einen void* als Parameter zu nehmen. Dann wäre deutlich, dass der Datentyp egal ist, solange die Anzahl der Bytes passt.

Hallo!
Danke erst mal für eure Antworten und Hinweise!
Zu dem "Grunprogrammaufbau ala Roman" -> Die Temperaturauslesung habe ich "einfach kopiert"!

Quelle: http://www.instructables.com/id/Wireless-indoor-outdoor-thermometer/

Ich konnte als LAIE bei dem Code ungefähr verstehen was abläuft, und er funktioniert im Grunde problemfrei! Selbst -20 Grad in meiner Kühltruhe werden PROBLEMFREI auf das Display gesendet!

Da sollte in Deinem Code ja wohl auch was anderes stehen:
Code: [Select]

lcd.write(buf*);*
Kleine Ursache, große Auswirkung.[/quote]
Ja, danke auch dafür - Leider konnte es nicht helfen.
Es kommt mir auch NICHT auf irgendwelche Fehler im LCD-Bereich an-> Dies ist nur für "Testzwecke" angeschlossen und wird nachher nicht verwendet. Der übersicht halber wird der entsprechende Programmode dann auch gelöscht.
Da ich dieses GROßE FORUM hier nicht sofort überblicke, könnte mir ein Link zu dem (heutigen) Thema mit gleicher / ähnlicher Problematik ja bestiimt weiter helfen....
Ich freue mich über jede weitere Antwort!

Jensii:
Zu dem "Grunprogrammaufbau ala Roman" -> Die Temperaturauslesung habe ich "einfach kopiert"!

Quelle: http://www.instructables.com/id/Wireless-indoor-outdoor-thermometer/

Es ist kein Qualitätskriterium, dass irgendwas irgendwo im Internet steht.
Auch bei Arduino Sketch-Code nicht.

Jensii:
Ich konnte als LAIE bei dem Code ungefähr verstehen was abläuft, und er funktioniert im Grunde problemfrei! Selbst -20 Grad in meiner Kühltruhe werden PROBLEMFREI auf das Display gesendet!

Ob ein Buffer Overflow zu Problemen führt oder nicht, hängt auch davon ab, welche Variablen als nächstes im RAM-Speicher hinter der Variablen stehen, die der Buffer Overflow betrifft. Mit ein wenig Glück passiert gar nichts. Mit etwas Pech bricht das ganze Programm zusammen.

Hast Du dabei den originalen Code mit dem Empfänger an Pin-11 getestet?

In dem Fall würde mich das nicht wundern:

  vw_rx_start();
  vw_set_rx_pin(11);

Pin-11 ist beim UNO wohl der Default Pin beim Empfangen. Da macht es natürlich nichts, den Befehl an falscher Stelle zu geben, weil 11 sowieso zum Empfangen gesetzt ist.

Aber wenn Du den Pin anders setzen möchtest, würde ich schon die Reihenfolge so wählen:

  1. Erst den Pin setzen
  2. Dann den Empfang starten

Also für mich klingt es so herum plausibler, wenn Du einen anderen RX-Pin verwenden möchtest:

vw_set_rx_pin(2);  //Zuweisung Anschlusspin 2 Funkempfänger
vw_rx_start();

Hallo!
Das mit dem "Defaultpin" kann ich nicht genau sagen, da ich aber wie schon geschrieben hatte, keine Empfangsprobleme (trotz "ungünstiger" Reihenfolge von vw_set_rx_pin(2); und vw_rx_start();).
Das habe ich einfach trotzdem angepasst!

Aber alles in allem habe ich eine Lösung gefunden:

/*
ALLES FUNKTIONSTÜCHTIG


*/


#include <LiquidCrystal.h>
#include <VirtualWire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 3

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

int i;

int heizungtemp; // Temperatur in C der Heizung selber über internen Fühler
int aussentemp;  //Temperatur in C von draussen per Funksender
int heizungsoll; // Solltemperatur der Heizung
int ladungein = 11; // Kontakt-Pin, an den Last Lastrelais angeschlossn wird
char Sensor1CharMsg[4]; //// Char für den Funk-Container (Empfangsseite)

LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Ansteuerkontakte für LCD-Display MIT TASTEN!!

void setup(){

  pinMode (ladungein, OUTPUT);  // Deklarierung des Ansteuerkontaktes für Laderelais als Ausgang
  sensors.begin();
  Serial.begin(9600);
  lcd.begin(16, 2);

  vw_setup(2000);
  vw_set_rx_pin(2);  //Zuweisung Anschlusspin 2 Funkempfänger 
  vw_rx_start();
  

}

void loop(){
  
  lcd.setCursor(0, 1);  // Displayanzeige Innentemperatur der Heizung
  lcd.print("Indoor: ");
  sensors.requestTemperatures();
  heizungtemp = sensors.getTempCByIndex(0);

  //lcd.print(sensors.getTempCByIndex(0));  // DisplayAusgabe der Heizungstemperaur über direkte Messung vom Sensor zum Display
  lcd.print(heizungtemp);                  // DisplayAusgabe über variable "heizungstemp" zum Display 
  lcd.setCursor(14, 1); 
  lcd.print((char)223);
  lcd.print("C");


  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;


if (vw_get_message(buf, &buflen)) 
    {
      int i;         // Message with a good checksum received, dump it. 
      for (i = 0; i < buflen; i++)
    {            
          Sensor1CharMsg[i] = char(buf[i]);  //Füllt den Sensor1Char mit dem Puffer-Wert des Funkmoduls
    }
        
       Sensor1CharMsg[buflen] = '\0';   //Wird benötigt, wenn Empfangene Daten kürzer als vorherige sind
       aussentemp = atoi(Sensor1CharMsg);  // GANZ WICHTIG! Konvertiert den Sensor1-Array in INTEGER-Zahl der Variable "aussentemp"
        
    }


/*

if( vw_get_message(buf, &buflen) )

{

  lcd.setCursor(0, 0);
  lcd.print("Outdoor:");

  for (i = 0; i < buflen; i++)

  {

    aussentemp = buf[i];
    lcd.write(buf[i]);
    Serial.write(buf[i]);
    
  }

  lcd.setCursor(14, 0);
  lcd.print((char)223);
  //Serial.println((char)223);
  //aussentemp = (char)223;
  lcd.print("C");

} */

//aussentemp = 12.0;
if (aussentemp < 16.5 )
{ 
    heizungsoll = 24;
}

if (aussentemp < 14.5 )
{ 
  heizungsoll = 28;
}

if (aussentemp < 12.5 )
{  
  heizungsoll = 32;
}

if (aussentemp < 10.5 )
{ 
  heizungsoll = 36;
}

if (aussentemp < 8.5 )
{ 
  heizungsoll = 40;
}

if (aussentemp < 6.5 )
{ 
  heizungsoll = 44;
}

if (aussentemp < 4.5 )
{ 
  heizungsoll = 48;
}

if (aussentemp < 2.5 )
{ 
  heizungsoll = 52;
}

if (aussentemp < 0.5 )
{ 
  heizungsoll = 56;
}

if (aussentemp < -1.5 )
{ 
  heizungsoll = 60;
}

if (aussentemp < -3.5 )
{ 
  heizungsoll = 64;
}

if (aussentemp < -5.5 )
{ 
  heizungsoll = 68;
}

if (aussentemp < -7.5 )
{ 
  heizungsoll = 72;
}

if (aussentemp < -9.5 )
{ 
  heizungsoll = 76;
}

if (aussentemp < -11.5 )
{ 
  heizungsoll = 80;
}

if (aussentemp < -13.5 )
{ 
  heizungsoll = 84;
}

if (aussentemp < -15.5 )
{ 
  heizungsoll = 88;
}

if (aussentemp < -17.5 )
{   
  heizungsoll = 92;
}

if (aussentemp > 16.5 )
{ 
  heizungsoll = 0;
}
 

if (heizungtemp < heizungsoll)
{  
  digitalWrite(ladungein, HIGH);
  Serial.println("Ladung EIN");
}


if (heizungtemp > heizungsoll)
{
  digitalWrite(ladungein, LOW);
  Serial.println("Ladung AUS");
}

 Serial.println("------------");
 Serial.println("heizungtemp:");              //Displayausgabe der Heizungstemperatur, Heizungssoll-Temperatur & Außentemperatur
 Serial.println(heizungtemp);
 Serial.println("heizungsoll:");
 Serial.println(heizungsoll);
 Serial.println("aussentemp:");
 Serial.println(aussentemp);

 Serial.println("------------");


 delay(5000);  // Verlangsamte Messung im 5-Sekunden Takt


}
Dieser Code kann alles was ich benötige, wie weit ein Bufferoverflow etc. stattfinden werden Langzeittests bis zum Herbst zeigen!
DANKE für alle Hilfe und Tipps!

Jensii:
Dieser Code kann alles was ich benötige

Falls Du mit Deinem Code etwas schalten möchtest, achte bei Verwendung mechanischer Relais auf die Schalthäufigkeit!

Sensoren können "springende Temperaturen" liefern, z.B. immer abwechselnd 6.4 und 6.5, oder dreimal 6.4 und dann einmal 6.5.

Da mußt Du an Deinen Umschaltpunkten aufpassen, dass Du Dir Dein Schaltrelais/Schaltschütz nicht aufgrund der Schalthäufigkeit in kürzester Zeit durch laufendes ein-/ausschalten kaputtschaltest.

Normalerweise gehört da eine Schalthysterese in der Programmlogik gesetzt, entweder beim Schalten oder bei der Temperaturerfassung.

Siehe: Hysterese – Wikipedia

Und: Die tiefste Temperatur eines Tages wird meistens zur Zeit des Sonnenaufgangs erreicht, also im Winter in Deutschland erst gegen 8:00 Uhr morgens. Je nach Stromtarif wäre es aber wahrscheinlich eine schlechte Strategie, einen Nachtspeicherofen dann nochmal gegen Sonnenaufgang aufzuladen, denn da dürfte schon lange wieder der Tagstromtarif gelten.

Hey!
Danke für dein Mitdenken und die Hyterese-Idee!
Das "Problem" wollte ich dadurch lösen, dass ich das Delay am Ende ewig in die Länge ziehe.
Ich werde nach Testläufen wohl auf ~10-15 Minutenintervalle setzen, hab ich mir Gedacht. Einige Hundert Kilo Heizung erhitzen sich ja nicht mal eben -> Da ist eine Gewisse Trägheit im Sytsem :wink:

Und das mit dem Nachladen am Morgen wird dadurch behoben, dass mir der Energieversorger (RWE) nur zu bestimmten Tageszeiten Strom zur Verfügung stellt! (Nachts 20- 6 Uhr und Mittags 13-15 Uhr)!

Gerade wenn die Temperatur sehr langsam steigt taucht da Problem das Jurs beschreibt häufig auf.
Die Außentemperatur überträgst du auch 4-5 Mal die Sekunde was doch absolut überflüssig ist. Alle paar Minuten eine Messung reicht völlig aus und würde auch mehr Ruhe in deine Steuerung bringen.

EDIT: Hab gerade gesehen das du in deiner Steuerung immer 5 Sekunden lang das ganze Programm anhälst, das ist auch nicht die feine englische Art. Lass deine Steuerung ruhig machen und übertrage die Temperatur einfach seltener, dann reduzierst du auch den Elektrosmog wesentlich und deine Batterie im Sender hält länger. Ansonsten sendest du 20-25 Mal deine Temperatur und die Steuerung bekommt nix davon mit weil sie abwartet, umgekehrt wäre sinnvoller.

Gruß