Benachrichtigungen
Alles löschen

Growatt Wechselrichter mit ESPHome auslesen

80 Beiträge
29 Benutzer
3 Likes
16.5 K Ansichten
(@mathschut)
Vorsichtiger Stromfühler
Beigetreten: Vor 1 Jahr
Beiträge: 7
 

@rattenfaenger Hi, wie hast du den Code auf einem Wemos D1 Mini zum laufen bekommen, ich bekomme da nur Fehler beim schreiben der Config.


   
AntwortZitat
(@caberliner)
Vorsichtiger Stromfühler
Beigetreten: Vor 11 Monaten
Beiträge: 8
 

So, ich habe meinen Code jetzt ja schon fast ein Jahr am Laufen und geb mal eine Art Langzeitbericht ab (und wie es scheint muss ich den Beitrag splitten damit er richtig angezeigt wird).
Zu meinen Spezifikationen:
- Growatt MIC 2000
- Viessmann BHKW
- Kostal AC PlenticoreBi mit 5 kWh BYD HVS
- KSEM

Hauptenergiegeber im Winter ist natürlich das BHKW mit 0,75 kWh, im Sommer ist es dann der MIC, da ich das BHKW meistens spätestens ab Mitte Mai/Juni mangels Wärmebedarf herunterfahre.
Das Viessmann BHKW (Vitovalor PT2) ist zZt überhaupt nicht per Modbus oder ähnlichem von außen ansteuerbar, da es auf die neueste Viessmann WLAN Schnittstelle setzt. Und hier habe ich dann blöderweise einen totalen Vendor Lock-In kombiniert mit einem sauschlechtem und störrischem Viessmann Service. Fazit für andere - nicht kaufen -. Ich hänge jetzt erstmal mit dem Teil fest bis es abgeschrieben ist.
Was macht das BHKW wenn es Strom produziert: Es speist stumpf AC-seitig ein, eine wirkliche Steuerung oder smarte Bedarfsdeckung (obwohl von Viessmann behauptet) konnte ich seit Inbetriebnahme vor fünf Jahren nicht feststellen. Dh häufig viel Überschuss der verpufft und falls mals Bedarf da ist kommt man in das 0,75 kWh Limit. Heißt einmal Herd an und man muss dann trotzdem fast alles dazukaufen.
Daher jetzt der Kostal PlenticoreBi mit Batterie. Warum 5 kWh Speicher? Beim Ordern hatte ich nicht mehr Euronen frei, sobald ich flüssig bin, werden 1-2 Blöcke dazu geordert. Das KSEM (Kostal Smart Meter G2) ist quasi mit dem PlenticoreBi verheiratet. Die Lösung soll überschüssigen STrom natürlich Ein- und Ausspeichern und hauptsächlich das BHKW für mich effizienter machen.
Und noch den Growatt MIC2000 mit aktuell nur 840 WPeak als quasi Balkonkraftwerk, noch limitiert auf 600 W, ab Unterschrift Steinmeier kann ich ja etwas hochgehen. (Stichwort Netzbetreiber, 2 Anlagen, Einspeisung...hier würde es jetzt kompliziert werden). Der 2000er läuft hier, damit ich auch zeitnah auf 2000 WPeak Verplattung hochgehen kann. Ich weiß, bei der Limitierung ist Effizienz eigentlich auch das Stichwort. Doch es ist ein Just for fun Projekt.
Alle Geräte sind an eine Home Assistant Instanz angebunden und mehr oder weniger Smart miteinander verschaltet.
Erkenntnisse aus meinem System:
1. Growatt per ESP steuern funktioniert perfekt, aber auch hier lernt man dazu.
2. Nie wieder Viessmann.
3. Falls ich einen neuen AC-Wechselrichter brauche, das nächste Mal vlt. auch Growatt, hier hat man wirklich Vollzugriff (hatte allerdings keine Lust auf einen Sommer Frickelei).
4. Die Kostal Software entspricht dem IT-Entwicklungsstand in unserem Land (traurig schlecht für einen Hersteller der sonst sehr robuste Geräte baut).

Größte Erleuchtung für den ESP-Code (gestern am 30.04.2024 nach fast einem Jahr Betrieb): Ich weiß nicht, ob das auch von euch beobachtet wurde, aber mein Code hat immer wieder unerklärliche Abregulierungen des Wechselrichters produziert, sichtbar im Verbrauchslog als plötzliche Spikes Richtung 0. Mir war das trotz Suche und Optimierungen im System schleierhaft. Gestern hatte ich es über einen längeren Zeitraum bei voller, praller Mittagssonne und hohem Stromverbrauch (größer 3500 kW, WaMa + Trockner + Spülmaschine + Rasenmähen). Und zack geht der Growatt plötzlich voll auf 0.
Daraufhin habe ich mir einmal das ESP-log in Echtzeit zu den Werten anschauen können und dabei festgestellt, dass er trotz des hohen Bedarfs negative "adjusted sensor values" errechnete.
Wo lag der Fehler: Ich hatte lange Tomaten auf den Augen, aber nun noch einmal die Definition der Variable in C (16-bit signed integer type is used to store negativ or positiv whole number. 16-bit integer and his value range: from -32768 to 32767). Ergo hatte ich die ganze Zeit einen Data overflow Error im Code, da ich in Deziwatt rechne. Die Dropdowns kommen also immer, wenn der "adjusted value" über 32767 Deziwatt ging. Was natürlich bei einem Hausverbrauch größer 3500 kW der Fall war. Simple Lösung 32 Bit Variable eingesetzt und Problem gelöst (32-bit integer and his value range: from -2147483648 to 2147483647). 2147483647 dW werde ich wohl im Haus nicht erreichen ;).

Diese r Beitrag wurde geändert Vor 4 Tagen von caberliner

   
AntwortZitat
(@caberliner)
Vorsichtiger Stromfühler
Beigetreten: Vor 11 Monaten
Beiträge: 8
 

Und falls es wem hilft, hier mein kompletter ESP-Code für den Shine Wifi-X (as is, ich müsste ihn mal noch aufräumen):

 Part 1:

# Growatt MIC600 ShineWifi-X to HomeAssistant via EspHome
# based on   Link entfernt  
# 14.10.2022 rkr                         V1.0
# 19.02.2023 rkr power factor working    V1.1

substitutions:
  orientation:   "Garten"  # change to whatever you want
  l_orientation: "garten"  # lower case name
  fritz: growattesphome

  device_description: "Growatt Solar Inverter ${orientation}nseite" ## adapt to your needs
  friendly_name:  "Growatt Solar Inverter ${orientation}"
  devicename: "Solar ${orientation}"
 
  publish_interval: 5s
  update_realtime: 1s
 
esphome:
    name: growatt-${l_orientation}
    comment: "Growatt MIC600 Wechselrichter Garten"
    friendly_name: Keller Wechselrichter
      
esp8266:
    board: esp07s
    restore_from_flash: true
  
  # Enable logging
logger:
    baud_rate: 0
  # Enable Home Assistant API
api:
    encryption:
     key: !secret api_growatt

web_server: ## can use when OTA does not work
  port: 80

ota:
    password: !secret esphome_api_password
  
wifi:
    ssid: !secret wifi_iot_ssid
    password: !secret wifi_iot_password
    use_address: ${fritz}.fritz.box
  
    # Enable fallback hotspot (captive portal) in case wifi connection fails
    ap:
      ssid: "${devicename} Fallback Hotspot"
      password: !secret wifi_iot_password

preferences:
  flash_write_interval: 10min

# Use three global variables to store the last three received power values
globals:
  - id: power_t0  # curent power value
    type: float
    restore_value: no
    initial_value: '0.0'
  - id: powerOffset_average  # average power value
    type: float
    restore_value: no
    initial_value: '0.0'
  - id: GrowattPower
    type: float
    initial_value: '0.0'
  - id: GrowDCPower
    type: float
    restore_value: no
    initial_value: '0.0'
  - id: global_relOutputPower ## needed for HA sensor
    type: int
  - id: global_calOutputPower ## needed for HA sensor
    type: int
  - id: last_selected_interval
    type: int
    restore_value: true
    initial_value: "5"

captive_portal:

time:
  - platform: homeassistant
    id: homeassistant_time

output:
# Blue Led
  - id: light_bl
    platform: gpio
    pin: 16
# Green Led
  - id: light_gr
    platform: gpio
    pin: 0
# Red Led
  - id: light_rd
    platform: gpio
    pin: 2

uart:
  id: mod_bus
  tx_pin: 1
  rx_pin: 3
  baud_rate: 115200
  
modbus:
  id: modbus1
  uart_id: mod_bus
  
#  flow_control_pin: GPIO4
  
modbus_controller:
  - id: growatt
# the Modbus device addr
    address: 0x1
    update_interval: ${update_realtime}  # use the substitution variable for the update interval
    modbus_id: modbus1
    setup_priority: -10  

button:
  - platform: restart
    name: "${devicename} Restart"

text_sensor:
  - platform: modbus_controller
    name: "${devicename} Firmware Version"
    address: 9
    force_new_range: True
    register_count: 3
    register_type: holding
    #internal: true
    entity_category: diagnostic

  - platform: modbus_controller
    name: "${devicename} Control Firmware"
    address: 12
    register_count: 3
    force_new_range: True
    register_type: holding
    #internal: true
    entity_category: diagnostic

  - platform: template
    name: "${devicename} Status"
    icon: mdi:eye
    entity_category: diagnostic
    lambda: |-
      if (id(status).state == 1) {
        return {"Normal"};
      } else if (id(status).state == 0)  {
        return {"Waiting"};
      } else {
        return {"Fault!"};
      }
switch:
  - platform: modbus_controller
    name: "${devicename} OnOff"
    skip_updates: 5
    address: 0
    register_type: holding

sensor:
  - platform: modbus_controller
    modbus_controller_id: growatt
    address: 0
    register_type: "read"
    accuracy_decimals: 0
    internal: true
    id: status
Diese r Beitrag wurde geändert Vor 4 Tagen von caberliner

   
AntwortZitat
(@caberliner)
Vorsichtiger Stromfühler
Beigetreten: Vor 11 Monaten
Beiträge: 8
 

Part 2 (ich komme beim Code auch schon ins Zeichenlimit)

 

  - platform: modbus_controller
    id: growatt_dc_power
    modbus_controller_id: growatt
    name: "${devicename} DcPower"
    address: 5
    register_type: "read"
    unit_of_measurement: W
    device_class: power
    state_class: measurement
    icon: mdi:solar-power-variant
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
        
  - platform: modbus_controller
    id: growatt_dc_voltage
    modbus_controller_id: growatt
    name: "${devicename} DcVoltage"
    address: 3
    register_type: "read"
    unit_of_measurement: V
    device_class: voltage
    state_class: measurement
    icon: mdi:solar-power-variant
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    
  - platform: modbus_controller
    id: growatt_dc_curennt
    modbus_controller_id: growatt
    name: "${devicename} DcInputCurrent"
    address: 4
    register_type: "read"
    unit_of_measurement: A
    device_class: current
    state_class: measurement
    icon: mdi:solar-power-variant
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    
  - platform: modbus_controller
    name: "${devicename} AcFrequency"
    address: 37
    register_type: "read"
    unit_of_measurement: Hz
    icon: mdi:current-ac
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.01
  
  - platform: modbus_controller
    name: "${devicename} AcVoltage"
    skip_updates: 9
    address: 38
    register_type: "read"
    unit_of_measurement: V
    device_class: voltage
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
  
  - platform: modbus_controller
    name: "${devicename} AcOutputCurrent"
    address: 39
    register_type: "read"
    unit_of_measurement: A
    device_class: current
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    
  - platform: modbus_controller
    name: "${devicename} AcPower"
    id: ac_output_power
    address: 35
#    address: 40
    register_type: "read"
    unit_of_measurement: W
    device_class: power
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    on_value:
      then:
        - globals.set:
            id: GrowattPower
            value: !lambda 'return float(x);'
    
  - platform: modbus_controller
    id: growatt_energy_today 
    modbus_controller_id: growatt
    name: "${devicename} EnergyToday"
    address: 53
    register_type: "read"
    unit_of_measurement: kWh
    device_class: energy
    state_class: total_increasing
    icon: mdi:solar-power
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1
    
  - platform: modbus_controller
    name: "${devicename} EnergyTotal"
    address: 55
    register_type: "read"
    unit_of_measurement: kWh
    state_class: total_increasing
    device_class: energy
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1

  - platform: modbus_controller
    name: "${devicename} Inverter Temperature"
    address: 93
    register_type: "read"
    unit_of_measurement: C
    device_class: temperature
    icon: mdi:thermometer
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1

  - platform: modbus_controller
    name: "The inside IPM in inverter Temperature"
    address: 94
    register_type: "read"
    unit_of_measurement: C
    device_class: temperature
    icon: mdi:thermometer
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
    - multiply: 0.1

   
AntwortZitat
(@caberliner)
Vorsichtiger Stromfühler
Beigetreten: Vor 11 Monaten
Beiträge: 8
 

Und Part 3:

 

  - platform: homeassistant
    name: "Total Momentanleistung from Home Assistant"
    entity_id: sensor.strom_ges   ## Enter the sensor name for your current consumption
    on_value:
      then:
        - logger.log:
            level: DEBUG
            format: 'Power value changed from %d to %d Deziwatt [dW]'
            args: ['id(power_t0)', 'int(x*10.0)']
        - globals.set:
            id: power_t0
            value: !lambda 'return int(x*10.0);'

  - platform: homeassistant
    name: "Average Consumption from Home Assistant"
    entity_id: sensor.average_power   ## Enter the sensor name for your average consumption
    on_value:
      then:
        - logger.log:
            level: DEBUG
            format: 'Average Power changed from %d to %d Deziwatt [dW]'
            args: ['id(powerOffset_average)', 'int(x*10.0)']
        - globals.set:
            id: powerOffset_average
            value: !lambda 'return int(x*10.0);'


  - platform: total_daily_energy
    name: "${devicename} Total Daily Energy"
    power_id: ac_output_power
    unit_of_measurement: kWh
    device_class: energy
    state_class: total_increasing
    icon: mdi:solar-power
    accuracy_decimals: 2
    restore: true
    filters:
      - multiply: 0.001   

## Variante aggressive Einspeisung
interval:
  - interval: 1s
    then:
      - lambda: !lambda |-
          ESP_LOGD("main", "Power_t0: %f ", id(power_t0));
          static int16_t powerOffset = 7000;           // Export offset (in dW)
          static int16_t powerOffset2 = 5000;          // Export offset (in dW)

          int32_t adjustedSensorValue = 0;   // important to use 32 bit
          
          adjustedSensorValue = (id(power_t0) < -powerOffset) ? 0 : (id(power_t0) >= -powerOffset && id(power_t0) < 0) ? id(power_t0) + powerOffset : (id(power_t0) < 0 && id(power_t0) > -powerOffset) ? -powerOffset - id(power_t0) : id(power_t0) + id(powerOffset_average) + powerOffset2;

          ESP_LOGD("main", "Adjusted Sensor Value: %d ", adjustedSensorValue);

          // Weiter mit den Berechnungen ohne Berücksichtigung von GrowattPower
          static int16_t powerMax = 20000;             
          static int16_t relOutputPower = 10;
          static int16_t calOutputPower = 0;
          static int16_t consumePower = 0;
          static int16_t PowerSet = 0;
          
          if (adjustedSensorValue > 0) {
              relOutputPower = adjustedSensorValue / 100.0;
              calOutputPower = adjustedSensorValue / 100.0;
              consumePower = adjustedSensorValue / 10.0;
              if (relOutputPower > 100) relOutputPower = 100.0; // Maximum should always be 100%
              if (relOutputPower < 0) relOutputPower = 0;       // Ensure non-negative value
              PowerSet = relOutputPower / 1000.0 * powerMax;     // Convert to DezidWatt (dW)

          } else {
              relOutputPower = 0;
              calOutputPower = 0;
              consumePower = 0;
              PowerSet = 0;
          }

          ESP_LOGD("main", "Relative Output Power: %d percent", relOutputPower);
          ESP_LOGD("main", "Calculated Output Power: %d percent", calOutputPower);
          ESP_LOGD("main", "Set Output Power to: %d Watt", PowerSet);
          ESP_LOGD("main", "Real Consumed Power: %d Watt", consumePower);

          esphome::modbus_controller::ModbusController *controller = id(growatt);
          uint16_t reg = 3;
          modbus_controller::ModbusCommandItem setOutputPower_command = modbus_controller::ModbusCommandItem::create_write_single_command(controller, reg, relOutputPower);
          controller->queue_command(setOutputPower_command);

   
AntwortZitat
Seite 6 / 6
Teilen: