Control Zendure via ESP32

I am working on a project to control a Zendure SolarFlow 800 Pro using an ESP32.

My ESP32 reads the power meter to determine the current power flow. A positive value means the house is importing power from the grid, while a negative value means excess power is being exported to the grid.

I can successfully read data from the SolarFlow (e.g. battery SOC, solar input power, and output power), but I cannot send commands to change the output power supplied to the house. Although my HTTP/MQTT request appears to be sent successfully, the SolarFlow does not change its output.

Below are some additional information and code snippets.

Has anyone encountered this issue before or has an idea what I might be doing wrong?

the loop:
if (firstRun) {
firstRun = 0;
readSolarflow();
Serial.printf(“Accu: %u %%\n”, gBatterySOC);
Serial.printf(“Solar: %u W\n”, gSolarPower);
Serial.printf(“FeedPower: %u W\n”, gFeedPower);

gMeterVal = getMeter();
Serial.printf("Meter: %i W\n", gMeterVal);

writeSmartMode();
feedPower(234);
readSolarflow();

the function

void writeSmartMode(){

HTTPClient http;

String url = String(“http://”) + zendureIp + “/properties/write”;
http.begin(url);
http.addHeader(“Content-Type”, “application/json”);

// JSON-Body aufbauen: { “sn”: “…”, “properties”: { “outputLimit”: } }
JsonDocument doc;
doc[“sn”] = zendureSn;
JsonObject props = doc[“properties”].to();
props[“smartMode”] = 0;

String body;
serializeJson(doc, body);

Serial.print("Sende HTTP POST: ");
Serial.println(url);
Serial.print("Body: ");
Serial.println(body);

int httpCode = http.POST(body);
String response = http.getString();

Serial.printf(“HTTP-Code: %d\n”, httpCode);
Serial.println(“Antwort:”);
Serial.println(response);

http.end();
}

the Log:

WLAN verbunden, IP ESP32: 192.168.178.35
{“timestamp”:1786121190,“messageId”:43,“sn”:“zendureSN”,“version”:3,“product”:“solarFlow800Pro”,“properties”:{“heatState”:0,“packInputPower”:0,“outputPackPower”:0,“outputHomePower”:221,“remainOutTime”:23820,“packState”:2,“electricLevel”:99,“gridInputPower”:0,“solarInputPower”:250,“solarPower1”:125,“solarPower2”:125,“solarPower3”:0,“solarPower4”:0,“pass”:2,“reverseState”:1,“socStatus”:0,“hyperTmp”:3141,“gridOffPower”:0,“dcStatus”:0,“pvStatus”:0,“acStatus”:1,“dataReady”:1,“gridState”:1,“BatVolt”:4996,“socLimit”:1,“faultLevel”:2,“writeRsp”:0,“acMode”:2,“inputLimit”:0,“outputLimit”:0,“socSet”:1000,“minSoc”:100,“gridStandard”:0,“gridReverse”:1,“inverseMaxPower”:800,“lampSwitch”:1,“gridOffMode”:2,“IOTState”:2,“Fanmode”:1,“Fanspeed”:0,“bindstate”:0,“factoryModeState”:0,“OTAState”:0,“oldMode”:0,“VoltWakeup”:0,“ts”:1786121038,“tsZone”:14,“smartMode”:1,“chargeMaxLimit”:800,“phaseSwitch”:2,“batCalTime”:60,“socCompSwitch”:0,“packNum”:1,“rssi”:-64,“is_error”:0},“packData”:[{“sn”:“SN”,“packType”:300,“socLevel”:99,“state”:2,“power”:4,“maxTemp”:3081,“totalVol”:4980,“batcur”:65535,“maxVol”:332,“minVol”:331,“softVersion”:4354}]}
Accu: 99 %
Solar: 250 W
FeedPower: 221 W
Meter: -64 W
Sende HTTP POST: to 192.168.178.64/properties/write
Body: {“sn”:“zendureSN”,“properties”:{“smartMode”:0}}
HTTP-Code: 200
Antwort:
{“timestamp”:1786121195,“messageId”:44,“success”:true,“code”:200,“sn”:“zendureSN”}
Sende HTTP POST: to 192.168.178.64/properties/write
Body: {“sn”:“zendureSN”,“properties”:{“smartMode”:1,“acMode”:1,“outputLimit”:0}}
HTTP-Code: 200
Antwort: {“timestamp”:1786121196,“messageId”:45,“success”:true,“code”:200,“sn”:“zendureSN”}
Sende HTTP POST: to 192.168.178.64/properties/write
Body: {“sn”:“zendureSN”,“properties”:{“smartMode”:1,“acMode”:2,“outputLimit”:234}}
HTTP-Code: 200
Antwort: {“timestamp”:1786121196,“messageId”:46,“success”:true,“code”:200,“sn”:“zendureSN”}
{“timestamp”:1786121200,“messageId”:47,“sn”:“zendureSN”,“version”:3,“product”:“solarFlow800Pro”,“properties”:{“heatState”:0,“packInputPower”:0,“outputPackPower”:0,“outputHomePower”:0,“remainOutTime”:23820,“packState”:2,“electricLevel”:99,“gridInputPower”:0,“solarInputPower”:0,“solarPower1”:0,“solarPower2”:0,“solarPower3”:0,“solarPower4”:0,“pass”:0,“reverseState”:1,“socStatus”:0,“hyperTmp”:3141,“gridOffPower”:0,“dcStatus”:0,“pvStatus”:0,“acStatus”:0,“dataReady”:1,“gridState”:1,“BatVolt”:4996,“socLimit”:1,“faultLevel”:2,“writeRsp”:0,“acMode”:2,“inputLimit”:0,“outputLimit”:234,“socSet”:1000,“minSoc”:100,“gridStandard”:0,“gridReverse”:1,“inverseMaxPower”:800,“lampSwitch”:1,“gridOffMode”:2,“IOTState”:2,“Fanmode”:1,“Fanspeed”:0,“bindstate”:0,“factoryModeState”:0,“OTAState”:0,“oldMode”:0,“VoltWakeup”:0,“ts”:1786121038,“tsZone”:14,“smartMode”:1,“chargeMaxLimit”:800,“phaseSwitch”:2,“batCalTime”:60,“socCompSwitch”:0,“packNum”:1,“rssi”:-62,“is_error”:0},“packData”:[{“sn”:“SN”,“packType”:300,“socLevel”:99,“state”:2,“power”:4,“maxTemp”:3081,“totalVol”:4980,“batcur”:65535,“maxVol”:332,“minVol”:331,“softVersion”:4354}]}

Unfortunately, the output was not changed, and no power was supplied to the house.

What is wrong? I think the Zendure device needs to be configured in some way to work, but how?

why do you use an ESP32 to connect to Zendure? OH can directly use the Zendure API for reading and writing. Of course OH needs the smart meter readings.

If the write to zendure doesn’t work, to be sure: you didn’t add the sf800 to HEMS, there cannot be two captains on 1 ship :wink:

The API is explained on github, ZenDSK, you’re using that?

antoher check: are you running the latest firmware? Zendure accidentally/onpurpose locked batteries from working this week when they ran an older version.

Antoher tip: run your json output through ChatGPT and ask what’s wrong

Thank you for your reply. I think now that the main problem is, that I still have HEMS activated. I‘m concerrned that the system doesn’t work correctly, if I switch it off and only control the power to my home. Will the battery still charge properly in that case?

that depends how you control the charging and discharging. With HEMS turned off, it doesn’t do anything by itself, you have to tell it exactly when to do what and how much.

And also keep in mind what source data you have available and what you want to achieve, in which part of the year. In the summer you perhaps want to use as much solar power as possible during evening/night. In the winter perhaps charge the battery when the dynamic tariffs are low?

So first sit back and determine what you want to achieve.

I now successfully finished my Software and see, that it is enough to control the power, that I want to have in my house. First step is to set the Solarflow into the automatic mode with the app, then to select the device and to disable HEMS. From then on you can control the energy that you want to have in your home. Loading the battery from the solar panel and also the choice of whether to draw energy from the battery or the solar panels works in the automatic mode without any further action.