Skip to main content

In meinem letzten Artikel habe ich euch die Grundlagen des Smart Campers gezeigt. Wir können jetzt die Lichter schalten und die Temperatur im Fahrzeug messen. Da unser Camper aber erst richtig „smart“ werden kann, wenn alle Komponenten Teil des Systems sind, habe ich mir Gedanken gemacht, wie ich an das Kernstück des Fahrzeuges, das Westfalia Zentralsteuergerät herankomme, da hier die Heizung, das Warmwasser und der Kühlschrank gesteuert und die Füllstände der Tanks gelesen werden können. Eine Anfrage an Westfalia, ob es hier eine Möglichkeit gibt, blieb leider unbeantwortet. Das macht es zwar schwieriger aber weckt auch meinen Tatendrang, es trotzdem zu schaffen. Um es kurz zu machen: Auch ohne die Hilfe von Westfalia habe ich eine Möglichkeit gefunden mich in das Zentralsteuergerät einzuklinken. Dieser Artikel beschreibt, wie man das Westfalia Zentralsteuergerät in das Homematic-System einbinden kann.

Grundlagen Westfalia Bedienteil und Zentralsteuergerät

Ich habe festgestellt, dass das Bedienteil über der Schiebetür mit 4 Kabeln versorgt wird. 12V und Ground sowie 2 verdrillte Adern (das ist auch schon der erste Hinweis, um was es sich hier handeln könnte). Da neben dem Stromanschluss nur 2 Kabel vorhanden sind, kann das Bedienteil nicht die ganze Magic im Camper machen, es muss noch irgendwo etwas mit sehr viel mehr Kabeln sein.

Ich erinnerte mich, beim Einbau der Netzvorrangschaltung in der Serviceklappe ein Steuergerät gesehen zu haben, aus der viele Kabel führten. Dazu muss die Bodenplatte in der Serviceklappe entfernt werden. Das Steuergerät ist von unten mit Klettverschluss an der Bodenplatte befestigt (Ich habe gelesen, dass bei den größeren Fahrzeugen das Steuergerät im Küchenblock ist). Hier habe ich 2 verdrillte Kabel in denselben Farben gefunden wie am Bedienteil. Die Geräte scheinen also über diese Kabel zu kommunizieren. Als nächstes habe ich mir die beiden Geräte mal von innen angeschaut. Und siehe da, an den Anschlüssen ist im Inneren jeweils ein TLE6250G CAN Transreceiver verlötet. Es handelt sich also um einen CAN-Bus.

Mit einem CAN-Analyzer (USBtin EB USB-CAN Adapter) kann man feststellen, dass die beiden Geräte über 500kBps kommunizieren.  4 IDs werden im Abstand von ca 15ms immer wieder gesendet.

ID DLC Startkonfiguration Frequenz Verwendung
0x350 2 00 60 15ms Tasten vom Bedienteil
0x200 8 6b 03 00 40 80 10 01 e0 15ms Antwort vom Zentralsteuergerät
0x330 8 B0 00 60 78 00 00 00 00 15ms Vielleicht die Zeit
0x240 8 03 26 0b e9 89 59 8d 6d 1s Keine Ahnung, brauche ich aber auch nicht

 

Es zeigt sich, dass das Bedienteil einfach nur stupide die Änderungen an den Tasten weitergibt, also die Position des Rades und das Drücken der Tasten. All das ist in Byte0 der ID 0x350 enthalten:

Ich kann mich durch Senden der richtigen Kombinationen durch die Menüs des Bedienteils bewegen und Werte lesen bzw. schreiben. Mit jeder Änderung der Drehposition, zeigt das Bedienteil ein anderes Menü als markiert an. Dies äußert sich dadurch, dass in der Antwort des Steuergerätes das 5te Byte die aktuell markierte Menüposition zeigt. Die Kombination aus dem Lesen der ID 0x200 und dem Senden von Tastenkombinationen ermöglich also eine Navigation durch die verschiedenen Menüs

Einstellung des Zentralsteuergeräts über CAN lesen bzw. Schreiben

Mit einem ESP32 Microcontroller und einem MCP2515 CAN Modul war die Kommunikation zum Zentralsteuergerät schnell hergestellt. Der im Abschnitt Installation bereitgestellte Code sendet die Menübefehle so lange bis das entsprechende Register ausgewählt ist. Dann betätigt der Code die Enter-Taste und sucht mit dem Drehrad den Wert, der gelesen oder geschrieben werden soll. Die Werte finden sich meistens im Bit0 der ID 0x200. Beim Schreiben wird der Wert über die Pfeiltasten so lange geändert, bis der Zielwert erreicht ist. Anschließend wird das Menü mit Drehrad und Entertaste wieder verlassen.

Kommunikation zwischen ESP32 und der Homematic

Jetzt müssen die Informationen noch mit der Homematic ausgetauscht werden und das ganze wie immer möglichst einfach und energiesparend. Ich habe eine Bibliothek gefunden, die asksin++ heißt und das Erstellen eigener Homematic-Komponenten ermöglicht, die über Funk eingebunden werden können. Leider bin ich aus dem Code nicht schlau geworden und habe mich dann für eine andere sehr viel einfachere Variante entschieden. Da mein Raspberry Pi sowieso im WLAN eingebunden ist, kann ich auch einfach über WLAN die Kommunikation zwischen ESP32 und der Homematic bewerkstelligen. Dafür benutze ich das Addon XMLAPI, das es ermöglicht über API-Calls Variablen der Homematic zu lesen und zu ändern. Da WLAN aber den Stromverbrauch meines ESP32 stark in die Höhe treibt, habe ich diesen über einen GPIO mit dem Raspberry Pi verbunden. Der ESP32 verbringt den Großteil seines Daseins in DeepSleep bei quasi 0mA Stromaufnahme. Sobald er eine Aktion (Wert Lesen oder Schreiben) ausführen soll, schreibt die Homematic den Befehl, der ausgeführt werden soll in die Variable „ESP32_Befehl“, der ESP32 wird über den GPIO geweckt, verbindet sich mit dem WLAN, liest über die XML-API den Befehl und führt ihn aus. Beim Lesen speichert der ESP32 den über den CAN-Bus ermittelten Wert in der dafür vorgesehenen Variablen und bestätigt in der Variablen „ESP32_Befehl“ durch setzten des Wertes „OK“ den Erfolg. Beim Schreiben holt sich der ESP32 den gewünschten Wert aus der entsprechenden Variablen und überträgt ihn über den CAN-Bus (durch aufeinanderfolgende Tastenbefehle) an das Steuergerät.

Das war’s zu den Grundlagen, als nächstes beschreibe ich, wie ihr es nachbauen könnt.

Komponenten:

Grundvoraussetzung ist, dass ihr die Raspberrymatic auf dem Raspberry Pi und das WLAN wie im ersten Teil der Smart Camper Artikel beschrieben aufgebaut habt.

Zusätzlich zu diesen Komponenten braucht ihr noch:

1x ESP32 Development Board (ca. 10€)
1x MCP2515 CAN Bus Shield (ca. 5€)
1x Satz Jumper Drähte (ca. 5€)

Installation:

Vorbereitungen in der Homematic WEB UI

Variable Typ Werte
HomeMatic_Befehl String XXX_Lesen / XXX_Schreiben
Status_Heizung Werteliste 0;1
Lueftug_Heizung Werteliste 0;1;2;3
SollTemp_Bad Zahl 10-40
SollTemp_IN Zahl 10-40
Status_Kuehlschrank Werteliste 0,1,2,3,4,5,6
Boiler Werteliste 0;1
Wasserstand Zahl

 

Code auf den ESP32 installieren

MCP2515::ERROR MCP2515::setNormalMode()

{

modifyRegister(MCP_CANCTRL, CANCTRL_OSM, CANCTRL_OSM);

return setMode(CANCTRL_REQOP_NORMAL);

}

Sie führt dazu, dass eine CAN Nachricht nur ein einziges Mal gesendet wird und kein acknowledge erwartet wird. Im Datenblatt des MCP2525 als Single-Shot-Modus beschrieben.

Anpassung der spezifischen Variablen:

Der Quellcode für den ESP32 befindet sich am Ende des Artikels zum Download. Um ihn einzusetzen, müssen noch folgende Variablen angepasst werden:

Typ Bezeichnung Bsp:
const char* ssid “MeinWLAN”
const char* password “MeinPW”
const String IP_Adr_HomeMatic “192.168.1.2”
String ISE_HomeMatic_Befehl “1234”
String ISE_Status_Heizung “1235”
String ISE_Lueftug_Heizung “1236”
String ISE_SollTemp_Bad “1237”
String ISE_SollTemp_IN „1238“
String ISE_Status_Kuehlschrank „1239“
String ISE_Boiler „1240“
String ISE_Wasserstand “1241”

Verbindung der Kompoenten

ESP32 mit dem MCP2515 und dem Raspberry Pi verbinden wie im letzten Bild gezeigt. CAN-H und CAN-L können mit Jumper-Kabeln an das an das Westfalia Zentralsteuergerät (Diagnose Eingang) angesteckt werden.

Programme in der Homematic

Um bei Änderung einer Variablen zu veranlassen, dass diese an das Zentralsteuergerät übertragen wird, wird in der Homematic ein Programm geschrieben, das bei Änderung der Variablen ausgelöst wird. Gleichzeitig muss aber verhindert werden, dass das Programm ausgelöst wird, wenn über die XMLApi ein Wert vom ESP32 gelesen wird. Hierfür gibt es die zusätzliche Bedingung, dass der Befehl „OK“ sein muss. Dies ist erst der Fall, wenn der Vorgang davor erfolgreich abgeschlossen wurde. Folgendes Beispiel zeigt das Programm, das natürlich für jede der Variablen angelegt werden muss.

Cloudmatic

Um das Ganze über das Handy steuern zu können, habe ich die neuen Funktionen in meiner CloudMatic hinzugefügt. Folgendes Bild zeigt die neue UI

Fazit

Nicht trivial nachzubauen, aber mit meiner Vorarbeit ist es in überschaubarer Zeit zu realisieren. Man sollte aber schonmal mit Microcontrollern gearbeitet haben. Jetzt kann ich von der Skipiste meine Heizung einschalten oder den Kühlschrank aktivieren, damit der kühl ist, wenn die Reise losgeht. Ich habe mein Ziel des Smart Campers zwar noch nicht erreicht, aber auf jeden Fall einen großen Schritt in die Richtung gemacht. Als nächstes mache ich mir Gedanken über die Türen und die Werte der Solaranlage.

#include <Arduino.h>
#include <mcp2515.h>
#include <HTTPClient.h>
#include <SPI.h>
#include "WiFi.h"

bool DEBUG_MODUS = true;//Auch Serielle eingaben werden als CAN-Nachrichten berücksichtigt + Serielle Infos

const char* ssid = "XXXX";
const char* password =  "XXXX";
const String IP_Adr_HomeMatic =  "192.168.178.2";

String ISE_HomeMatic_Befehl = "XXXX";
String ISE_ESP32_Boot_Count = "XXXX";

String ISE_Status_Heizung = "XXXX";
String ISE_Lueftug_Heizung = "XXXX";
String ISE_SollTemp_Bad = "XXXX";
String ISE_SollTemp_IN = "XXXX";
String ISE_Status_Kuehlschrank = "XXXX";
String ISE_Boiler = "XXXX";
String ISE_Wasserstand = "XXXX";

//RTC_DATA_ATTR = 16kB RTC-Memory bleibt im sleep modus erhalten
RTC_DATA_ATTR int bootCount = 0; 
RTC_DATA_ATTR byte DrehPosByte32 = 0x00;//Letzte Drehposition wird hier gehalten
RTC_DATA_ATTR byte SendenByte = 0x00;
const byte EnterKnopf = 0x40;
const byte RunterKnopf = 0x20;
const byte RaufKnopf = 0x80;

MCP2515 mcp2515(5); //CS an Pin 5
struct can_frame canMsg_TX; //Senden Nachricht
struct can_frame canMsg_RX; //Empfangen Nachricht


unsigned long Zeitstempel = 0;   // Zeitstempel für Timeout
const int Timeout = 10000; //Bricht die Bewegung im Menue nach dieser Zeit ab, weil davon auszugehen ist, das etwas nicht stimmt

int Zaehler_Nachricht = 0; //hält die aktuelle Anzahl der Wiederholungen
int Anzahl_Nachricht_Wiederholen = 10; //wie oft eine Nachricht (nur aktuelle Drehposition) gesendet wird, bis die Nächste erzeugt wird
                                      //bei 50 => 1 Sekunde bis nächsten Befehl
                                      //bei 6[empfohlen] => ca. 120ms/Befehl oder 8Befehle/Sekunde 
                                      //ACHTUNG: Wert muss Mindestens 2 sein, sonst werden Knöpfe gedrückt aber nicht mehr losgelassen

//Dann und Sonst-Befehle für die Menuepfade
const int ZielErreicht = 0;
const int RadPlus = 1;
const int RadMinus = 2;
const int Enter = 3;
const int Runter = 4;
const int Rauf = 5;

// Hier sind die Menuepfade der einzelnen Funtionen gespeichert:
// CANByte, Erwartungswert, BefehlDann,BefehlSonst 
byte MenuePos_KuehlschrankStufe [2][4] {  {5,0x15,Enter,RadPlus},
                                          {5,0xa5,ZielErreicht,RadPlus}};
byte MenuePos_HeizungSchalten [2][4] {    {5,0x16,Enter,RadPlus},
                                          {5,0xd6,ZielErreicht,RadPlus}};
byte MenuePos_HeizungLueftung [2][4] {    {5,0x16,Enter,RadPlus},
                                          {5,0xe6,ZielErreicht,RadPlus}};
byte MenuePos_HeizungTemp [3][4] {        {5,0x16,Enter,RadPlus},
                                          {5,0x16,ZielErreicht,RadPlus}};
byte MenuePos_HeizungTempBad [3][4] {     {5,0x16,Enter,RadPlus},
                                          {5,0x16,ZielErreicht,RadMinus}};
byte MenuePos_HeizungBoiler [2][4] {      {5,0x16,Enter,RadPlus},
                                          {5,0xe6,ZielErreicht,RadPlus}};
byte MenuePos_Wasserstand [2][4] {        {5,0x12,Enter,RadPlus},
                                          {5,0x12,ZielErreicht,ZielErreicht}};

//Setup
void setup1() {
  Serial.begin(115200);

}

void setup() {

  int WiFi_Connect_Versuche = 0;
  Serial.begin(115200);
  mcp2515.reset();
  mcp2515.setBitrate(CAN_500KBPS,MCP_8MHZ);
  mcp2515.setNormalMode();
  Serial_Senden("Setup OK");
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial_Senden("Connecting to WiFi..");
    ++WiFi_Connect_Versuche;
    if(WiFi_Connect_Versuche>20){
      ESP.restart();// Reset, wenn was WiFi Modul spinnt und nach 10sekunden noch keine Verbindung besteht
    }
  }
  ++bootCount;
  Serial_Senden("Verbunden");
  
  //Read_ISE_ID_Value(ISE_HomeMatic_Befehl);
  //Write_ISE_ID_Value(ISE_ESP32_Boot_Count, String(bootCount));

  //der Raspberry Pi Zero möchte etwas sagen
  pinMode(GPIO_NUM_34, INPUT_PULLUP);
  Serial_Senden(String(digitalRead(GPIO_NUM_34)));
  if (digitalRead(GPIO_NUM_34)==1){
    String Befehl = Read_ISE_ID_Value(ISE_HomeMatic_Befehl);
    String Funktion = split(Befehl, '_',0);
    String LesenSchreiben = split(Befehl, '_',1);
    Serial_Senden(Befehl);
    if (Funktion=="Alles"){
      CAN_BUS_LesenSchreiben("HeizungBoiler",LesenSchreiben );
      CAN_BUS_LesenSchreiben("HeizungTempBad",LesenSchreiben );
      CAN_BUS_LesenSchreiben("HeizungTemp",LesenSchreiben );
      CAN_BUS_LesenSchreiben("HeizungLueftung",LesenSchreiben );
      CAN_BUS_LesenSchreiben("HeizungSchalten",LesenSchreiben );
      CAN_BUS_LesenSchreiben("Wasserstand",LesenSchreiben );
      CAN_BUS_LesenSchreiben("Kuehlschrank",LesenSchreiben );
    } 
    else{
      CAN_BUS_LesenSchreiben(Funktion,LesenSchreiben );
    }  
  }

  //Schlafenszeit bis GPIO34 High
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_34, HIGH);
  Serial_Senden("mcp2515 und ich gehen schlafen!!");
  mcp2515.setSleepMode();
  esp_deep_sleep_start();
}

//Endlosschleife
void loop() {
  //Das hier wird nie passieren, da vorher der SleepMode aktiviert wird
}

//true wenn Nachricht mit ID 0x200 erhalten (nur alle 2 mal)
bool CAN_Nachricht_0x200_erhalten(){
  if (mcp2515.readMessage(&canMsg_RX) == MCP2515::ERROR_OK) {
    if (canMsg_RX.can_id == 0x200){
      if(Zaehler_Nachricht <(Anzahl_Nachricht_Wiederholen/2)){
        Zaehler_Nachricht++;
        CAN_Senden(SendenByte);
      }
      else if (Zaehler_Nachricht <Anzahl_Nachricht_Wiederholen-1){
        Zaehler_Nachricht++;
        CAN_Senden(DrehPosByte32);
      }
      else {
        Zaehler_Nachricht = 0;
        return true;
      }
    }
  }    
  else if (DEBUG_MODUS == true){
    if (Serial.available() > 0){
      // read the incoming stringg
      String incomingString = Serial.readStringUntil('\n');
      for (int i = 0; i < 8; i++) {
        canMsg_RX.data[i] = byte(incomingString.toInt());
      }
      if(Zaehler_Nachricht <Anzahl_Nachricht_Wiederholen-1){
        Zaehler_Nachricht++;
        CAN_Senden(DrehPosByte32);
      }
      else {
        Zaehler_Nachricht = 0;
        return true;
      }
    }
  }
  return false;
}

void CAN_Senden(byte Byte0){
  SendenByte = Byte0;
  canMsg_TX.can_id = 0x351;
  canMsg_TX.can_dlc = 2;
  canMsg_TX.data[0] = Byte0;
  canMsg_TX.data[1] = 0x60;  
  mcp2515.sendMessage(&canMsg_TX);
}

void MenueRad_drehen(bool UZS){
  if (UZS == true){
    if(DrehPosByte32<32){++DrehPosByte32;}
    else{DrehPosByte32 = 0;}
  }
  else{
    if(DrehPosByte32!=0){--DrehPosByte32;}
    else{DrehPosByte32 = 31;}
  }
}

//Berechnet den Wert falls dieser nicht klar im Byte steht
float Wert_aus_Byte(byte ByteWert, int WertTyp){
  switch ( WertTyp )
  {
    case 1: //Temperatur
      if (ByteWert % 5 == 0){
        Serial_Senden(String(float(ByteWert)/10));
        return float(ByteWert)/10;
      }
      else{
        Serial_Senden(String(float(256+ByteWert)/10));
        return float(256+ByteWert)/10;
      }
      break; 
  case 2: //Boiler
      if (ByteWert == 0x96){
        return 1;
      }
      else{
        return 0;
      }
    default:
      return float(ByteWert);
  }
}
//Berechnet den Wert der in das Can Byte geschreiben werden muss
byte Byte_aus_Wert(float FloatWert, int WertTyp){
  switch ( WertTyp )
  {
    case 1: //Temperatur
      if (FloatWert >=26){
        return (FloatWert*10)-256;
      }
      else{
        return FloatWert*10;
      }
      break; 
    case 2: //Boiler
      if (FloatWert == 1){
        return 0x96;
      }
      else{
        return 0xa6;
      }
    default:
      return FloatWert;
  }
}

//Führt einen Tastenbefehl aus
bool BefehlAusfuehren (byte Befehl){
  switch ( Befehl )
  {
      case ZielErreicht: //0 = Erledigt
          CAN_Senden(DrehPosByte32);
          return false;
          //Letzter Befehl => Fertig
      case RadPlus://1 = Rad+
          MenueRad_drehen(true);
          CAN_Senden(DrehPosByte32);
          return true;
      case RadMinus://2 = Rad-
          MenueRad_drehen(false);
          CAN_Senden(DrehPosByte32);
          return true;
      case Enter://3 = OK
          CAN_Senden(DrehPosByte32|EnterKnopf);
          return true;
      case Runter://4 = Runter
          CAN_Senden(DrehPosByte32|RunterKnopf);
          return true;
      case Rauf://5 = Rauf
          CAN_Senden(DrehPosByte32|RaufKnopf);
          return true;
  }
}
//Liest ByteLesen und berechnet den Wert abhängig vom Typ (1=Temperatur)
float Wert_Lesen(byte ByteLesen, int WertTyp){
  return Wert_aus_Byte(canMsg_RX.data[ByteLesen],WertTyp);
}
//Schreibt den Sollwert (vom Ty Float) in ByteSetzen abhängig vom Typ (1=Temperatur)
float Wert_Setzen(float SollWert, int WertTyp, byte ByteSetzen){
  int Taste;
  Zeitstempel = millis();
  if (Wert_aus_Byte(canMsg_RX.data[ByteSetzen],WertTyp)>SollWert) {
    Taste = Runter;
  }
  else{Taste = Rauf; }
  do{
    if (CAN_Nachricht_0x200_erhalten()==true){
      if (Taste == Runter){
        if(Wert_aus_Byte(canMsg_RX.data[ByteSetzen],WertTyp)>SollWert){
          BefehlAusfuehren(Taste);
        }
        else{return 1;}
      }
      else{
        if(Wert_aus_Byte(canMsg_RX.data[ByteSetzen],WertTyp)<SollWert){
          BefehlAusfuehren(Taste);
        }
        else{return 1;}
      }
    }
  }while (millis() - Zeitstempel <= Timeout);//Timeout
  Serial_Senden("Timeout");
  return 0;
}
//Öffnet das ausgewählte Menue
bool MenueEintrag_auswaehlen(byte MenuePos [] [4]){
  int i = 0;
  Zeitstempel = millis();
  do{
    if (CAN_Nachricht_0x200_erhalten()==true){
      if(canMsg_RX.data[MenuePos[i][0]] != MenuePos[i][1]){//Wenn RegisterWert != Erwartungswert
         Serial_Senden("Falscher Wert");
         BefehlAusfuehren(MenuePos[i][3]);
      }
      else{ //Wenn RegisterWert == Erwartungswert
         Serial_Senden("Erwarteter Wert");
         if(BefehlAusfuehren(MenuePos[i][2])==false){
            //Zielposition erreicht
            return true;
         }
         ++i;
      }
    }
  }while (millis() - Zeitstempel <= Timeout);//Timeout
  Serial_Senden("Timeout");
  return false;
}
//Beendet das Menu und geht wieder ins Hauptmenue
bool Menue_Exit(){
  Zeitstempel = millis();
  do{
    if (CAN_Nachricht_0x200_erhalten()==true){
      if(canMsg_RX.data[5]==0x14){
        BefehlAusfuehren(Enter);
        return true;
      }
      else if(canMsg_RX.data[5]==0x35){
        BefehlAusfuehren(Enter);
        return true;
      }
      else if(canMsg_RX.data[5]==0x32){
        BefehlAusfuehren(Enter);
        return true;
      }
      else if(canMsg_RX.data[5]==0x36){
        BefehlAusfuehren(Enter);
        return true;
      }
      else{
        BefehlAusfuehren(RadPlus);
      }
    }
  }while (millis() - Zeitstempel <= Timeout);//Timeout
  Serial_Senden("Timeout");
  return false;
}

//Teilt einen String in 2 Teile
String split(String s, char delimiter, int index) {
  int Position = s.indexOf(delimiter);
  if (index == 0){
    return s.substring(0,Position);
  }
  else{
    return s.substring(Position+1,s.length());
  }
}

void Serial_Senden(String Text){
  if (DEBUG_MODUS == true){
    Serial.println(Text);
  }
}

float CAN_BUS_LesenSchreiben(String Funktion, String LesenSchreiben){
  bool Status_IO = false;
  byte WertByte = 0;
  byte WertTyp = 0;
  float RueckgabeWert = 0;
  String ISE_ID = "";
  if (Funktion=="Kuehlschrank"){
    Serial_Senden("Start Kühlschrankmenue:");
    Status_IO = Menue_Exit();
    if (Status_IO==true){
      Status_IO = MenueEintrag_auswaehlen(MenuePos_KuehlschrankStufe);
    }
    Serial_Senden(String(Status_IO));
    ISE_ID = ISE_Status_Kuehlschrank;
  }
  else if (Funktion=="Wasserstand"){
    Serial_Senden("Start Wasserstand:");
    Status_IO = Menue_Exit();
    if (Status_IO==true){
      Status_IO = MenueEintrag_auswaehlen(MenuePos_KuehlschrankStufe);
    }
    ISE_ID = ISE_Wasserstand;
  }
  else if (Funktion=="HeizungSchalten"){
    Serial_Senden("Start HeizungSchalten:");
    Status_IO = Menue_Exit();
    if (Status_IO==true){
      Status_IO = MenueEintrag_auswaehlen(MenuePos_HeizungSchalten);
    }
    WertByte = 4;
    WertTyp = 2;
    ISE_ID = ISE_Status_Heizung;
  }
  else if (Funktion=="HeizungLueftung"){
    Serial_Senden("Start HeizungLueftung:");
    Status_IO = Menue_Exit();
    if (Status_IO==true){
      Status_IO = MenueEintrag_auswaehlen(MenuePos_HeizungLueftung);
    }
    ISE_ID = ISE_Lueftug_Heizung;
  }
  else if (Funktion=="HeizungTemp"){
    Serial_Senden("Start HeizungTemp:");
    Status_IO = Menue_Exit();
    if (Status_IO==true){
      Status_IO = MenueEintrag_auswaehlen(MenuePos_HeizungTemp);
    }
    WertTyp = 1;
    ISE_ID = ISE_SollTemp_IN;
  }
  else if (Funktion=="HeizungTempBad"){
    Serial_Senden("Start HeizungTempBad:");
    Status_IO = Menue_Exit();
    if (Status_IO==true){
      Status_IO = MenueEintrag_auswaehlen(MenuePos_HeizungTempBad);
    }
    WertTyp = 1;
    ISE_ID = ISE_SollTemp_Bad;
  }
  else if (Funktion=="HeizungBoiler"){
    Serial_Senden("Start HeizungBoiler:");
    Status_IO = Menue_Exit();
    if (Status_IO==true){
      Status_IO = MenueEintrag_auswaehlen(MenuePos_HeizungBoiler);
    }
    WertByte = 4;
    WertTyp = 2;
    ISE_ID = ISE_Boiler;
  }  
  if(Status_IO==true){
    if (LesenSchreiben == "Schreiben"){
      Serial_Senden("Start Schreiben:");
      String SollWert_String = Read_ISE_ID_Value(ISE_ID);
      float SollWert = SollWert_String.toFloat();
      RueckgabeWert = Wert_Setzen(SollWert,WertTyp,WertByte);
    }
    else{
      Serial_Senden("Start Lesen:");
      RueckgabeWert = Wert_Lesen(WertByte, WertTyp);
      Serial_Senden(String(RueckgabeWert));
      Write_ISE_ID_Value(ISE_ID, String(RueckgabeWert));
    }
    Serial_Senden("Befehl OK = Bestaetigen");
    Write_ISE_ID_Value(ISE_HomeMatic_Befehl, "OK");
    Menue_Exit();
    Zeitstempel = millis();
    do{
      if (CAN_Nachricht_0x200_erhalten()==true){
        CAN_Senden(DrehPosByte32);
        return RueckgabeWert;
      }
     }while (millis() - Zeitstempel <= Timeout);//Timeout
  }
Write_ISE_ID_Value(ISE_HomeMatic_Befehl, "Fehler");
return 0;
}

//Liest den Value der ISE_ID über xmlapi aus
String Read_ISE_ID_Value(String ISE_ID){
   String Return_value = "";
   if ((WiFi.status() == WL_CONNECTED)) { 
 
    HTTPClient http;
    http.begin("http://" + IP_Adr_HomeMatic + "/addons/xmlapi/sysvar.cgi?ise_id=" + ISE_ID);
    int httpCode = http.GET();                                  
 
    if (httpCode > 0) {
 
        String payload = http.getString();
        //Serial_Senden(payload);
        int StartVal = payload.indexOf("value='")+7;
        int EndVal = payload.indexOf("'",StartVal);
        Return_value = payload.substring(StartVal,EndVal);
      }
 
    else {
      Serial_Senden("Error on HTTP request Homematic xmlapi");
    }
    http.end();
  }
  return Return_value;
}

//Setzt den Value der ISE_ID über xmlapi
void Write_ISE_ID_Value(String ISE_ID, String ISE_Value){
   String Return_value = "";
   if ((WiFi.status() == WL_CONNECTED)) { 
    HTTPClient http;
    http.begin("http://" + IP_Adr_HomeMatic + "/addons/xmlapi/statechange.cgi?ise_id=" + ISE_ID + "&new_value=" + ISE_Value);
    int httpCode = http.GET();                                        
 
    if (httpCode > 0) {
 
        String payload = http.getString();
        int StartVal = payload.indexOf("new_value")+11;
        int EndVal = payload.indexOf(" ",StartVal)-1;
        Return_value = payload.substring(StartVal,EndVal);
        if(Return_value.equals(ISE_Value)){
          Serial_Senden("ISE_Value: " + ISE_ID + " gesetzt:" + ISE_Value);
        }
        else{
          Serial_Senden("Write_ISE_ID_Value error");
        }
      }
    else {
      Serial_Senden("Error on HTTP request Homematic xmlapi");
    }
    http.end();
  }
}

16 Comments

  • Mirko sagt:

    Hallo Ihr 2,

    Vielen Dank für den Beitrag, gut beschrieben und Reverse Engineerd. Verfolge Eure Beiträge regelmäßig um die Wartezeit auf unseren Columbus 540AD zu verkürzen. Er soll im Juli kommen. Die eine oder andere Idee werden wir sicher auch aufgreifen.

    Lg Mirko

    • Sonja sagt:

      Hallo Mirko!
      Wir drücken dir die Daumen, dass dein Columbus pünktlich geliefert wird! Und dann willkommen im Club! 🙂
      Wenn du Fragen hast, sobald du dich ans Umsetzen der Ideen machst, melde dich gerne!
      Schöne Grüße
      Sonja & Matthias

    • Matthias Harzheim sagt:

      Hallo Mirko,

      ich hätte ehrlichgesagt nicht geglaubt, auf einen so technischen Artikel, so viel Rückmeldung zu bekommen. Scheinen aber doch einige „advanced“-Bastler zu unseren Lesen zu gehören, was mich sehr freut!

      Grüße,

      Matthias

  • Mario sagt:

    Hi,
    sehr schönes Projekt, gut recherchiert und die Anbindung an Homematic ist sehr interessant.
    Betreibst du denn deine CAN-Ankopplung parallel zum Zentralsteuergerät? Das Steuergerät sendet ja zyklisch Drehposition und Tasteninformationen wie du es auch beschrieben hast auf ID 0x350. Das „beißt“ sich doch dann mit dem Senden einer zusätzlichen Info auf 0x350, oder? In deinem Code benutzt du auch die ID 0x351, wieso?
    Ciao
    Mario

    • Matthias Harzheim sagt:

      Hi Mario,

      hier etwas verspätet deine Antwort: Das Hauptsteuergerät scheint auf jeder ID ein Bedienteil zu akzeptieren. Damit sich die beiden nicht in die Quere kommen, sollte man eine andere ID als die 0x350 verwenden, sonst sendet das eine Bedienteil die eine und das andere gleichzeitig die andere Position, was zu einem hin- und herspringen führt. Die 0x351 habe ich gewählt damit das manuelle Bedienteil die höhere Prio auf dem CAN hat.

      Grüße,

      Matthias

      • Mario sagt:

        Hi Matthias,

        danke dir für deine Antwort. Ist ja ein Ding, dass man einfach andere Ids als Input verwenden kann. Na ja, wenn es funktioniert, umso besser. Bei mir klappt es noch nicht, aber ich bin dran!
        Was ich aber schon sagen kann ist, dass sich bei mir in der ID 330 diverse Statusinfos wie die von Kühlschrank und Standheizung verbergen. Im Byte 1 (von 0 bis 7 gezählt) liegt am Bit 2 die SH und am Bit 4 der KS. Batteriespannung u. a. findet man auch in der ID 330 …

        Viele Grüße
        Mario

  • Stefan.AC sagt:

    Vielen Dank! Das werde ich in meinem Nugget verwenden.

    kann man denn erkennen, wenn einer der Werte durch das Panel verändert wurde?

    Sollte man dazu alle Tastendrücke des Panels mitbekommen, und die entsprechenden Antworten mitlesen und interpretieren?

    Alternativ könnte man auch sporadisch die Werte aufrufen, dann darf man nur nicht gleichzeitig selber am Rad drehen 🙂

    Stefan

    • Matthias Harzheim sagt:

      Hallo Stefan,

      um Strom zu sparen, lese ich nicht die ganze Zeit den CAN mit, da der leider nie „schläft“. Ist wahrscheinlich auch ein Grund, warum der Columbus so viel Strom im Standby braucht. Aber ich habe Funktionen geschrieben, um den aktuellen Wert zu ermitteln. Theoretisch könnte man auch die Information, ob der Kühlschrank oder die Heizung an ist ohne etwas auf den CAN zu senden ermitteln. Es wird ja im Hauptmenü angezeigt und muss daher auch auf der dauerhaft gesendeten CAN Nachricht stehen. Wenn dass jemand schreibt gerne mit mir teilen.

  • Mirko sagt:

    Hallo Ihr 2,
    der Nachbau der CAN Anbindung des Westfalia Zentralsteuergeräts läuft aufgrund Eurer guten Beschreibung auf einem ESP32 und kommuniziert mit einem CAN USB Adapter…. (der Columbus lässt auf sich warten um es am Gateway zu testen).
    Stelle mir gerade die Frage inwiefern sich eine Ortungsfunktion über GPS/GSM am Raspi realisieren ließe. Ihr habt ja dafür eine Wipro und den Pro Finder, verbaut. Zusätzlich einige Smart Home Funktionen über einen LTE WLAN Stick nach außen angebunden. Das heißt Ihr habt 2 SIM Karten im Einsatz?
    Würdest Du diese Kombination genau so wieder einbauen oder gibt es eine Möglichkeit mit nur einer Simkarte auszukommen und die Ortungsfunktion mit über das Smart Home zu realisieren? Knackpunkt wird vermutlich die Stromaufnahme des Smart Homes im Vergleich zur WiPro sein?

    Freue mich auf Eure Rückmeldung
    Gruß Mirko

    P.S.: Wohnen seit Jahren in einem selbst gestrickten Smart Home, zunächst einige Module in selbst entwickelt, später, Homematic, FHEM, Smartvisu als Bedienoberfläche ergänzt. Das Gesamtkonzept für den Camper muss sich noch finden….

    • Matthias Harzheim sagt:

      Hallo Mirco,

      erst einmal freut es mich sehr, dass jemand meine Ideen nachbaut und dass es auch noch funktioniert . Zu deiner Vermutung: Ja ich habe 2 Sim-Karten im Camper (eine für die Wiipro und eine für mein Smart-Camper). Die Wiipro kommuniziert aber nur über SMS und ist daher sehr günstig hier habe ich eine Prepaid Karte mit 9c/SMS ohne Datenvolumen im Einsatz. Im Smart-Camper verwende ich einen O2 Datentarif (ohne SMS und Telefon) mit 150MB/Monat für 1,99 €. Das hat den Vorteil, dass ich jederzeit über mein Handy auch Datenvolumen nachkaufen kann, da der Vertrag sehr flexibel ist. So kann ich den Hotspot auch mal beruflich nutzen, wenn ich einfach Datenvolumen aufladen kann.
      Jetzt zu deiner Frage, ob ich es wieder so machen würde. Die Wiipro war als erstes da und Ausbauen macht keinen Sinn. Natürlich könnten alle Funktionen auch genauso von der Homematic übernommen werden. Blinker und Lichter sowie Hupe müsste man dann als Signalgeber noch anschließen. Den ESP32 gib es auch mit GPS-Modul. Das könnte man dann sehr einfach lesen und die Koordinaten auch in der App anzeigen. Ich würde hierzu einen TTGO T-Beam verwenden, der in der Lenksäule verstaut wird und sowohl die Hupe und das Licht schaltet und die GPS Positionierung übernimmt.
      Ich versuche gerade die Verbindung zum Komfort-CAN (CAN-Bus für die Zentralverriegelung und die Türen und Fenster) des Ducato herzustellen, um diese Informationen auch in meinem Smart-Camper zu haben. Da hier aber Low-Speed-CAN und nicht High-Speed-CAN wie bei der Westfalia Zentralsteuerung verwendet wird, ist es etwas schwieriger, da es keine fertige Schaltung im Internet gibt und ich das erstmal realisieren muss. Sobald ich diese Infos auch habe fehlt eigentlich nur noch das GPS um die WiiPro zu ersetzen. Fensterkontakte gibt es ja auch bei Homematic.
      Trotzdem finde ich die WiiPro hat ihre Daseinsberechtigung, da sie wirklich gut funktioniert und noch nie einen Fehlalarm hatte. Ich werde aber den Ausgang der WiiPro für die externe Sirene auch noch überwachen, dann sind die 2 Systeme quasi verbunden und mein Smart-Camper weiß, dass ein Alarm ausgelöst wurde. Dann kann ich alle Liter einschalten, um zusätzlich Abschreckung zu generieren. Man könnte sich dann wenigstens den Pro-Finder sparen.
      Grüße,

      Matthias

  • André sagt:

    Hallo Sonja, hallo Matthias,
    vielen Dank für Eure Beiträge wie Ihr Eure Smart-Camping-Ideen mit der Westalia Zentraleinheit realisiert. Habe dieses mit einem (noch nicht gelieferten) Nugget auch vor. Die Verbindung mit einem Raspberry pi in Verbindung mit den Victronic Produkten bietet sich ja quasi an.

    Liebe Sonja,
    Eure Berichte vom Klettern gefallen uns ebenfalls sehr, da sie bei uns ebenfalls auf der Agenda stehen und mit ein Grund für den Camperkauf waren.

    Weiterhin happy Camping !
    LG André

  • Magda sagt:

    Es wäre cool, wenn man das als fertige Plug and Play Lösung bei euch kaufen könnte. Vielleicht ist das noch eine Idee für euch…

    Danke für den Beitrag und für die vielen anderen! Ohne diesen Blog wäre unser 540d niemals so cool geworden!

    • Matthias Harzheim sagt:

      Hallo Magda,
      danke für deine netten Worte! Ich hatte wirklich überlegt das Ding Plug and Play anzubieten. Leider ist es beim Verkauf von sowas immer schwierig mit Gewährleistung, wenn jemand mal was falsch macht und das Thema Zertifizierung war auch noch ein Problem. Also verkaufen wir unseren Blumentopf und unsere Lichterkette ohne viel Elektronik:-)

      Grüße,

      Matthias

  • Sebastian sagt:

    Hallo Matthias,
    wir haben unseren Columbus 601D jetzt auch ein paar Monate und bin nun dabei das Zentralsteuergerät einzubinden.
    Bei dem CAN Modul MCP2515 und dem AZdelivery ESP32 DevKitC V4 bin ich mir gerade unsicher bei der Verbindung. In der Beschreibung des MCP2515 steht, dass dieser mit 5V arbeitet und beim ESP32 steht das dieser an den GPIOs keine 5V verträgt sondern nur 3.3V.

    Hast du bei dir einen Pegelwandler (Level Shifter) 5V 3.3V dazwischen?

    Viele Grüße aus Rostock,
    Sebastian

  • Magda sagt:

    Ich würde wirklich gern diese Funktionen in unserem Camper haben. Gibt es irgendeine Chance das mit einer Anleitung für nur etwas technisch versierte nachzubauen? Wichtig wäre mir zu wissen welche Teile genau und wo ich genau was worauf programmieren, achten und dann anschließen muss.

    Es gibt so viele Westfalia camper (columbus, Nugget.. ) für die das ein echter gamechanger wäre.

    LG
    Magda

Leave a Reply