Auf dieser Seite
An sonnigen Tagen hat mein 5-kW-Wechselrichter von Huawei den Großteil seines Stroms für ein paar Cent eingespeist, während die Vaillant-Wärmepumpe das Haus am Abend mit Netzbezug geheizt hat. Die Lösung ist in der Theorie einfach: Wenn es PV-Überschuss gibt, soll die Wärmepumpe ihn als Wärme speichern, in den Räumen und im Warmwasserspeicher. In diesem Beitrag zeige ich, wie ich das mit einem ESP32 für 5 € umgesetzt habe, was ich gemessen habe und welche Fallen mich am meisten Zeit gekostet haben.
Getestet vs. modellabhängig. Alles mit ✅ läuft bei mir zu Hause: SUN2000-5KTL-M1 mit SDongle, aroTHERM split VWL 75/5 AS + uniTOWER VWL 78/5 IS, VRC 700, Firmware auf einem Lolin S3 Mini. Alles mit ⚠️ hängt von deinem Modell, deiner Firmware oder deiner Installation ab: Schau in deine Handbücher, bevor du es übernimmst.
Was du baust
- Ein ESP32 liest den Wechselrichter alle paar Sekunden über Modbus TCP aus (✅).
- Alle 10 Minuten entscheidet er, ob es genug stabilen Überschuss gibt, um die Wärmepumpe mit Solarstrom zu betreiben.
- Wenn ja, hebt er die Raum-Solltemperatur für begrenzte Zeit um ein paar Grad an (ein Vaillant-Quick-Veto, ✅) und optional die Warmwasser-Solltemperatur.
- Verschwindet die Sonne, beginnt Netzbezug oder ist der Raum warm genug, hört er auf. Die von dir gesetzten Grenzen überschreitet er nie.
- Ein kleines Web-Dashboard auf dem ESP32 zeigt alles an, ganz ohne eigene Cloud.
Hardware
| Bauteil | Was ich verwende | Hinweise |
|---|---|---|
| Mikrocontroller | Lolin S3 Mini (ESP32-S3, 4 MB Flash, 2 MB PSRAM) | ✅ Jeder ESP32 mit WLAN funktioniert; das PSRAM des S3 hilft bei großen JSON-Antworten |
| Stromversorgung | USB-C-Handyladegerät, 5 V ≥ 1 A | ✅ Nur ein zertifiziertes Netzteil; das Board braucht etwa 0,5 W |
| Verbindung zum Wechselrichter | Huawei SDongle (WLAN/LAN) | ✅ Modbus TCP auf Port 502; keine Verkabelung zum ESP32 |
| Verbindung zur Wärmepumpe | myVAILLANT-Gateway (VR 920/921) oder ein eBUS-Adapter | ⚠️ Siehe die drei Varianten unten |
| Optionale Relais | Relaismodul mit Optokoppler und 3,3-V-Logikeingang | ⚠️ Nur für Verbraucher oder einen SG-Ready-Eingang, den dein Handbuch dokumentiert |
Anschließen
- Versorge den ESP32 über USB-C. Das erste Flashen läuft über USB, alle späteren Updates über WLAN (ArduinoOTA).
- Kein Kabel zum Wechselrichter. Der SDongle ist schon in deinem WLAN; der ESP32 spricht über das Netzwerk mit ihm.
- Relais (optional): Relais-
INan einen freien GPIO,GNDanGND, Relais-VCCan das, was das Modul vorgibt. Meide beim S3 Mini die Strapping-Pins (0, 3, 45, 46), USB (19, 20) und die RGB-LED (47). - Alles mit Netzspannung oder an den Klemmen der Wärmepumpe gehört in die Hände eines Elektrikers. Der ESP32 hängt nie direkt am Netz und nie an einer Vaillant-Klemme, außer die Installationsanleitung nennt genau diese Klemme als potenzialfreien Eingang für genau diesen Zweck.
Teil 1: den Huawei-Wechselrichter auslesen
Modbus TCP oder RTU?
| Weg | Wie | Status |
|---|---|---|
| Modbus TCP über SDongle | WLAN/LAN, Port 502, Unit-ID 1 | ✅ Das verwende ich |
| Modbus RTU über RS485 | COM-Port des Wechselrichters, braucht einen RS485-Transceiver (z. B. MAX3485-Modul) | ⚠️ Pinbelegung und Terminierung sind modellabhängig; der SDongle ist dann eventuell nicht gleichzeitig nutzbar |
Um Modbus TCP zu aktivieren, stellt ein Installateur-Konto in der FusionSolar-App oder im Webportal Modbus TCP am Dongle auf "Enable (Unrestricted)" (⚠️ der Menüname ändert sich je nach App-Version). Danach antwortet Port 502 im lokalen Netzwerk.
Die Falle, die mich einen Tag gekostet hat: Der SDongle akzeptiert nur einen Modbus-TCP-Client gleichzeitig. Hängt Home Assistant, eine Handy-App oder ein zweiter ESP dran, laufen deine Abfragen in einen Timeout. Wähle einen Leser aus und versorge alles andere von dort aus mit Daten.
Zwei weitere Eigenheiten: Warte nach dem Verbinden etwa eine Sekunde bis zur ersten Anfrage und lies in Blöcken von höchstens 64 Registern mit einer kleinen Pause zwischen den Anfragen.
Die wichtigen Register (✅ getestet am SUN2000-5KTL-M1)
Alle sind Holding-Register, gelesen mit Funktionscode 03. 32-Bit-Werte sind Big-Endian (High-Word zuerst). Die Liste ist mit wlcrs/huawei-solar-lib abgeglichen.
| Register | Typ | Gain | Bedeutung |
|---|---|---|---|
| 32064 | I32 | 1 | PV-Eingangsleistung (W) |
| 32080 | I32 | 1 | AC-Wirkleistung (W), der "Solar"-Wert, den ich verwende |
| 32114 | U32 | 100 | Ertrag heute (kWh) |
| 32106 | U32 | 100 | Gesamtertrag (kWh) |
| 37100 | U16 | 1 | Status des Stromzählers (1 = online), nur mit Huawei Smart Meter |
| 37113 | I32 | 1 | Netzleistung am Zähler (W): positiv = Einspeisung, negativ = Netzbezug |
| 37119 / 37121 | I32 | 100 | Eingespeiste / bezogene Energie (kWh) |
Den Hausverbrauch gibt es nicht als Register. Mit Zähler: consumption = active power − grid power. Ohne Zähler
(mein Fall) schätzt die Firmware grid = house base load + heat pump + car − solar, wobei die Grundlast des Hauses
eine Einstellung ist (bei mir 300 W).
Firmware: eine Anfrage, dekodiert
Ich verwende eModbus, das Anfragen in eine Warteschlange stellt und dich asynchron zurückruft:
#include <WiFi.h>
#include "ModbusClientTCP.h"
#include "secrets.h" // WIFI_SSID, WIFI_PASSWORD
WiFiClient wifiClient;
ModbusClientTCP modbus(wifiClient);
const uint32_t TOKEN_AC = 1;
int32_t readI32(ModbusMessage& msg, uint16_t offsetRegisters) {
uint16_t high, low;
// Byte 3 is the first data byte after unit ID, function code and byte count.
msg.get(3 + offsetRegisters * 2, high);
msg.get(5 + offsetRegisters * 2, low);
return (int32_t)(((uint32_t)high << 16) | low);
}
void onData(ModbusMessage msg, uint32_t token) {
if (token == TOKEN_AC) {
int32_t pvInputW = readI32(msg, 0); // 32064
int32_t activePowerW = readI32(msg, 16); // 32080
Serial.printf("PV %ld W, AC %ld W\n", (long)pvInputW, (long)activePowerW);
}
}
void onError(Error error, uint32_t token) {
Serial.printf("Modbus error %02X on request %u\n", (int)error, (unsigned)token);
}
void setup() {
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(200);
}
modbus.onDataHandler(&onData);
modbus.onErrorHandler(&onError);
modbus.setTimeout(5000, 200); // response timeout, gap between requests (ms)
modbus.begin();
modbus.setTarget(IPAddress(192, 168, 1, 11), 502);
}
void loop() {
static uint32_t last = 0;
if (millis() - last > 5000 && modbus.pendingRequests() == 0) {
last = millis();
modbus.addRequest(TOKEN_AC, 1, READ_HOLD_REGISTER, 32064, 18); // 32064..32081
}
}Teil 2: die Vaillant-Wärmepumpe überwachen und steuern
Es gibt drei Zugänge. Sie unterscheiden sich stark in Aufwand und Risiko:
| Weg | Was du bekommst | Lokal? | Status |
|---|---|---|---|
| myVAILLANT-Cloud (über das VR 920/921-Gateway, das du schon hast) | Raum- und Warmwassertemperaturen, Betriebsarten, Energie, Quick-Veto, Warmwasser-Solltemperatur | Nein | ✅ Das verwende ich, inoffizielle API |
| eBUS-Adapter + ebusd | Alles auf dem Bus: Vorlauf-/Rücklauftemperatur, Verdichterdaten, Sollwerte; Sollwerte schreiben | Ja | ⚠️ Bei mir noch nicht im Einsatz; Nachrichtendefinitionen hängen von der Reglerfirmware ab |
| SG Ready / Energiemanager-Eingang | Zwei potenzialfreie Kontakte: "Solarüberschuss, mehr heizen" | Ja | ⚠️ Nur wenn das Handbuch deines Geräts den Eingang dokumentiert; Verkabelung durch einen Installateur |
Der Cloud-Weg (den ich nutze)
Das Gateway hat keine lokale API; die myVAILLANT-App spricht mit der Vaillant-Cloud, und der ESP32 macht das auch. Die Open-Source-Bibliothek myPyllant dokumentiert die API; die Teile, die ich brauche, habe ich nach C++ portiert. Drei Dinge sind wichtig:
- Die Anmeldung läuft über OAuth mit PKCE, einmalig von dir im Browser. Versuch nicht, die Login-Seite zu automatisieren.
- Die API hat ein Rate Limit. Auf
403 "Out of call volume quota"reagierst du, indem du genau so lange wartest, wie in der Meldung steht. Meine Firmware fragt den Status alle 5 Minuten ab, solange die Sonne scheint, sonst alle 15 Minuten: etwa 400 Aufrufe pro Tag. - Die Cloud hinkt hinterher. Nach einer Änderung kommt einige Minuten lang noch der alte Wert zurück. Vertrau ~10 Minuten lang deiner eigenen Änderung, statt auf veraltete Daten zu reagieren.
Zum Steuern verwende ich das Quick-Veto: eine vorübergehende Raum-Solltemperatur mit Dauer, die der Regler nach
Ablauf von selbst wieder entfernt. Das macht es ausfallsicher: Stirbt der ESP32 mitten im Boost, kehrt die Heizung
von allein zum Normalbetrieb zurück. Beim VRC 700 ist der Endpoint PATCH …/zone/{index}/heating/quick-veto, und er
braucht sowohl desiredRoomTemperatureSetpoint als auch duration (nur die Dauer liefert HTTP 400 ✅).
Der lokale Weg: eBUS
VRC 700, Wärmepumpe und Gateway teilen sich den eBUS, einen zweiadrigen Niederspannungsbus. Ein eigener Adapter (zum Beispiel der ebusd-Adapter, auf dem ein ESP32 sitzt) hängt am Bus, und ebusd dekodiert ihn mit den Nachrichtendateien aus ebusd-configuration. Schließ den eBUS nie direkt an Pins des ESP32 an: Der Bus läuft mit deutlich mehr als 3,3 V und braucht eine passende Schnittstellenschaltung.
Sobald ebusd läuft, finde heraus, was dein Regler bereitstellt, statt Namen zu raten:
ebusctl find # lists all messages ebusd knows for your devices
ebusctl read <name> # read one value, using a name from the list aboveebusd kann alles an MQTT veröffentlichen, das deine Automatisierung (oder der ESP32) abonniert. ⚠️ Fang nur lesend an; schreib Sollwerte erst, wenn du die genaue Nachricht auf deiner Reglerfirmware bestätigt hast.
Teil 3: die Automatisierung, sicher umgesetzt
Die Entscheidung, alle 10 Minuten
spare = solar − house base load − car (if charging) # 1 s samples, 10-min average
level = highest level whose start is reached
and where spare − learned heat pump draw ≥ margin
start only if spare ≥ level 1 start for ≥ 80 % of the window # steady sun, not one sunny minute
and ≥ 30 min since the last boost ended # protects the compressor
stop if grid import > 30 Wh in the window # checked every second
or no level fits any more
target = day temperature + level offset (+1 … +4.5 °C), never above the cap (24.5 °C)Was ich auf die harte Tour gelernt habe:
- Lern den tatsächlichen Verbrauch der Wärmepumpe, aber nur, während sie den Raum heizt. Eine Warmwasserbereitung zieht ~1,8 kW; als das in den "Verbrauch auf Stufe 1" (tatsächlich ~0,8 kW) eingeflossen ist, startete gar kein Boost mehr.
- Zähl Warmwasserbereitungen nicht als gescheiterte Boosts. Der Regler startet sie nach seinem eigenen Zeitplan; wenn du deinen Raum-Boost beendest, stoppt das sie nicht.
- Starte nach Stabilität, nicht nach Durchschnittswerten. Eine einzelne Sonnenlücke zwischen Wolken sollte keinen Verdichter starten.
Warmwasser: verschieben statt dagegen ankämpfen
Warmwasser ist die am leichtesten verschiebbare Last, weil der Speicher die Wärme stundenlang hält. Der robusteste Schritt kostet keine Zeile Code: Erlaube im VRC 700 (oder in der App) Warmwasser nur von 11:00 bis 15:00. Zusätzlich kann der ESP32 bei Überschuss die Warmwasser-Solltemperatur anheben und danach wiederherstellen (er merkt sich den ursprünglichen Wert im Flash, sodass er auch nach einem Neustart wiederhergestellt wird).
Sicherheitsregeln in der Firmware
- Jede Änderung ist vorübergehend (Quick-Veto mit Dauer) oder wird gemerkt und wiederhergestellt (Warmwasser-Solltemperatur, Heizbetriebsart), auch nach einem Stromausfall.
- Harte Obergrenzen: maximale Raum-Solltemperatur, maximale Warmwasser-Solltemperatur, maximale Pausenzeit (18 h).
- Fehlen Daten oder sind sie veraltet, endet der Boost. Keine Daten heißt nie "mehr heizen".
- Die eigenen Schutzfunktionen der Wärmepumpe (Frostschutz, Legionellenschutz, Mindestlaufzeiten) bleiben unangetastet.
Mein Regler heizt einen Raum mit 22 °C. Warum?
Ist die Raumaufschaltung auf "Kein" gestellt (engl. "None"), folgt der VRC 700 nur der Außentemperatur und ignoriert den Raum. Zwei Lösungen: Stell die Raumaufschaltung im Installateurmenü auf "Aktiv" oder "Thermostat" (⚠️ nur wenn der Regler in einem Wohnraum hängt), oder, wie meine Firmware, pausiere die Heizung, solange der Raum über seiner Solltemperatur liegt und keine Sonne scheint, und setze sie fort, sobald der Raum wieder bei der Solltemperatur ist.
Einrichtung, Schritt für Schritt
- Aktiviere in FusionSolar (Installateur-Konto) Modbus TCP am Dongle; notier dir seine IP.
- Flashe den ESP32 einmal per USB mit WLAN-Zugangsdaten und der Dongle-IP; prüf im seriellen Log, ob Messwerte kommen.
- Öffne das Dashboard und melde dich bei myVAILLANT an (oder richte die Firmware auf ebusd/MQTT aus).
- Stell Grundlast des Hauses, Boost-Stufen, Obergrenzen und die Toleranz für Netzbezug ein; starte im Modus Auto mit vorsichtigen Werten.
- Beobachte einen sonnigen Tag im Verlaufsdiagramm: Solarleistung, Verbrauch der Wärmepumpe, geschätzter Netzbezug und Boost-Phasen.
- Feintuning: Senk den Start von Stufe 1 auf den tatsächlichen Mindestverbrauch der Wärmepumpe (~0,7–0,9 kW bei meinem Gerät).
Fehlerbehebung
| Symptom | Ursache und Lösung |
|---|---|
timeout reading 30000, keine Daten vom Wechselrichter | Ein anderer Modbus-Client belegt den SDongle. Trenn ihn; immer nur ein Leser gleichzeitig |
| Erste Anfrage nach dem Verbinden schlägt fehl | Nach dem TCP-Verbindungsaufbau ~1 s warten, bevor du liest |
| Manche Blöcke liefern Exceptions | Nicht jedes Modell hat jedes Register (Zähler, Batterie). Markier sie als nicht unterstützt und versuch es nur selten erneut |
myVAILLANT 403 Out of call volume quota | Zu viele Aufrufe. Warte die angegebene Zeit und frag danach seltener ab |
HTTP 400 beim Verlängern eines Quick-Vetos (VRC 700) | Schick die Solltemperatur zusammen mit der Dauer |
| Boost startet nie, obwohl die Sonne scheint | Prüf den gelernten Verbrauch pro Stufe; setz ihn zurück, falls eine Warmwasserbereitung ihn aufgebläht hat |
| Wärmepumpe taktet stark | Verlange stabilen Überschuss und eine Ruhezeit zwischen Boosts |
Open-Source-Projekte, die du kennen solltest
- wlcrs/huawei-solar-lib und wlcrs/huawei_solar: Modbus-Registertabelle von Huawei und Home-Assistant-Integration.
- eModbus/eModbus: asynchrones Modbus TCP/RTU für den ESP32.
- signalkraft/myPyllant und signalkraft/mypyllant-component: myVAILLANT-API und Home-Assistant-Integration.
- john30/ebusd und john30/ebusd-configuration: lokaler eBUS-Daemon und Nachrichtendateien.
- evcc-io/evcc: Überschussladen für E-Autos (und Wärmepumpen über SG Ready).
- Open-Meteo: kostenlose Wetter- und Einstrahlungsprognose, um den nächsten Tag zu planen.
Fazit
Einen Huawei-Wechselrichter auszulesen ist einfach, sobald du die Ein-Client-Regel und die Registertabelle kennst. Eine Vaillant-Wärmepumpe sicher zu steuern ist die schwierigere Hälfte: Setz auf vorübergehende Änderungen, die von selbst ablaufen, halte die Rate Limits der Cloud ein (oder geh mit eBUS lokal) und bau Geduld in deine Regeln ein (stabile Sonne, Ruhezeiten), damit der Verdichter nicht gestresst wird. Das Ergebnis bei mir zu Hause: An sonnigen Tagen läuft die Wärmepumpe mit Solarstrom, das Haus speichert die Wärme, und am Abend pausiert die Heizung, statt den Strom wieder aus dem Netz zu kaufen.