From 7b70c042bdd6167ef67dabf041590120d9d20d9f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 16 Sep 2019 16:56:16 +0200 Subject: [PATCH] Add command ModuleAddress 1/2/3 Add command ModuleAddress 1/2/3 to set Pzem module address when a single module is connected (#2315) --- sonoff/_changelog.ino | 1 + sonoff/sonoff.h | 2 ++ sonoff/xdrv_03_energy.ino | 17 +++++++++-- sonoff/xnrg_03_pzem004t.ino | 34 ++++++++++++++++----- sonoff/xnrg_05_pzem_ac.ino | 60 +++++++++++++++++++++++++++---------- sonoff/xnrg_06_pzem_dc.ino | 56 ++++++++++++++++++++++++++-------- 6 files changed, 132 insertions(+), 38 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 34b6681a2..d00dc075d 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -12,6 +12,7 @@ * Add initial support for up to three PZEM-004T on serial connection with addresses x.x.x.1 (default), 2 and 3 (#2315) * Add initial support for up to three PZEM-003/-017 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) * Add driver USE_SDM630_2 as future replacement for USE_SDM630 - Pls test and report + * Add command ModuleAddress 1/2/3 to set Pzem module address when a single module is connected (#2315) * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 89e3c53f3..9cefa9ca1 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -279,6 +279,8 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_PIN_STATE, FUNC_MODULE_INIT, FU FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS}; +enum AddressConfigSteps { ADDR_IDLE, ADDR_RECEIVE, ADDR_SEND }; + enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_MAX }; const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote"; diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 60f312917..5a9356985 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -37,14 +37,15 @@ #define D_CMND_VOLTAGECAL "VoltageCal" #define D_CMND_CURRENTCAL "CurrentCal" #define D_CMND_TARIFF "Tariff" +#define D_CMND_MODULEADDRESS "ModuleAddress" enum EnergyCommands { CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, - CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET }; + CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_MODULEADDRESS }; const char kEnergyCommands[] PROGMEM = "|" // No prefix D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" - D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" + D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_MODULEADDRESS "|" #ifdef USE_ENERGY_MARGIN_DETECTION D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" #ifdef USE_ENERGY_POWER_LIMIT @@ -57,7 +58,7 @@ const char kEnergyCommands[] PROGMEM = "|" // No prefix void (* const EnergyCommand[])(void) PROGMEM = { &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, - &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, + &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndModuleAddress, #ifdef USE_ENERGY_MARGIN_DETECTION &CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh, #ifdef USE_ENERGY_POWER_LIMIT @@ -614,6 +615,16 @@ void CmndFrequencySet(void) } } +void CmndModuleAddress(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4) && (1 == Energy.phase_count)) { + Energy.command_code = CMND_MODULEADDRESS; + if (XnrgCall(FUNC_COMMAND)) { // Module address + ResponseCmndDone(); + } + } +} + #ifdef USE_ENERGY_MARGIN_DETECTION void CmndPowerDelta(void) { diff --git a/sonoff/xnrg_03_pzem004t.ino b/sonoff/xnrg_03_pzem004t.ino index 5e1d3ceb6..46a39f44c 100644 --- a/sonoff/xnrg_03_pzem004t.ino +++ b/sonoff/xnrg_03_pzem004t.ino @@ -59,8 +59,9 @@ TasmotaSerial *PzemSerial = nullptr; struct PZEM { float energy = 0; uint8_t send_retry = 0; - uint8_t read_state = 0; + uint8_t read_state = 0; // Set address uint8_t phase = 0; + uint8_t address = 0; } Pzem; struct PZEMCommand { @@ -70,8 +71,6 @@ struct PZEMCommand { uint8_t crc; }; -IPAddress pzem_ip(192, 168, 1, 1); - uint8_t PzemCrc(uint8_t *data) { uint16_t crc = 0; @@ -86,10 +85,10 @@ void PzemSend(uint8_t cmd) PZEMCommand pzem; pzem.command = cmd; - for (uint32_t i = 0; i < sizeof(pzem.addr) -1; i++) { - pzem.addr[i] = pzem_ip[i]; - } - pzem.addr[3] = pzem_ip[3] + Pzem.phase; + pzem.addr[0] = 0; // Address 0.0.0.1 + pzem.addr[1] = 0; + pzem.addr[2] = 0; + pzem.addr[3] = ((PZEM_SET_ADDRESS == cmd) && Pzem.address) ? Pzem.address : 1 + Pzem.phase; pzem.data = 0; uint8_t *bytes = (uint8_t*)&pzem; @@ -97,6 +96,8 @@ void PzemSend(uint8_t cmd) PzemSerial->flush(); PzemSerial->write(bytes, sizeof(pzem)); + + Pzem.address = 0; } bool PzemReceiveReady(void) @@ -209,6 +210,9 @@ void PzemEvery200ms(void) if (Pzem.phase >= Energy.phase_count) { Pzem.phase = 0; } + if (Pzem.address) { + Pzem.read_state = 0; // Set address + } Pzem.send_retry = 5; PzemSend(pzem_commands[Pzem.read_state]); } @@ -230,6 +234,7 @@ void PzemSnsInit(void) } Energy.phase_count = 3; // Start off with three phases Pzem.phase = 2; + Pzem.read_state = 1; } else { energy_flg = ENERGY_NONE; } @@ -242,6 +247,18 @@ void PzemDrvInit(void) } } +bool PzemCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + Pzem.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3 + } + else serviced = false; // Unknown command + + return serviced; +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -254,6 +271,9 @@ bool Xnrg03(uint8_t function) case FUNC_EVERY_200_MSECOND: if (PzemSerial) { PzemEvery200ms(); } break; + case FUNC_COMMAND: + result = PzemCommand(); + break; case FUNC_INIT: PzemSnsInit(); break; diff --git a/sonoff/xnrg_05_pzem_ac.ino b/sonoff/xnrg_05_pzem_ac.ino index 54fe8a079..60362adbc 100644 --- a/sonoff/xnrg_05_pzem_ac.ino +++ b/sonoff/xnrg_05_pzem_ac.ino @@ -40,6 +40,8 @@ struct PZEMAC { float energy = 0; uint8_t send_retry = 0; uint8_t phase = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; } PzemAc; void PzemAcEverySecond(void) @@ -49,27 +51,34 @@ void PzemAcEverySecond(void) if (data_ready) { uint8_t buffer[30]; // At least 5 + (2 * 10) = 25 - uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, 10); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); + uint8_t registers = 10; + if (ADDR_RECEIVE == PzemAc.address_step) { + registers = 2; // Need 1 byte extra as response is F8 06 00 02 00 01 FD A3 + PzemAc.address_step--; + } + uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemAcModbus->ReceiveCount()); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); } else { Energy.data_valid = 0; + if (10 == registers) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 - // 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 - // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- - Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V - Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A - Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W - Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz - Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 + // 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 + // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- + Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V + Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A + Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W + Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz + Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 - PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh - if (PzemAc.phase == Energy.phase_count -1) { - EnergyUpdateTotal(PzemAc.energy, false); - PzemAc.energy = 0; + PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh + if (PzemAc.phase == Energy.phase_count -1) { + EnergyUpdateTotal(PzemAc.energy, false); + PzemAc.energy = 0; + } } } } @@ -80,7 +89,12 @@ void PzemAcEverySecond(void) PzemAc.phase = 0; } PzemAc.send_retry = ENERGY_WATCHDOG; - PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, 0x04, 0, 10); + if (ADDR_SEND == PzemAc.address_step) { + PzemAcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemAc.address); + PzemAc.address_step--; + } else { + PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, 0x04, 0, 10); + } } else { PzemAc.send_retry--; @@ -110,6 +124,19 @@ void PzemAcDrvInit(void) } } +bool PzemAcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemAc.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3 + PzemAc.address_step = ADDR_SEND; + } + else serviced = false; // Unknown command + + return serviced; +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -122,6 +149,9 @@ bool Xnrg05(uint8_t function) case FUNC_ENERGY_EVERY_SECOND: if (uptime > 4) { PzemAcEverySecond(); } // Fix start up issue #5875 break; + case FUNC_COMMAND: + result = PzemAcCommand(); + break; case FUNC_INIT: PzemAcSnsInit(); break; diff --git a/sonoff/xnrg_06_pzem_dc.ino b/sonoff/xnrg_06_pzem_dc.ino index 5188c051e..983c2db6a 100644 --- a/sonoff/xnrg_06_pzem_dc.ino +++ b/sonoff/xnrg_06_pzem_dc.ino @@ -40,6 +40,8 @@ struct PZEMDC { float energy = 0; uint8_t send_retry = 0; uint8_t channel = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; } PzemDc; void PzemDcEverySecond(void) @@ -49,25 +51,32 @@ void PzemDcEverySecond(void) if (data_ready) { uint8_t buffer[26]; // At least 5 + (2 * 8) = 21 - uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, 8); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); + uint8_t registers = 8; + if (ADDR_RECEIVE == PzemDc.address_step) { + registers = 2; // Need 1 byte extra as response is F8 06 00 02 00 01 FD A3 + PzemDc.address_step--; + } + uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemDcModbus->ReceiveCount()); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); } else { Energy.data_valid = 0; + if (8 == registers) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - // 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29 - // Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc-- - Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V - Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A - Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + // 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29 + // Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc-- + Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V + Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A + Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W - PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh - if (PzemDc.channel == Energy.phase_count -1) { - EnergyUpdateTotal(PzemDc.energy, false); - PzemDc.energy = 0; + PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh + if (PzemDc.channel == Energy.phase_count -1) { + EnergyUpdateTotal(PzemDc.energy, false); + PzemDc.energy = 0; + } } } } @@ -78,7 +87,12 @@ void PzemDcEverySecond(void) PzemDc.channel = 0; } PzemDc.send_retry = ENERGY_WATCHDOG; - PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8); + if (ADDR_SEND == PzemDc.address_step) { + PzemDcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemDc.address); + PzemDc.address_step--; + } else { + PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8); + } } else { PzemDc.send_retry--; @@ -109,6 +123,19 @@ void PzemDcDrvInit(void) } } +bool PzemDcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemDc.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3 + PzemDc.address_step = ADDR_SEND; + } + else serviced = false; // Unknown command + + return serviced; +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -121,6 +148,9 @@ bool Xnrg06(uint8_t function) case FUNC_ENERGY_EVERY_SECOND: if (uptime > 4) { PzemDcEverySecond(); } // Fix start up issue #5875 break; + case FUNC_COMMAND: + result = PzemDcCommand(); + break; case FUNC_INIT: PzemDcSnsInit(); break;