Ich habe in früheren Artikeln den Einbau des Votronic Batteriecomputers sowie des Solarreglers beschrieben. Außerdem verwende ich den Votronic Bluetooth Connector um die Information über Batterie und Solaranlage in der Votronic Energie Monitor App sehen zu können. Das geht aber nur im Nahbereich (Bluetooth-Reichweite). Es wäre aber super, diese Informationen auch Online sehen zu können. Außerdem sind das aus Smart-Camper Sicht sehr relevante Daten, die man zum intelligenten Steuern von anderen Funktionen sehr gut gebrauchen kann. Daher müssen diese Daten in mein Smart-Camper-System integriert werden. Wenn mein Handy die Daten lesen kann, muss das auch mit einem Microcontroller möglich sein. Ich habe bei Votronic angefragt, ob sie mir die relevanten Infos zum Bluetooth-Connector zusenden können. Die Antwort war, meine Anfrage wird von einer Fachabteilung geprüft. Da das jetzt aber schon bald 2 Monate her ist, erwarte ich nicht, von Votronic noch Unterstützung zu bekommen. Das bedeutet, ich muss mal wieder Reverse Engineering betreiben.

Grundlagen Bluetooth Low Energy (BLE)

Wie der Name schon verrät ist es eine Bluetooth Technologie die sehr wenig Energie benötigt und sich damit hervorragend für batteriebetriebene Anwendungen eignet. Ein Bluetooth-Server (in diesem Fall der Votronic Bluetooth Connector) bietet dabei verschiedene Werte (Characteristics) zum Lesen oder Schreiben an. Diese Werte sind in so genannten Services themenspezifisch gruppiert. Die IDs der Services und Charakteristics sind für alle Bluetooth-Empfänger frei lesbar. Um auch die Werte lesen zu können, muss eine Verbindung zwischen dem Server und dem Empfänger aufgebaut werden. Hierfür stehen die Möglichkeiten Paring und Bonding zur Verfügung. Wobei Bonding sich wahrscheinlich am besten als dauerhaftes Paring beschreiben lässt, um das Passwort nicht ständig neu eingeben zu müssen. Der Votronic Bluetooth Connector setzt voraus, dass man sich mit ihm bondet, bevor er seine Daten zur Verfügung stellt. Dafür gibt es einen kleinen Knopf am Gerät, mit dem man das Bonding aktivieren kann.

Votronic Bluetooth Low Energy

Mit der Android-App nRF Connect kann man sich mit BLE Netzwerken verbinden und Daten lesen bzw. schreiben. Nachdem man sich mit dem Votronic Bluetooth Connector verbunden hat (bonding über die App von Votronic), kann man hier einige interessante Informationen erhalten. Neben der BLE-Adresse, die man später braucht um sich mit dem Gerät zu verbinden, kann man hier die Services und Characteristics sowie die Werte sehen.

Neben den Geräteinformationen stellt man fest, dass der Bluetooth Connector 3 Services anbietet

#

Service

Verwendung

1 1d14d6ee-fd63-4fa1-bfa4-8f47b42119f0 Keine Ahnung wofür
2 d0cb6aa7-8548-46d0-99f8-2d02611e5270 Hier finden wir die Rohdaten für Batteriecomputer und Solarregler
3 ae64a924-1184-4554-8bbc-295db9f2324a Wird benötigt zum Austausch der vom BLE-Connector aufgezeichneten Werte

Durch Ein- und Ausstecken des Solarreglers sowie des Batteriecomputers konnte ich herausfinden, dass es 2 Characteristics im Service 2 gibt, in denen die Daten von Solarregler und Batteriecomputer in jeweils 20 Byte übertragen werden.

#

Characteristic

Verwendung

2.1 9a082a4e-5bcc-4b1d-9958-a97cfccfa5ec Daten aus dem Batteriecomputer
2.2 971ccec2-521d-42fd-b570-cf46fe5ceb65 Daten aus dem Solarregler
Weitere charakteristics Keine Ahnung wofür

Das war der einfache Teil. Jetzt geht es darum herauszufinden, welche Bytes welche Daten beinhalten. Dafür habe ich mir den Batteriecomputer in einer kleinen Teststrecke an mein Labornetzgerät angeschlossen. Hier kann ich die Spannung beliebig variieren und so besser herausfinden, welche Bytes nun welche Informationen enthalten.

Zunächst möchte ich die Batteriespannung lesen. Diese wird als Gleitkommazahl mit einer Stelle hinter dem Komma in der Anzeige des Batteriecomputers dargestellt. Es ist also wahrscheinlich, dass diese Information in mindestens 2 Byte (1 Byte könnte nur 255 unterschiedliche Werte abbilden) abgebildet wird. Ich habe mir noch einen kleinen Trick überlegt: Und zwar habe ich sowohl die Aufbaubatterie-Spannung als auch die Starterbatterie-Spannung an das Labornetzgerät angeschlossen. In den Daten müssten jetzt 2 Bytes immer die gleichen Werte haben, da ich ja beide an derselben Spannungsquelle habe. Damit konnte ich herausfinden, dass Byte 0 und Byte 1 die Spannung der Aufbaubatterie und Byte 2 und Byte3  die Spannung der Starterbatterie enthält. Die Bytereihenfolge ist dabei little-endian (also das kleinstwertigste Byte am Anfang). In den folgenden Tabellen habe ich meine Erkenntnisse für Solarregler und Batteriecomputer zusammengefasst an an einem Beispiel erklärt:

Batteriecomputer

Byte 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Beispiel FF 04 FD 04 64 00 60 00 64 00 F4 FF FF E8 03 5E 14 18 02 04
Spannung Spannung Unbekannt % Unbekannt Strom Unbekannt
HEX 04FF 04FD 64 FFF4
Dezimal 1279 1277 100 65524
Wert 12,79 12,77 100 -0,024
Einheit V V % A

Solarregler

Byte 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Beispiel FF 04 FD 04 64 00 60 00 64 00 F4 FF FF 00 01 00 01 18 02 04
Spannung Unbekannt Status Unbekannt Ladung Energie Unbekannt
HEX 04FF 0001 0001
Dezimal 1279 1 1
Wert 12,79 1 10
Einheit V Ah Wh

Einbinden der Werte in das Homematic SmartHome

Mit diesen Erkenntnissen kann ich nun mit einem ESP32 Microcontroller die Werte über Bluetooth Low Energy auslesen (der ESP32 muss natürlich vorher über die „Bond“- Taste mit dem Votronic Bluetooth Connector gebonded werden). Der Sourcecode für den ESP32 kann am Ende des Artikels heruntergeladen werden.

Beschreibung:

Um die Werte vom Bluetooth Connector an die Raspberrymatic zu übertragen, startet ein Programm auf der Raspberrymatic, dass zunächst in die Variable ESP32_Befehl den Wert „Votronic“ einträgt. Anschließend wird der ESP32 aufgeweckt. Dafür wird über Funk mittels des „Homematic 8-Kanal-Empfangsmodul HM-MOD-Re-8“ der GPIO 32 auf GND gezogen. Dadurch wacht der ESP32 auf, verbindet sich mit dem WLAN und liest über die XML-API den Befehl in der Variablen ESP32_Befehl. Wird hier “Votronic” erkannt, verbindet sich der ESP32 mit dem Bluetooth Connector und liest die geforderten Werte aus. Anschließend werden diese über WLAN unter Zuhilfenahme der XML-API in die dafür vorgesehen Variablen in der Raspberrymatic übertragen und der ESP32 begibt sich wieder in den Schlafmodus.

Fazit

Die Informationen von Batteriecomputer und Solarregler stellen einen wirklichen Mehrwert in meinem System dar. Jetzt kann man diese sehr relevanten Informationen zu jeder Zeit in der App sehen und auf Basis des Ladestroms oder des Ladestandes automatisch Aktionen auslösen. Beispiele hierfür sind Warnung per Push-Mitteilung an mein Handy, wenn der Ladestand des Campers unter 60% fällt. Oder Warnung, bei hohen Strömen und verriegelter Tür (eventuell ein Kurzschluss). Auch ohne die Hilfe von Votronic lies sich das Ganze sehr einfach umsetzen. Wer ein bisschen mit einem Lötkolben umgehen kann und etwas Basiswissen zu Microcontrollern mitbringt, kann das sehr einfach nachbauen. Und hier noch ein Bild, das zeigt, was ich im Camper jetzt schon alles in mein Smart-Camper-System integriert habe.

ESP32 Sourcecode. Da der BLE-Stack sehr groß ist, muss zum Flashen unter Werkzeug->Partition Scheme der Wert “Huge App” eingestellt werden.

#include <Arduino.h>
#include "BLEDevice.h"
#include <HTTPClient.h>
#include "WiFi.h"
#include <driver/rtc_io.h>
 
#define HM_MAC "90:fd:9f:4f:45:38"  // Adresse über nRF Connect App finden
uint32_t PIN = 173928 ;             // Bonding Passwort

const char* ssid = "XXXX"; //SSID
const char* password =  "XXXX"; //WLAN-Passwort
const String IP_Adr_HomeMatic =  "192.168.8.2"; //IP-Adresse Raspberrymatic im WLAN

//ISE IDs aus XML-API auf der Raspberrymatic
String ISE_HomeMatic_Befehl = "XXXX"; 
String ISE_Spannung_Aufbaubatterie = "XXXX";
String ISE_Spannung_Starterbatterie = "XXXX";
String ISE_Batterieladungsstatus = "XXXX";
String ISE_Strom_BC = "XXXX";
String ISE_Strom_SR = "XXXX";
String ISE_Ladung_SR_Wh = "XXXX";
String ISE_Ladung_SR_Ah = "XXXX";
String ISE_Solarstatus = "XXXX";

 
// Service und Characteristic des Votronic Bluetooth Connectors
static BLEUUID Votronic_serviceUUID("d0cb6aa7-8548-46d0-99f8-2d02611e5270");
static BLEUUID Batteriecomputer_charUUID("9a082a4e-5bcc-4b1d-9958-a97cfccfa5ec");
static BLEUUID Solarregler_charUUID("971ccec2-521d-42fd-b570-cf46fe5ceb65"); 

//Variablen, die immer über die Callbacks aktualisiert werden
//Wert bleibt im Sleep-Modus erhalten
RTC_DATA_ATTR float Spannung_Aufbaubatterie, Strom_BC, Strom_SR, Spannung_Starterbatterie;
RTC_DATA_ATTR int16_t Batterieladungsstatus, Ladung_SR_Wh, Ladung_SR_Ah;
RTC_DATA_ATTR String Solarstatus;

 
static BLEAddress *pServerAddress;
static BLERemoteCharacteristic* pRemoteCharacteristic_BC;
static BLERemoteCharacteristic* pRemoteCharacteristic_SR;
BLEClient*  pClient;
 
class MySecurity : public BLESecurityCallbacks {
  bool onConfirmPIN(uint32_t pin){
    return false;
  }
  uint32_t onPassKeyRequest(){
        ESP_LOGI(LOG_TAG, "PassKeyRequest");
        //delay(1000);
    return PIN;
  }
  void onPassKeyNotify(uint32_t pass_key){
        ESP_LOGI(LOG_TAG, "On passkey Notify number:%d", pass_key);
  }
  bool onSecurityRequest(){
      ESP_LOGI(LOG_TAG, "On Security Request");
    return true;
  }
  void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl){
    ESP_LOGI(LOG_TAG, "Starting BLE work!");
    if(cmpl.success){
      uint16_t length;
      esp_ble_gap_get_whitelist_size(&length);
      ESP_LOGD(LOG_TAG, "size: %d", length);
    }
  }
};

// Batteriecomputer Callback
static void Batteriecomputer_Callback (BLERemoteCharacteristic* pBLERemoteCharacteristic,uint8_t* pData,size_t length,bool isNotify) {
  int16_t iStrom;
  
  Spannung_Aufbaubatterie = float(pData[1]<<8 | pData[0])/100;
  Spannung_Starterbatterie = float(pData[3]<<8 | pData[2])/100;
  iStrom = pData[11]<<8 | pData[10];
  Strom_BC = float(iStrom)/1000;
  Batterieladungsstatus = pData[8];
}

// Solarregler Callback
static void Solarregler_Callback (BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {

    Strom_SR = float(pData[5]<<8 | pData[4])/10; ;
    if(pData[12]==9){
      Solarstatus = "Aktiv";
    }
    else if(pData[12]==25){
      Solarstatus = "Stromreduzierung";
    }
    else{
      Solarstatus = String(pData[12]);
    }
    Ladung_SR_Wh = (pData[16]<<8 | pData[15])*10;
    Ladung_SR_Ah = (pData[14]<<8 | pData[13]);
//    Serial.print("Solarstrom: ");
//    Serial.print(Strom_SR);
//    Serial.print("  |  ");
//    Serial.print("Solarstatus: ");
//    Serial.print(Solarstatus);
//    Serial.print("  |  ");
//    Serial.print("Ladung Wh: ");
//    Serial.print(Ladung_SR_Wh);
//    Serial.print("  |  ");
//    Serial.print("Ladung Ah: ");
//    Serial.print(Ladung_SR_Ah);
//    Serial.println("");
}
 
// Verbinde mit Votronic BLE Server.
bool connectToServer(BLEAddress pAddress)
{
  Serial.print("Verbinde mit ");
  Serial.println(pAddress.toString().c_str());
 
  //BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT );
  BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT );
  BLEDevice::setSecurityCallbacks(new MySecurity());
 
  BLESecurity *pSecurity = new BLESecurity();
  pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND); //
  pSecurity->setCapability(ESP_IO_CAP_OUT);
  pSecurity->setRespEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
  pClient = BLEDevice::createClient();

  if ( pClient->connect(pAddress) ) {
          Serial.println("BLE Verbunden");
          BLERemoteService* pRemoteService = pClient->getService(Votronic_serviceUUID);
          if (pRemoteService == nullptr)
          {
            Serial.print("Service UUID nicht gefunden: ");
            Serial.println(Votronic_serviceUUID.toString().c_str());
            return false;
          }
          //Callback für Batteriecomputer
          pRemoteCharacteristic_BC = pRemoteService->getCharacteristic(Batteriecomputer_charUUID);
          if (pRemoteCharacteristic_BC == nullptr) {
            Serial.print("Characteristic UUID nicht gefunden: ");
            Serial.println(Batteriecomputer_charUUID.toString().c_str());
            return false;
          }
          pRemoteCharacteristic_BC->registerForNotify(Batteriecomputer_Callback);
          Serial.println("Callback Batteriecomputer_Callback");
        
          //Callback für Solarregler
          pRemoteCharacteristic_SR = pRemoteService->getCharacteristic(Solarregler_charUUID);
          if (pRemoteCharacteristic_SR == nullptr) {
            Serial.print("Characteristic UUID nicht gefunden: ");
            Serial.println(Batteriecomputer_charUUID.toString().c_str());
            return false;
          }
          
          pRemoteCharacteristic_SR->registerForNotify(Solarregler_Callback);
          Serial.println("Callback Solarregler_Callback");
  }
  else{
      Serial.println("BLE nicht Verbunden");
  }
  return true;
}

void loop()
{
  //Wird nie erreicht, da der ESP32 vorher ein Schläfchen macht
}

//GPIOs konfigurieren
void Setup_GPIO(){
  // Pins sind mit dem Homematic 8-Kanal-Empfangsmodul verbunden
  pinMode(GPIO_NUM_33, INPUT_PULLUP);
  pinMode(GPIO_NUM_25, INPUT_PULLUP);
  pinMode(GPIO_NUM_26, INPUT_PULLUP);
  pinMode(GPIO_NUM_27, INPUT_PULLUP);
  pinMode(GPIO_NUM_14, INPUT_PULLUP);
  pinMode(GPIO_NUM_12, INPUT_PULLUP);
  pinMode(GPIO_NUM_13, INPUT_PULLUP);



  //WakeupPin = 32
  pinMode(GPIO_NUM_32, INPUT_PULLUP);
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_32, LOW);
  rtc_gpio_pullup_en(GPIO_NUM_32);
}

//BLE Connection und Listener auf Werte des Batteriecomputers und Solarreglers
void Setup_Votronic(){
  BLEDevice::init("");
  pServerAddress = new BLEAddress(HM_MAC);
  connectToServer(*pServerAddress);
}

//WLAN-Verbindung herstellen
void Setup_WiFi(){
  int WiFi_Connect_Versuche = 0;
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("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
    }
  }
  Serial.println("Verbunden");
}

void Send_ALL_Votronic_ISE_Values(){
  Write_ISE_ID_Value(ISE_Spannung_Aufbaubatterie, String(Spannung_Aufbaubatterie));
  Write_ISE_ID_Value(ISE_Spannung_Starterbatterie, String(Spannung_Starterbatterie));
  Write_ISE_ID_Value(ISE_Batterieladungsstatus, String(Batterieladungsstatus));
  Write_ISE_ID_Value(ISE_Strom_BC, String(Strom_BC));
  Write_ISE_ID_Value(ISE_Strom_SR, String(Strom_SR));
  Write_ISE_ID_Value(ISE_Ladung_SR_Wh, String(Ladung_SR_Wh));
  Write_ISE_ID_Value(ISE_Ladung_SR_Ah, String(Ladung_SR_Ah));
  Write_ISE_ID_Value(ISE_Solarstatus, String(Solarstatus));
  Write_ISE_ID_Value(ISE_HomeMatic_Befehl, "OK");
}

void setup() {
  Serial.begin(115200);
  Setup_GPIO();
  //Votronic auslesen
  if(digitalRead(GPIO_NUM_33)==0){
      Serial.println("Read Votronic BLE Connector");
      Setup_Votronic();
      Setup_WiFi();
     //der Raspberry Pi Zero möchte etwas sagen
      String Befehl = Read_ISE_ID_Value(ISE_HomeMatic_Befehl);
      Serial.println(Befehl);
      if(Befehl == "Votronic"){
        Send_ALL_Votronic_ISE_Values();
      }
  }
  //Unbenutzt (Soll später CAN-Nachrichten an die Zentralverriegelung senden)
  if(digitalRead(GPIO_NUM_33)==0){Serial.println("GPIO_NUM_33");}
  if(digitalRead(GPIO_NUM_25)==0){Serial.println("GPIO_NUM_25");}
  if(digitalRead(GPIO_NUM_26)==0){Serial.println("GPIO_NUM_26");}
  if(digitalRead(GPIO_NUM_27)==0){Serial.println("GPIO_NUM_27");}
  if(digitalRead(GPIO_NUM_14)==0){Serial.println("GPIO_NUM_14");}
  if(digitalRead(GPIO_NUM_12)==0){Serial.println("GPIO_NUM_12");}
  if(digitalRead(GPIO_NUM_13)==0){Serial.println("GPIO_NUM_13");}

  
  //Schlafenszeit bis GPIO32 Low
  Serial.println("ich gehen schlafen!!");
  esp_deep_sleep_start();
}

//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.println(payload);
        int StartVal = payload.indexOf("value='")+7;
        int EndVal = payload.indexOf("'",StartVal);
        Return_value = payload.substring(StartVal,EndVal);
      }
 
    else {
      Serial.println("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.println("ISE_Value: " + ISE_ID + " gesetzt:" + ISE_Value);
        }
        else{
          Serial.println("Write_ISE_ID_Value error");
        }
      }
    else {
      Serial.println("Error on HTTP request Homematic xmlapi");
    }
    http.end();
  }
}

4 Comments

  • Oliver sagt:

    Hallo, ich bin zwar kein Camper aber für mein Gartenhäusle habe ich die gelichen Gedanken gehabt. Ich verwende ebenfalls die Votronic Energy Monitor App Via Bluetooth Connector 1430. Stationär ist der LCD-Batterie-Computer mit Smart Shunt und LCD-Solar Computer ausreichend. Aber eine Fernwartung bzw. Überwachung wäre mir lieber. Deshalb ein großes Kompliment an die Vorarbeit. Ich würde das Projekt gerne ebenfalls umsetzen und deshalb die Frage welche Komponenten werden dafür benötigt? Gruß Oliver

    • Matthias Harzheim sagt:

      Hallo Oliver,

      um es so umzusetzen wie hier beschrieben, benötigst du den Raspberry Pi Zero, mit der Raspberrymatic-Installation, sowie einen ESP32 und ein GSM WLAN Router. Eigentlich musst du alles so aufsetzten wie hier beschrieben: https://cumulumbus.de/smart-home-im-camper-van-smart-camper/ (außer den 4 Kanal Schaltaktor). Dann musst du auf den ESP32 den Code aus dem Votronic Artikel flashen.

      Wenn du vor Ort schon WLAN hast, kannst du dir auch überlegen, nur den ESP32 zu nehmen und darauf einen kleinen Webserver zu betreiben, der dir die Daten die mein Code ausliest immer anzeigt. Das ist wenn du nichts weiter einbinden willst die billigste Lösung. Im Internet findet man ganz viele Beispiele wie man mit einem ESP32 Sensordaten über einen Webserver senden kann.

      Viel Spaß beim Basteln!

  • Niklas sagt:

    Hallo Matthias,

    ich bin gerade zufällig über deinen Artikel gestolpert.
    Meine Idee war bisher einen ESP32 für einen eigenen Batteriemonitor zu nutzen, allerdings hast du mich auf eine Idee gebracht, mich würden deine Gedanken interessieren.
    Ich habe bereits den Votronic Shunt und hatte die Idee (da ich das BT Modul nicht habe) am ESP32 per Datenkabel den Shunt auszulesen.
    Meinst du, das wäre möglich und somit eine Alternative zum BT Modul?

    Danke und beste Grüße
    Niklas

    • Matthias Harzheim sagt:

      Hallo Niklas,

      Ich habe nur Bluetooth verwendet, da ich es schon im Camper hatte. Sonst hätte ich auch den esp32 direkt an den Shunt angeschlossen. Dieser kommuniziert glaube ich über RS485 mit dem Anzeigemodul. Ich vermute es ist nicht schwer genauso wie bei Bluetooth herauszufinden, wie die Kommunikation aufgebaut ist. Ich hatte mir auch schon Mal einen RS485 zu TTL Adapter gekauft. Leider hatte ich dann keine Zeit um mir das anzuschauen. Aber wenn du hier weiterkommst, kannst du gerne deine Errungenschaften hier teilen.

      Grüße und viel Erfolg,

      Matthias

Leave a Reply