diff --git a/tools/esp8266/app.cpp b/tools/esp8266/app.cpp
index f21a72ec..0a071a74 100644
--- a/tools/esp8266/app.cpp
+++ b/tools/esp8266/app.cpp
@@ -82,6 +82,7 @@ void app::setup(uint32_t timeout) {
iv = mSys->addInverter(name, invSerial, modPwr);
if(NULL != iv) {
mEep->read(ADDR_INV_PWR_LIM + (i * 2),&iv->powerLimit);
+ iv->devControlRequest = true; // set to true to update the active power limit from setup html page
DPRINTLN(DBG_INFO, F("add inverter: ") + String(name) + ", SN: " + String(invSerial, HEX) + ", Power Limit: " + String(iv->powerLimit));
for(uint8_t j = 0; j < 4; j++) {
mEep->read(ADDR_INV_CH_NAME + (i * 4 * MAX_NAME_LENGTH) + j * MAX_NAME_LENGTH, iv->chName[j], MAX_NAME_LENGTH);
@@ -216,6 +217,8 @@ void app::loop(void) {
bool rxRdy = mSys->Radio.switchRxCh();
+ char respTopic[64];
+
if(!mSys->BufCtrl.empty()) {
uint8_t len;
packet_t *p = mSys->BufCtrl.getBack();
@@ -230,7 +233,7 @@ void app::loop(void) {
if(0 != len) {
Inverter<> *iv = mSys->findInverter(&p->packet[1]);
- if(NULL != iv && p->packet[0] == 0x95) {
+ if(NULL != iv && p->packet[0] == (0x15 + 0x80)) { // response from get all information command
uint8_t *pid = &p->packet[9];
if (*pid == 0x00) {
DPRINT(DBG_DEBUG, "fragment number zero received and ignored");
@@ -249,6 +252,10 @@ void app::loop(void) {
}
}
}
+ if(NULL != iv && p->packet[0] == (0x51 + 0x80)) { // response from dev control command q'n'd
+ snprintf(respTopic, 64, "%s/devcontrol/%d/resp", mMqtt.getTopic(), iv->id);
+ mMqtt.sendMsg2(respTopic,(const char*)p->packet,false);
+ }
}
}
@@ -354,11 +361,13 @@ void app::loop(void) {
yield();
if(mSerialDebug)
DPRINTLN(DBG_INFO, F("Requesting Inverter SN ") + String(iv->serial.u64, HEX));
- if(iv->powerLimitChange){
+ if(iv->devControlRequest){
if(mSerialDebug)
- DPRINTLN(DBG_INFO, F("Requesting Inverter to change power limit to ") + String(iv->powerLimit));
- mSys->Radio.sendControlPacket(iv->radioId.u64, uint16_t(iv->powerLimit*10));
- iv->powerLimitChange = false;
+ DPRINTLN(DBG_INFO, F("Devcontrol request ") + String(iv->devControlCmd) + F(" power limit ") + String(iv->powerLimit));
+ mSys->Radio.sendControlPacket(iv->radioId.u64, uint16_t(iv->powerLimit),iv->devControlCmd);
+ // ToDo: Only set Request to false if succesful executed
+ iv->devControlRequest = false;
+ // ^^
} else {
mSys->Radio.sendTimePacket(iv->radioId.u64, mPayload[iv->id].ts);
mRxTicker = 0;
@@ -504,10 +513,12 @@ void app::showSetup(void) {
uint64_t invSerial;
char name[MAX_NAME_LENGTH + 1] = {0};
uint16_t modPwr[4];
+ uint16_t invActivePowerLimit = -1;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
mEep->read(ADDR_INV_ADDR + (i * 8), &invSerial);
mEep->read(ADDR_INV_NAME + (i * MAX_NAME_LENGTH), name, MAX_NAME_LENGTH);
mEep->read(ADDR_INV_CH_PWR + (i * 2 * 4), modPwr, 4);
+ mEep->read(ADDR_INV_PWR_LIM + (i * 2),&invActivePowerLimit);
inv += F("
Inverter ") + String(i) + "
";
inv += F("");
@@ -521,6 +532,12 @@ void app::showSetup(void) {
inv += String(name);
inv += F("\"/ maxlength=\"") + String(MAX_NAME_LENGTH) + "\">";
+ inv += F("");
+ inv += F("";
+
+
inv += F("");
for(uint8_t j = 0; j < 4; j++) {
@@ -613,17 +630,68 @@ void app::showErase() {
}
//-----------------------------------------------------------------------------
-void app::cbMqtt(const char* topic, byte* payload, unsigned int length) {
+void app::cbMqtt(char* topic, byte* payload, unsigned int length) {
+ // callback handling on subscribed devcontrol topic
DPRINTLN(DBG_INFO, F("app::cbMqtt"));
- // DPRINTLN(DBG_INFO, topic);
- // ToDo check topic !
- int inverterId = 0; // ToDo get inverter id from topic
- Inverter<> *iv = this->mSys->getInverterByPos(inverterId);
- if(NULL != iv) {
- iv->powerLimit = std::stoi((char*)payload);
- iv->powerLimitChange = true;
- mEep->write(ADDR_INV_PWR_LIM + inverterId * 2,iv->powerLimit);
- DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit) + F("W") );
+ DPRINTLN(DBG_INFO, topic);
+ // subcribed topics are mTopic + "/devcontrol/#" where # is //
+ // eg. mypvsolar/devcontrol/1/11/400 --> inverter 1 active power limit 400 Watt
+ char *token = strtok(topic, "/");
+ while (token != NULL)
+ {
+ // .../devcontrol///
+ // ^^^
+ if (strcmp(token,"devcontrol")){
+ Inverter<> *iv = this->mSys->getInverterByPos(std::stoi(strtok(NULL, "/")));
+ // .../devcontrol///
+ // ^^^
+ if(NULL != iv) {
+ // switch case subcmd
+ switch ( std::stoi(strtok(NULL, "/")) ){
+ // .../devcontrol///
+ // ^^^^
+ case 11: // Active Power Control
+ iv->devControlCmd = 11;
+ iv->powerLimit = std::stoi(strtok(NULL, "/"));
+ // .../devcontrol///
+ // ^^^
+ mEep->write(ADDR_INV_PWR_LIM + iv->id * 2,iv->powerLimit);
+ // updateCrc();
+ // mEep->commit();
+ DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit) + F("W") );
+ break;
+ case 0: // Turn On
+ iv->devControlCmd = 0;
+ DPRINTLN(DBG_INFO, F("Turn on inverter ") + String(iv->id) );
+ break;
+ case 1: // Turn Off
+ iv->devControlCmd = 1;
+ DPRINTLN(DBG_INFO, F("Turn off inverter ") + String(iv->id) );
+ break;
+ case 2: // Restart
+ iv->devControlCmd = 2;
+ DPRINTLN(DBG_INFO, F("Restart inverter ") + String(iv->id) );
+ break;
+ case 12: // Reactive Power Control
+ // iv->devControlCmd = 12;
+ // uint16_t reactive_power = std::stoi(strtok(NULL, "/"));
+ // .../devcontrol///
+ // ^^^
+ DPRINTLN(DBG_INFO, F("Reactive Power Control not implemented for inverter ") + String(iv->id) );
+ break;
+ case 13: // Set Power Factor
+ // iv->devControlCmd = 13;
+ // uint16_t power_factor = std::stoi(strtok(NULL, "/"));
+ // .../devcontrol///
+ // ^^^
+ DPRINTLN(DBG_INFO, F("Set Power Factor not implemented for inverter ") + String(iv->id) );
+ break;
+ }
+ iv->devControlRequest = true;
+ }
+ break;
+ }
+ token = strtok(NULL, "/");
}
}
@@ -813,6 +881,7 @@ void app::saveValues(bool webSend = true) {
char buf[20] = {0};
uint8_t i = 0;
uint16_t interval;
+ uint16_t activepowerlimit=-1;
// inverter
serial_u addr;
@@ -824,6 +893,10 @@ void app::saveValues(bool webSend = true) {
addr.u64 = Serial2u64(buf);
mEep->write(ADDR_INV_ADDR + (i * 8), addr.u64);
+ // active power limit
+ activepowerlimit = mWeb->arg("inv" + String(i) + "ActivePowerLimit").toInt();
+ mEep->write(ADDR_INV_PWR_LIM + i * 2,activepowerlimit);
+
// name
mWeb->arg("inv" + String(i) + "Name").toCharArray(buf, 20);
mEep->write(ADDR_INV_NAME + (i * MAX_NAME_LENGTH), buf, MAX_NAME_LENGTH);
diff --git a/tools/esp8266/app.h b/tools/esp8266/app.h
index 96d1b9cb..2f3d9e8e 100644
--- a/tools/esp8266/app.h
+++ b/tools/esp8266/app.h
@@ -51,7 +51,7 @@ class app : public Main {
void setup(uint32_t timeout);
void loop(void);
void handleIntr(void);
- void cbMqtt(const char* topic, byte* payload, unsigned int length);
+ void cbMqtt(char* topic, byte* payload, unsigned int length);
uint8_t app_loops;
uint8_t getIrqPin(void) {
diff --git a/tools/esp8266/hmInverter.h b/tools/esp8266/hmInverter.h
index 2e65a2c7..213e8c66 100644
--- a/tools/esp8266/hmInverter.h
+++ b/tools/esp8266/hmInverter.h
@@ -70,7 +70,8 @@ class Inverter {
byteAssign_t* assign; // type of inverter
uint8_t listLen; // length of assignments
uint16_t powerLimit; // limit power output
- bool powerLimitChange; // true if change needed
+ uint8_t devControlCmd; // carries the requested cmd
+ bool devControlRequest; // true if change needed
serial_u serial; // serial number as on barcode
serial_u radioId; // id converted to modbus
uint8_t channels; // number of PV channels (1-4)
@@ -82,7 +83,7 @@ class Inverter {
Inverter() {
ts = 0;
powerLimit = -1; // 65535 W Limit -> unlimited
- powerLimitChange = false;
+ devControlRequest = false;
}
~Inverter() {
diff --git a/tools/esp8266/hmRadio.h b/tools/esp8266/hmRadio.h
index 69100118..1062664c 100644
--- a/tools/esp8266/hmRadio.h
+++ b/tools/esp8266/hmRadio.h
@@ -166,26 +166,30 @@ class HmRadio {
return mTxChLst[mTxChIdx];
}*/
- void sendControlPacket(uint64_t invId, uint16_t data) {
+ void sendControlPacket(uint64_t invId, uint16_t data, uint8_t cmd) {
DPRINTLN(DBG_VERBOSE, F("hmRadio.h:sendControlPacket"));
- sendCmdPacket(invId, 0x51, 0x80, false);
- mTxBuf[10] = 0x0b; // control type --> 0x0b => Type_ActivePowerContr
- mTxBuf[11] = 0x00;
- // 4 bytes control data
- // Power Limit fix point 10 eg. 30 W --> 0d300 = 0x012c
- mTxBuf[12] = (data >> 8) & 0xff; // 0x01
- mTxBuf[13] = (data ) & 0xff; // 0x2c
- //
- mTxBuf[14] = 0x00;
- mTxBuf[15] = 0x00;
+ // sendCmdPacket(invId, 0x51, 0x80, false); // 0x80 implementation as original DTU code
+ sendCmdPacket(invId, 0x51, 0x81, false);
+ int cnt = 0;
+ mTxBuf[10] = cmd; // cmd --> 0x0b => Type_ActivePowerContr, 0 on, 1 off, 2 restart, 12 reactive power, 13 power factor
+ mTxBuf[10 + (++cnt)] = 0x00;
+ if (cmd == 11){
+ // 4 bytes control data
+ // Power Limit fix point 10 eg. 30 W --> 0d300 = 0x012c
+ mTxBuf[10 + (++cnt)] = (data*10 >> 8) & 0xff; // 0x01
+ mTxBuf[10 + (++cnt)] = (data*10 ) & 0xff; // 0x2c
+ // are these two bytes necessary?
+ mTxBuf[10 + (++cnt)] = 0x00;
+ mTxBuf[10 + (++cnt)] = 0x00;
+ }
// crc control data
- uint16_t crc = crc16(&mTxBuf[10], 6);
- mTxBuf[16] = (crc >> 8) & 0xff;
- mTxBuf[17] = (crc ) & 0xff;
+ uint16_t crc = crc16(&mTxBuf[10], 10 - (cnt+1));
+ mTxBuf[10 + (++cnt)] = (crc >> 8) & 0xff;
+ mTxBuf[10 + (++cnt)] = (crc ) & 0xff;
// crc over all
- mTxBuf[18] = crc8(mTxBuf, 18);
+ mTxBuf[10 + (++cnt)] = crc8(mTxBuf, 18);
- sendPacket(invId, mTxBuf, 19, true);
+ sendPacket(invId, mTxBuf, 10 + (++cnt), true);
}
void sendTimePacket(uint64_t invId, uint32_t ts) {
diff --git a/tools/esp8266/mqtt.h b/tools/esp8266/mqtt.h
index c6a2bf2b..9e973162 100644
--- a/tools/esp8266/mqtt.h
+++ b/tools/esp8266/mqtt.h
@@ -99,7 +99,10 @@ class mqtt {
else
mClient->connect(DEF_DEVICE_NAME);
}
- mClient->subscribe("home/huette/powerset");
+ char topic[MQTT_TOPIC_LEN + 13 ]; // "/devcontrol/#" --> + 6 byte
+ // ToDo: "/devcontrol/#" is hardcoded
+ snprintf(topic, MQTT_TOPIC_LEN + 13, "%s/devcontrol/#", mTopic);
+ mClient->subscribe(topic); // subscribe to mTopic + "/devcontrol/#"
}
WiFiClient mEspClient;