Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
pipelka committed Jan 18, 2024
1 parent 8bec07f commit 42961ef
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 35 deletions.
3 changes: 2 additions & 1 deletion include/ModbusSunSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ class ModbusSunSpecClass : protected ModbusTCP {
uint64_t serial{0};
int32_t timeout{0};
int16_t power{0};
bool pending{false};
};

void loop();

void loopPowerLimit();

void setLimit(uint64_t serial, uint16_t watts, const char *cause);
bool setLimit(uint64_t serial, uint16_t watts, const char *cause);

Limit* getLimit(uint64_t serial);

Expand Down
76 changes: 47 additions & 29 deletions src/ModbusSunSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ bool ModbusSunSpecClass::HregStr(uint16_t offset, const String& value, uint16_t
String tValue = value.substring(0, size);

bool rc{true};
uint16_t* c = (uint16_t*)tValue.c_str();
const uint16_t* c = reinterpret_cast<const uint16_t*>(tValue.c_str());

int i = 0;
for(i = 0; i < (tValue.length() + 1) / 2; i++) {
Expand All @@ -85,11 +85,6 @@ void ModbusSunSpecClass::init(Scheduler& scheduler) {
server();

if(config.SunSpec.RemoteControl) {
for (auto &conf : config.SunSpec.Inverter) {
if (conf.Enabled && conf.Serial != 0) {
setLimit(conf.Serial, conf.MaxPower, "init");
}
}
MessageOutput.printf("Total Max Power: %uW\r\n", getTotalMaxPower());
MessageOutput.printf("AC Output: %u phases\r\n", getPhaseCount());
}
Expand Down Expand Up @@ -218,7 +213,7 @@ void ModbusSunSpecClass::init(Scheduler& scheduler) {
scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&ModbusSunSpecClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.setInterval(1 * TASK_SECOND);
_loopTask.setInterval(500 * TASK_MILLISECOND);
_loopTask.enable();

scheduler.addTask(_taskTask);
Expand All @@ -233,13 +228,28 @@ void ModbusSunSpecClass::loopPowerLimit() {
int32_t now = (int32_t)millis();

for (auto &limit : _limit) {
if (limit.serial != 0 && now - limit.timeout > 0) {
if (auto conf = getConfig(limit.serial)) {
if(conf != nullptr) {
setLimit(limit.serial, conf->MaxPower, "timeout");
}
}
if(limit.serial == 0) {
continue;
}

auto conf = getConfig(limit.serial);
if(conf == nullptr) {
limit.serial = 0;
continue;
}

if(limit.pending) {
limit.pending = !setLimit(limit.serial, limit.power, "update");
}

if (now - limit.timeout > 0) {
if(!limit.pending) {
limit.serial = 0;
}
else {
limit.power = conf->MaxPower;
limit.pending = true;
}
}
}
}
Expand Down Expand Up @@ -281,15 +291,15 @@ void ModbusSunSpecClass::loop() {
}

for (auto& t : stats->getChannelTypes()) {
for (auto& c : inv->Statistics()->getChannelsByType(t)) {
for (auto& c : stats->getChannelsByType(t)) {
if (t == TYPE_AC) {
auto phase = conf->channel_ac[c].Phase;
phases[phase].current += (stats->getChannelFieldValue(t, c, FLD_IAC) * 100);
phases[phase].voltage = max(phases[phase].voltage, (uint16_t)(stats->getChannelFieldValue(t, c, FLD_UAC) * 10));
phases[phase].power += (uint16_t)(stats->getChannelFieldValue(t, c, FLD_PAC) * 10);
phases[phase].power_factor += (uint16_t)(stats->getChannelFieldValue(t, c, FLD_PF) * 100);
phases[phase].energy += (uint16_t)(inv->Statistics()->getChannelFieldValue(t, c, FLD_YT) * 10 * 1000); // kWh -> Wh
phases[phase].frequency += (uint16_t)(inv->Statistics()->getChannelFieldValue(t, c, FLD_F) * 10);
phases[phase].energy += (uint32_t)(stats->getChannelFieldValue(TYPE_AC, c, FLD_YT) * 10 * 1000); // kWh -> Wh
phases[phase].frequency += (uint16_t)(stats->getChannelFieldValue(t, c, FLD_F) * 10);
phases[phase].count++;
}
if (t == TYPE_DC) {
Expand Down Expand Up @@ -338,6 +348,9 @@ void ModbusSunSpecClass::loop() {
if(!SunPosition.isDayPeriod()) {
HregU16(40107, 2);
}
else if(Datastore.getIsAtLeastOneReachable()) {
HregU16(40107, 3);
}
else if(Datastore.getIsAtLeastOneProducing()) {
bool throttled = (Hreg(40158) == 1) && (Hreg(40154) < 100);
HregU16(40107, throttled ? 5 : 4);
Expand All @@ -355,6 +368,7 @@ void ModbusSunSpecClass::setPowerLimit(uint16_t limit_pct, uint16_t timeout_sec)
MessageOutput.println("--------------");

uint16_t total_power{0};
int i{1};

for (auto &conf : config.SunSpec.Inverter) {
if (!conf.Enabled || conf.Serial == 0) {
Expand All @@ -363,7 +377,7 @@ void ModbusSunSpecClass::setPowerLimit(uint16_t limit_pct, uint16_t timeout_sec)

uint16_t curr_power = (conf.MaxPower * limit_pct) / 100;

MessageOutput.printf("new power level: %iW (%u%%)\r\n", curr_power, limit_pct);
MessageOutput.printf("Inverter #%i: new power level: %iW (%u%%)\r\n", i++, curr_power, limit_pct);

total_power += curr_power;

Expand All @@ -374,7 +388,7 @@ void ModbusSunSpecClass::setPowerLimit(uint16_t limit_pct, uint16_t timeout_sec)
}

if (limit->power != curr_power) {
setLimit(conf.Serial, curr_power, "update");
limit->pending = true;
limit->power = curr_power;
}

Expand Down Expand Up @@ -440,23 +454,27 @@ SUNSPEC_INVERTER_CONFIG_T* ModbusSunSpecClass::getConfig(uint64_t serial) {
return nullptr;
}

void ModbusSunSpecClass::setLimit(uint64_t serial, uint16_t watts, const char *cause) {
bool ModbusSunSpecClass::setLimit(uint64_t serial, uint16_t watts, const char *cause) {
if(serial == 0) {
return;
return true;
}

auto inv = Hoymiles.getInverterBySerial(serial);
if (inv) {
MessageOutput.print("Inverter ");
MessageOutput.print(serial, HEX);
MessageOutput.printf(" new limit is %uW (%s)\r\n", watts, cause);
inv->sendActivePowerControlRequest(watts, PowerLimitControlType::AbsolutNonPersistent);
if (!inv) {
return true;
}
else {
MessageOutput.print("Ignored limit for mising inverter ");
MessageOutput.print(serial, HEX);
MessageOutput.print("\r\n");

if(inv->SystemConfigPara()->getLastLimitCommandSuccess() == CMD_PENDING) {
MessageOutput.println("setlimit: last command still pending - skipping");
return false;
}

MessageOutput.print("Inverter ");
MessageOutput.print(serial, HEX);
MessageOutput.printf(" new limit is %uW (%s)\r\n", watts, cause);

inv->sendActivePowerControlRequest(watts, PowerLimitControlType::AbsolutNonPersistent);
return true;
}

ModbusSunSpecClass::Limit* ModbusSunSpecClass::getLimit(uint64_t serial) {
Expand Down
4 changes: 2 additions & 2 deletions src/WebApi_dtu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
return;
}

auto FrequencyDefinition = Hoymiles.getRadioCmt()->getCountryFrequencyList()[root["cmt_country"].as<CountryModeId_t>()].definition;
/*auto FrequencyDefinition = Hoymiles.getRadioCmt()->getCountryFrequencyList()[root["cmt_country"].as<CountryModeId_t>()].definition;
if (root["cmt_frequency"].as<uint32_t>() < FrequencyDefinition.Freq_Min
|| root["cmt_frequency"].as<uint32_t>() > FrequencyDefinition.Freq_Max
|| root["cmt_frequency"].as<uint32_t>() % Hoymiles.getRadioCmt()->getChannelWidth() > 0) {
Expand All @@ -179,7 +179,7 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
response->setLength();
request->send(response);
return;
}
}*/

CONFIG_T& config = Configuration.get();

Expand Down
6 changes: 3 additions & 3 deletions src/WebApi_sunspec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ void WebApiSunSpecClass::onSunSpecGet(AsyncWebServerRequest* request)
auto inv = Hoymiles.getInverterBySerial(config.Inverter[i].Serial);

uint8_t max_channels_ac{1};
if (inv == nullptr) {
if (!inv) {
obj["type"] = "Unknown";
obj["max_power"] = config.SunSpec.Inverter[i].MaxPower;
} else {
obj["type"] = inv->typeName();
max_channels_ac = inv->Statistics()->getChannelsByType(TYPE_AC).size();
obj["max_power"] = config.SunSpec.Inverter[i].MaxPower | inv->DevInfo()->getMaxPower();
}

obj["max_power"] = config.SunSpec.Inverter[i].MaxPower;

JsonArray channel_ac = obj.createNestedArray("channel_ac");
for (uint8_t c = 0; c < max_channels_ac; c++) {
JsonObject chanData = channel_ac.createNestedObject();
Expand Down

0 comments on commit 42961ef

Please sign in to comment.