Growatt Wechselrichter mit ESPHome auslesen

@devil26 Dieses esphome yaml reizt mich sehr zum ausprobieren. Ich habe einen Stromzähler der Einspeise- und Verbrauchsleistung angibt. In Homeassistant habe ich mir einen kombinierten Sensor für die Momentanleistung erstellt. Da ich noch ein BHKW mit Stromproduktion besitze kommen bei mir natürlich auch negative Werte vor. Reicht es dafür einen weiteren edge case zu definieren? Wobei durch die Berechnung hier: relOutputPower=1 einer Momentanleistung von 0 Watt entspricht, oder? (Growatt MIC 1000TL-X)

 if (relOutputPower < 1) relOutputPower = 0; // Minimum should be allways 0 %

Also hier jetzt meine adaptierte Version. Ich muss das Ganze noch testen, die Anlage geht erst live, das primäre logging sieht aber schon ganz gut aus. Feedback gerne willkommen.

# 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}"
esphome:
    name: growatt-${l_orientation}
    comment: "Growatt MIC600 Wechselrichter Garten"
    project:
      name: "raka.shinewifi_x_sensor"
      version: "23.2.19"

esp8266:
board: esp07s

Enable logging

logger:
baud_rate: 0

Enable Home Assistant API

api:
encryption:
key: "yourkeyhere"
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: "Yourpassword"

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: GrowattPower
    type: float
    initial_value: '0.0'
  • id: global_relOutputPower ## needed for HA sensor
    type: int
  • id: global_calOutputPower ## needed for HA sensor
    type: int
    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

modbus_controller:

  • id: growatt

the Modbus device addr

address: 0x1
modbus_id: modbus1
setup_priority: -10  

sensor:

  • platform: modbus_controller
    name: "${devicename} DcPower"
    address: 5
    register_type: "read"
    unit_of_measurement: W
    device_class: power
    icon: mdi:flash
    value_type: U_DWORD
    accuracy_decimals: 1
    filters:

    • multiply: 0.1
  • platform: modbus_controller
    name: "${devicename} DcVoltage"
    address: 3
    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} DcInputCurrent"
    address: 4
    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} AcFrequency"
    address: 37
    register_type: "read"
    unit_of_measurement: Hz
    icon: mdi:flash
    value_type: U_WORD
    accuracy_decimals: 1
    filters:

    • multiply: 0.01
  • platform: modbus_controller
    name: "${devicename} AcVoltage"
    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"
    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
    name: "${devicename} EnergyToday"
    address: 53
    register_type: "read"
    unit_of_measurement: kWh
    device_class: energy
    icon: mdi:flash
    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} 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: homeassistant
    name: "Total Momentanleistung from Home Assistant"
    entity_id: sensor.yoursensor ## 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(x10.0)']
    - globals.set:
    id: power_t0
    value: !lambda 'return int(x
    10.0);'

  • platform: template
    name: "Relative Output Power"
    device_class: power
    unit_of_measurement: "W"
    state_class: "measurement"
    accuracy_decimals: 0
    lambda: |-
    return id(global_relOutputPower);

  • platform: template
    name: "Calculated Output Power"
    device_class: power
    unit_of_measurement: "W"
    state_class: "measurement"
    accuracy_decimals: 0
    lambda: |-
    return id(global_calOutputPower);
    interval:

  • interval: 10s
    then:

    • lambda: |-
      ESP_LOGD("main", "Growatt AC Power: %f ", id(GrowattPower));
      static int16_t powerOffset = 500; // Export offset (-50 W)
      static int16_t powerMax = 6000; // 6000 dW = 600 W (if Growatt MIC 600TL-X)
      static int16_t relOutputPower = 10; // Max output active power (0 - 100 %) // 10 % => ~60 W (if Growatt MIC 600TL-X)
      static int16_t calOutputPower = 0;
      static int16_t consumePower = 0;
      static int16_t GroPower = 0;
      static int16_t PowerSet = 0;
      static float global_relOutputPower = 0;
      static float global_calOutputPower = 0;
      relOutputPower = id(power_t0) / powerMax * 100.0;
      global_relOutputPower = relOutputPower;
      calOutputPower = id(power_t0) / powerMax * 100.0;
      global_calOutputPower = calOutputPower;
      consumePower = id(power_t0) / 10.0;
      PowerSet = relOutputPower / 1000.0 * powerMax;
      if (relOutputPower > 100) relOutputPower = 100; // Maximum should allways be 100 %
      if (relOutputPower < 0) relOutputPower = 0; // Ensure non-negative value
      PowerSet = relOutputPower / 1000.0 * powerMax;

      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; // Register: Max output active power (in %)
      modbus_controller::ModbusCommandItem setOutputPower_command = modbus_controller::ModbusCommandItem::create_write_single_command(controller, reg, relOutputPower);
      controller->queue_command(setOutputPower_command);

So per Mqtt kann ich nun den wert steuern hier mein Code

- platform: mqtt_subscribe
name: "mqtttest"
id: mqtttest
topic: the/topic
on_value:
then:
- lambda: |-
esphome::modbus_controller::ModbusController *controller = id(growatt);
uint16_t reg = 3; // Register: Max output active power (in %)
float value = id(mqtttest).state;
uint16_t register_value = static_cast(value);
modbus_controller::ModbusCommandItem setOutputPower_command = modbus_controller::ModbusCommandItem::create_write_single_command(controller, reg, register_value);
controller->queue_command(setOutputPower_command);

Mal ne Frage:

ACPower scheint bei mir beim MOD10KTL-X irgendwie nur eine Phase zu sein, gibt es den Wert auch für die anderen drei Phasen bzw. einen Gesamtwert der Abgabe?

Mal ne Frage:

ACPower scheint bei mir beim MOD10KTL-X irgendwie nur eine Phase zu sein, gibt es den Wert auch für die anderen zwei Phasen bzw. einen Gesamtwert der Abgabe?

@querys moin probiere mal Adresse 3023

Erstmal danke für die Mühe für den Lambda call code.

Hier der Berichtigte Code, der Original Code hatte den Fehler das nur die Leistung vom Smart Meter eingespeist wurde und die Leistung vom WR nicht.

Das hatte zur folge das bei 300W verbrauch und 150W vom WR noch 150W Bezug Übrig blieben. Nun regelt er Sauber auf 0 +/- 20W.

Das Power Offset ist noch nicht implementiert und ist ohne Funktion.

- platform: homeassistant
name: "Total Power from SmartMeter"
entity_id: sensor.sdm72d_m_system_power_total # Power Entity from SmartMeter
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);'
interval:
- interval: 1s
then:
- lambda: !lambda |-
ESP_LOGD("main", "Growatt AC Power: %f ", id(GrowattPower));
int16_t powerOffset = 500; // Export offset (-50 W)
int16_t powerMax = 10000; // 6000 dW = 600 W (if Growatt MIC 600TL-X)
static int16_t relOutputPower = 10; // Max output active power (0 - 100 %) // 10 % => ~60 W (if Growatt MIC 600TL-X)
static int16_t calOutputPower = 0;
static int16_t consumePower = 0;
static int16_t GroPower = 0;
static int16_t PowerSet = 0;
relOutputPower = id(power_t0)/100 + id(GrowattPower) / powerMax * 1000;
calOutputPower = id(power_t0)/100 + id(GrowattPower) / powerMax * 1000;
consumePower = id(power_t0)/10 + id(GrowattPower);
PowerSet = relOutputPower / 1000.0 * powerMax;
if (relOutputPower > 100) relOutputPower = 100.0; // Maximum should be allways 100 %
if (relOutputPower < 0) relOutputPower = 0; // Ensure non-negative value
PowerSet = relOutputPower / 1000.0 * powerMax;
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; // Register: Max output active power (in %)
modbus_controller::ModbusCommandItem setOutputPower_command = modbus_controller::ModbusCommandItem::create_write_single_command(controller, reg, relOutputPower);
controller->queue_command(setOutputPower_command);

@mummy Danke für deinen Tipp. Der hat zwar leider nicht funktioniert, aber ich habe mich nochmal genauer eingelesen.

Ich hatte irgendwie die Konfig für einen 1-Phasigen Growatt als Vorlage verwendet, das passt natürlich nicht. Mein 3-Phasen WR mit auch 2 Strings hat entsprechend mehr Möglichkeiten.

Die korrekte Adresse für Total AC ist übrigens "address: 35"

@querys guten Morgen,

Ja Adresse 35 benutze ich auch . Du hast wahrscheinlich noch eine alte Firmware auf dem WR. Für die TL-X sind eigentlich die Register ab 3000 gedacht. Aber die anderen funktionieren auch.

Keine Ahnung warum hier Adresse 40 genommen wird. Ist ja VA und 35 ist ja Watt.

Mfg

Mummy

Warum regelt ihr eigentlich alle runter? Hat das einen bestimmten Grund? Ich lass das einfach laufen und speise dann halt 45kwh/Tag ins Netz.

@querys

Da gibt es verschiedene Gründe.

Ich zb. Hab einen Akku zwischen Modulen und Wechselrichter. Da liegt es natürlich auf der Hand das nur soviel Energie aus dem Akku entnommen wird wie das Haus verbraucht.

Zweiter Grund ist das ich den EVU Nichts schenke was er den Nachbarn wieder teuer verkaufen kann.

So hat jeder seine Beweggründe.

guten tag,

würde mich hier auch gerne mal zu wort melden mit der gleichen idee der nulleinspeisung.

in home assistant erhalte ich dank tibber api von meinem zähler auch nur den derzeitigen verbrauch aller phasen zusammen. dort ist der anliegende solarstrom dann schon abgezogen.

um den eigentlichen verbrauch nun zu erhalten muss ich doch nur den sensor vom growatt current power davon abziehen und erhalte meinen tatsächlichen verbrauch. mit dem ich dann rechnen kann.

andererseits habe ich aber auch einen sensor der mir die einspeisung vom zähler angibt. also wenn ich eh schon mehr produziere als ich verbrauche. jetzt könnte ich doch meinen wr immer prozentual um diesen wert drosseln oder nicht?

die daten von tibber erhalte ich im sekundentakt. node-red läuft bei mir leider iwie nicht um das darüber nun zu realisieren. ihr flasht den wlan stick dafür neu wenn ich das richtig verstehe?

danke

@soulreafer

Moin,

Wenn du die Daten vom Wechselrichter und vom Zähler in HA hast,dann sollte HA im Energie Dashboard selbständig den Hausverbrauch ausrechnen und anzeigen.

Ansonsten gibt's über HACS noch das erweiterte Energie Dashboard was im Sekunden Takt anzeigt und Watt.

Ja wir flashen den growatt Stick um nicht die China Cloud zu benutzen.

Du kannst aber in HA auch den originalen Stick über die Cloud einbinden. Nur wird damit die Regelung schwierig weil du nur alle 5 min. Daten von der Cloud bekommst.

Die Rechte blase wird von HA selbst berechnet. Oben kommt vom WR und links kommt vom Zähler.

Ich würde die alternative Firmware für den Stick gerne ausprobieren, möchte aber vorher den Weg zurück geklärt haben?

Hat jemand die original Firmware und schon probiert den Stick wieder in den Auslieferungszustand zurück zu bringen ? Im Netz habe ich dazu nichts gefunden.

@soulreafer

Moin ich habe das mit einem template sensor in Homeassistent gelöst der mir die Momentanleistung angibt. Funktioniert super.

# Calculate Remaining Power
- platform: template
sensors:
remaining_power:
value_template: >
{{ '%0.1f' | format(states('sensor.power_tibber') | float(0) -
states('sensor.power_production_tibber') | float(0)) }}
unit_of_measurement: W
device_class: power
friendly_name: "Momentanleistung"
unique_id: '8626ca2b-7'

edit 02.09.2023: ich habe noch eine Fehler und eine Optimierungsoption gefunden.

  • Fehler: powermax hatte eine fehlende umrechnung dW zu W

  • Optimierung: Im falle power_t0 größer 0 Watt habe ich jetzt einfach noch eine Variable für den Basisstromverbrauch eingefügt

edit2 02.09.2023: und irgendwie mag der growatt mic es nicht wenn man powerset=powermax hat, also wird noch etwas variiert und ein Watt abgezogen. mal gucken ob das keine Fehler produziert

error log [12:05:41][E][modbus_controller:068]: Modbus error function code: 0x6 exception: 1 [12:05:41][E][modbus_controller:068]: Modbus error function code: 0x6 exception: 1 [12:05:41][E][modbus_controller:072]: Modbus error - last command: function code=0x6 register address = 0x3 registers count=1 payload size=2 [12:05:41][E][modbus_controller:072]: Modbus error - last command: function code=0x6 register address = 0x3 registers count=1 payload size=2
edit3 03.09.2023: alles blödsinn, man sollte seinen eigenen code lesen können, jetzt sollte es passen

Hier übrigens mein aktueller Code zur Aktualisierung der Einspeisung.

Ich verwende die GrowattPower nicht, da ich ja wie oben gesagt eh einen Kombinationssensor nutze. In meinem Szenario ist die Solaranlage jedoch nur ein kleiner Baustein und kondoliert einem BHKW. Deswegen mein powerOffset von -800 Watt.

der powerOffset wird dann im adjustedSensorValue berücksichtigt.

Folgende Erklärung zum adjusted Sensor Value (ich war faul, hat ChatGPT geschrieben):

Der gegebene Code verwendet eine bedingte Ternäroperator-Syntax, um einen Sensorwert anzupassen. Ich werde den Code Schritt für Schritt erklären:
  1. Die Variable adjustedSensorValue wird verwendet, um den angepassten Sensorwert zu speichern.
  2. Die Bedingung id(power_t0) < -powerOffset überprüft, ob der Wert von power_t0 kleiner ist als der negative Wert von powerOffset.
    • Wenn diese Bedingung wahr ist, wird der adjustedSensorValue auf 0 gesetzt, was bedeutet, dass der Sensorwert auf 0 eingestellt wird, wenn der Wert von power_t0 zu klein ist.
  3. Wenn die erste Bedingung nicht erfüllt ist, wird die nächste Bedingung geprüft: -powerOffset <= id(power_t0) < 0.
    • Wenn diese Bedingung wahr ist, wird der adjustedSensorValue auf id(power_t0) + powerOffset gesetzt. Das bedeutet, dass der Sensorwert um den Wert von powerOffset erhöht wird, wenn er zwischen -powerOffset und 0 liegt.
  4. Wenn weder die erste noch die zweite Bedingung wahr ist, wird die dritte Bedingung geprüft: 0 <= id(power_t0) < -powerOffset.
    • Wenn diese Bedingung wahr ist, wird der adjustedSensorValue auf -powerOffset - id(power_t0) gesetzt. Dies bedeutet, dass der Sensorwert um den Wert von powerOffset reduziert wird, wenn er zwischen 0 und -powerOffset liegt.
  5. Wenn keine der vorherigen Bedingungen erfüllt ist, wird der adjustedSensorValue einfach auf den ursprünglichen Wert von power_t0 gesetzt.
Zusammenfassend: Der Code passt den Sensorwert power_t0 basierend auf verschiedenen Bedingungen an und verwendet dabei den Wert von powerOffset. Je nachdem, wo sich der ursprüngliche Wert von power_t0 im Wertebereich befindet, wird der angepasste Wert entweder erhöht, verringert oder auf Null gesetzt.
## alternative codeschreibweise des adjustedSensorValue
if id(power_t0) < -powerOffset:
adjustedSensorValue = 0
elif -powerOffset <= id(power_t0) < 0:
adjustedSensorValue = id(power_t0) + powerOffset
elif 0 <= id(power_t0) < -powerOffset:
adjustedSensorValue = -powerOffset - id(power_t0)
else:
adjustedSensorValue = id(power_t0)
interval:
- interval: 1s
then:
- lambda: !lambda |-
ESP_LOGD("main", "Power_t0: %f ", id(power_t0));
static int16_t powerOffset = 8000;           // Export offset (in dW)
static int16_t powerOffset2 = 1500;           // Basic consumption (in dW)
int16_t adjustedSensorValue = 0;
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) + powerOffset2;
ESP_LOGD("main", "Adjusted Sensor Value: %d ", adjustedSensorValue);
// Weiter mit den Berechnungen ohne Berücksichtigung von GrowattPower
static int16_t powerMax = 15000;
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 from 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);

@mummy Moin,

ich habe mal deinen Code bei mir ausprobiert.Ich habe bei mir jetzt das Problem das mal kurz Einspeisung von 130Watt und im nächsten Moment ziehe ich 130Watt,

wie kann ich das minimieren?Habe einen Mic 600 auf 1000W eingestellt.Kann man nicht auf durchgehend leichte Einspeisung gehen und da regeln?Ohne das ich bei der Erzeugung Bezug vom Netz habe.

gruß Tobias

@grobi152 welche Firmware Version hast du ? Bei der Alten Firmware ist 160W Minimum zum Regeln.i


@mummy Habe grad letzte Woche das aktuelle Update auf den WR gemacht, was über Computer Bild glaube ich zu bekomme war.

GHAA.... 1050 / 17 oder so ähnlich

Welche Version hast du drauf?Hast du noch etwas anderes am WR eingestellt?

Gibt es eine Möglichkeit, eine Mindestleistung einzustellen,quasi so das die Grundlast immer gedeckt ist und dann die Regelung greift?