Makeskyblue V119 Wifi Laderegler auslesen

V125 hat ein anderes Protokoll als V118 / 119. Es wird nicht funktionieren. Ich kann es nicht testen, da ich kein Gerät habe.

in der aktuellen Tasmota Changelog v15.2.0 ist von einem:

Support for MakeSkyBlue Solar Charger Energy Monitor #24151

die Rede. Leider finde ich nirgends weitere Informationen welche Firmwareversionen (V118…V125) unterstützt werden bzw. wie die Verkabelung oder Einrichtung zu erfolgen hat.

1 „Gefällt mir“

Hallo,

ich habe in der Zwischenzeit ein bisschen rumgespielt, um den MSB V118 auszulesen.

Meine Implementierung umfasst auch einen AM2301 Klimasensor, da ich auch Temperatur und Luftfeuchte überwachen möchte. Ein anderer Sensor wäre vermutlich besser geeignet, aber die Werte sind brauchbar.

Leider konnte ich dem Regler nicht die Nachkommastelle von “Total Production” entlocken.

Inzwischen nutze ich den ESP8266 unter ESPHome.

Viele Grüße
Christian

esphome:

name: makeskyblue-v118

comment: MakeSkyBlue V118 + DHT21 (AM2301) via ESPHome

esp8266:

board: nodemcuv2

logger:

 baud_rate: 0

 level: DEBUG

 logs:

   sensor: INFO

   uart: DEBUG

wifi:

ssid: !secret wifi_ssid

password: !secret wifi_password

min_auth_mode: WPA2

ap:

ssid: "MSB-V118-Fallback"

password: !secret fallback_password

captive_portal:

api:

encryption:

key: !secret api_key

reboot_timeout: 15min # Neustart nach 15 Min ohne API

ota:

  • platform: esphome

    password: !secret ota_password

web_server:

port: 80

uart:

id: v118_uart

baud_rate: 9600

rx_pin: RX

tx_pin: TX

rx_buffer_size: 512

sensor:

# --- DHT21 / AM2301 ---

  • platform: dht

    pin: D5

    model: DHT22

    temperature:

    name: "MSB Umgebungstemperatur"

    id: amb_temp

    device_class: temperature

    state_class: measurement

    accuracy_decimals: 1

    humidity:

    name: "MSB Luftfeuchtigkeit"

    id: amb_hum

    device_class: humidity

    state_class: measurement

    accuracy_decimals: 0

    update_interval: 30s

# --- V118 Sensoren (Templates werden im Lambda befüllt) ---

  • platform: template

    name: "MSB Akku Spannung"

    id: batt_voltage

    unit_of_measurement: "V"

    device_class: voltage

    state_class: measurement

    accuracy_decimals: 1

  • platform: template

    name: "MSB Akku Strom"

    id: batt_current

    unit_of_measurement: "A"

    device_class: current

    state_class: measurement

    accuracy_decimals: 1

  • platform: template

    name: "MSB PV Spannung"

    id: pv_voltage

    unit_of_measurement: "V"

    device_class: voltage

    state_class: measurement

    accuracy_decimals: 1

  • platform: template

    name: "MSB PV Leistung"

    id: pv_power

    unit_of_measurement: "W"

    device_class: power

    state_class: measurement

    accuracy_decimals: 0

  • platform: template

    name: "MSB PV Energie"

    id: pv_energy

    accuracy_decimals: 0

    unit_of_measurement: "kWh"

    device_class: energy

    state_class: total_increasing

  • platform: template

    name: "MSB Regler Temperatur"

    id: ctrl_temp

    unit_of_measurement: "°C"

    device_class: temperature

    state_class: measurement

    accuracy_decimals: 1

  • platform: integration

    name: "MSB PV-Erzeugung"

    sensor: pv_power

    id: v_power_cum

    time_unit: h

    unit_of_measurement: "kWh"

    device_class: energy

    state_class: total_increasing

    accuracy_decimals: 3

    integration_method: left

    filters:

    • multiply: 0.001

    restore: true

  • platform: template

    name: "MSB Code 12"

    id: error_byte12

    accuracy_decimals: 0

    state_class: measurement

    entity_category: diagnostic

  • platform: template

    name: "MSB Code 13"

    id: error_byte13

    accuracy_decimals: 0

    state_class: measurement

    entity_category: diagnostic

  • platform: template

    name: "MSB Statusflag"

    id: status_byte15

    accuracy_decimals: 0

    state_class: measurement

    entity_category: diagnostic

# --- ALLE TEXT SENSOREN IN EINEM BLOCK ---

text_sensor:

  • platform: template

    name: "MSB Fehler"

    id: error_byte12_text

    entity_category: diagnostic

  • platform: template

    name: "MSB Lademodus"

    id: charger_mode

    icon: "mdi:battery-charging"

    entity_category: diagnostic

  • platform: template

    name: "MSB Code 13 Status"

    id: error_byte13_text

    entity_category: diagnostic

time:

  • platform: sntp

    id: sntp_time

    timezone: "Europe/Berlin"

    on_time:

    • seconds: 0

      minutes: 0

      hours: 3

      then:

      • sensor.integration.reset: v_power_cum

      • logger.log: "Energiezähler um Mitternacht zurückgesetzt und NodeMCU neu gestartet."

      • delay: 3s

      • lambda: |-

        App.safe_reboot();

sun:

latitude: 49.44

longitude: 7.74

interval:

  • interval: 10s

    then:

    • lambda: |-

      // Buffer komplett leeren vor neuer Anfrage

      while (id(v118_uart).available()) {

      uint8_t dummy;
      
      id(v118_uart).read_byte(&dummy);
      

      }

    # Anfrage senden

    • uart.write:

      id: v118_uart

      data: [0xAA, 0x55, 0x00, 0x00, 0x00, 0x55]

    # WICHTIG: Warten auf Antwort

    • delay: 350ms

    # Antwort verarbeiten

    • lambda: |-

      static uint8_t buf[18];

      uint8_t b;

      static float last_energy = -1;

      int attempts = 0;

      bool found_header = false;

      // Timeout-Schutz: max 50 Bytes durchsuchen

      while (id(v118_uart).available() > 0 && attempts < 50) {

      attempts++;
      
      
      
      // Suche nach 0xAA Header
      
      if (!id(v118_uart).read_byte(&b)) {
      
        ESP_LOGW("MSB", "Lesefehler beim Header-Suchen");
      
        break;
      
      }
      
      
      
      if (b == 0xAA) {
      
        // Prüfe ob genug Daten für komplette Nachricht vorhanden
      
        if (id(v118_uart).available() < 19) {
      
          delay(10);
      
        }
      
        if (id(v118_uart).available() < 19) {
      
          ESP_LOGW("MSB", "Nicht genug Daten nach Header");
      
          break;
      
        }
      
      
      
        // Header 2: 0xBB
      
        if (!id(v118_uart).read_byte(&b) || b != 0xBB) {
      
          ESP_LOGW("MSB", "Ungültiger zweiter Header: 0x%02X", b);
      
          continue; // Weitersuchen
      
        }
      
        
      
        found_header = true;
      
        break;
      
      }
      

      }

      static uint8_t fail_count = 0;

      if (!found_header) {

      fail_count++;
      
      if (fail_count >= 3) {
      
        ESP_LOGW("MSB", "Kein gültiger Header gefunden (%d)", fail_count);
      
        fail_count = 0;
      
      }
      
      return;
      

      }

      fail_count = 0;

      // Payload lesen

      for (int i = 0; i < 18; i++) {

      if (!id(v118_uart).read_byte(&buf\[i\])) {
      
        ESP_LOGW("MSB", "Lesefehler bei Byte %d", i);
      
        return;
      
      }
      

      }

      // Checksumme prüfen

      int checksum = 0xBB;

      for (int i = 0; i < 17; i++) checksum += buf[i];

      if ((checksum & 0xFF) != buf[17]) {

      ESP_LOGW("MSB", "Checksumme fehlerhaft (erwartet 0x%02X, erhalten 0x%02X)", 
      
               checksum & 0xFF, buf\[17\]);
      
      return;
      

      }

      ESP_LOGD("MSB", "Raw data - B12:%d B13:%d B14:%d B15:%d", buf[12], buf[13], buf[14], buf[15]);

      // Ab hier dein bestehender Code für Mapping und Sensoren

      int battV = buf[0] | (buf[1] << 8);

      int battA = buf[2] | (buf[3] << 8);

      int pvV = buf[4] | (buf[5] << 8);

      int pvW = buf[6] | (buf[7] << 8);

      int temp = buf[8] | (buf[9] << 8);

      int ener_raw = buf[10] | (buf[11] << 8);

      int byte12 = buf[12];

      int byte13 = buf[13];

      int mode_raw = buf[14];

      int byte15 = buf[15];

      id(batt_voltage).publish_state(battV * 0.1f);

      id(batt_current).publish_state(battA * 0.1f);

      id(pv_voltage).publish_state(pvV * 0.1f);

      id(pv_power).publish_state(pvW);

      id(ctrl_temp).publish_state(temp * 0.1f);

      id(error_byte12).publish_state(byte12);

      id(error_byte13).publish_state(byte13);

      id(status_byte15).publish_state(byte15);

      // Fehlertext

      switch (byte12) {

      case 0:  id(error_byte12_text).publish_state("OK"); break;
      
      case 18: id(error_byte12_text).publish_state("Solar-Eingangsspannung zu niedrig"); break;
      
      case 60: id(error_byte12_text).publish_state("Übertemperatur"); break;
      
      case 63: id(error_byte12_text).publish_state("Batteriespannung zu hoch"); break;
      
      case 65: id(error_byte12_text).publish_state("Batteriespannung zu gering"); break;
      
      case 71: id(error_byte12_text).publish_state("Solar-Eingangsspannung zu hoch"); break;
      
      case 73: id(error_byte12_text).publish_state("Ladestrom zu hoch"); break;
      
      case 74: id(error_byte12_text).publish_state("Kurzschluss Lastausgang"); break;
      
      default: {
      
        char txt\[16\];
      
        sprintf(txt, "Code %d", byte12);
      
        id(error_byte12_text).publish_state(txt);
      
      }
      

      }

      // Energie (Spike-Schutz)

      float e = (float)ener_raw;

      if (last_energy < 0 || (e >= last_energy && e - last_energy < 2.0f)) {

      id(pv_energy).publish_state(e);
      
      last_energy = e;
      

      }

      static int last_mode = -1;

      if (mode_raw != last_mode) {

      last_mode = mode_raw;
      
      
      
      if      (mode_raw == 0) id(charger_mode).publish_state("Standby");
      
      else if (mode_raw == 4) id(charger_mode).publish_state("Hauptladung");
      
      else if (mode_raw == 3) id(charger_mode).publish_state("Absorption");
      
      else if (mode_raw == 6) id(charger_mode).publish_state("Float");
      
      else {
      
        char m\[12\];
      
        sprintf(m, "Code %d", mode_raw);
      
        id(charger_mode).publish_state(m);
      
      }
      

      }

      ESP_LOGD("MSB", "Erfolgreich gelesen - PV: %.1fV, %.0fW", pvV*0.1f, (float)pvW);