@rattenfaenger Hi, wie hast du den Code auf einem Wemos D1 Mini zum laufen bekommen, ich bekomme da nur Fehler beim schreiben der Config.
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 ;).
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
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
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);