Add command ModuleAddress 1/2/3

Add command ModuleAddress 1/2/3 to set Pzem module address when a single module is connected (#2315)
This commit is contained in:
Theo Arends 2019-09-16 16:56:16 +02:00
parent d193b8cb1a
commit 7b70c042bd
6 changed files with 132 additions and 38 deletions

View File

@ -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-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 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 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 * 6.6.0.11 20190907
* Change Settings crc calculation allowing short term backward compatibility * Change Settings crc calculation allowing short term backward compatibility

View File

@ -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_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}; 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, 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 }; 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"; 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";

View File

@ -37,14 +37,15 @@
#define D_CMND_VOLTAGECAL "VoltageCal" #define D_CMND_VOLTAGECAL "VoltageCal"
#define D_CMND_CURRENTCAL "CurrentCal" #define D_CMND_CURRENTCAL "CurrentCal"
#define D_CMND_TARIFF "Tariff" #define D_CMND_TARIFF "Tariff"
#define D_CMND_MODULEADDRESS "ModuleAddress"
enum EnergyCommands { enum EnergyCommands {
CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, 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 const char kEnergyCommands[] PROGMEM = "|" // No prefix
D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" 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 #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 "|" 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 #ifdef USE_ENERGY_POWER_LIMIT
@ -57,7 +58,7 @@ const char kEnergyCommands[] PROGMEM = "|" // No prefix
void (* const EnergyCommand[])(void) PROGMEM = { void (* const EnergyCommand[])(void) PROGMEM = {
&CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal,
&CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndModuleAddress,
#ifdef USE_ENERGY_MARGIN_DETECTION #ifdef USE_ENERGY_MARGIN_DETECTION
&CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh, &CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh,
#ifdef USE_ENERGY_POWER_LIMIT #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 #ifdef USE_ENERGY_MARGIN_DETECTION
void CmndPowerDelta(void) void CmndPowerDelta(void)
{ {

View File

@ -59,8 +59,9 @@ TasmotaSerial *PzemSerial = nullptr;
struct PZEM { struct PZEM {
float energy = 0; float energy = 0;
uint8_t send_retry = 0; uint8_t send_retry = 0;
uint8_t read_state = 0; uint8_t read_state = 0; // Set address
uint8_t phase = 0; uint8_t phase = 0;
uint8_t address = 0;
} Pzem; } Pzem;
struct PZEMCommand { struct PZEMCommand {
@ -70,8 +71,6 @@ struct PZEMCommand {
uint8_t crc; uint8_t crc;
}; };
IPAddress pzem_ip(192, 168, 1, 1);
uint8_t PzemCrc(uint8_t *data) uint8_t PzemCrc(uint8_t *data)
{ {
uint16_t crc = 0; uint16_t crc = 0;
@ -86,10 +85,10 @@ void PzemSend(uint8_t cmd)
PZEMCommand pzem; PZEMCommand pzem;
pzem.command = cmd; pzem.command = cmd;
for (uint32_t i = 0; i < sizeof(pzem.addr) -1; i++) { pzem.addr[0] = 0; // Address 0.0.0.1
pzem.addr[i] = pzem_ip[i]; pzem.addr[1] = 0;
} pzem.addr[2] = 0;
pzem.addr[3] = pzem_ip[3] + Pzem.phase; pzem.addr[3] = ((PZEM_SET_ADDRESS == cmd) && Pzem.address) ? Pzem.address : 1 + Pzem.phase;
pzem.data = 0; pzem.data = 0;
uint8_t *bytes = (uint8_t*)&pzem; uint8_t *bytes = (uint8_t*)&pzem;
@ -97,6 +96,8 @@ void PzemSend(uint8_t cmd)
PzemSerial->flush(); PzemSerial->flush();
PzemSerial->write(bytes, sizeof(pzem)); PzemSerial->write(bytes, sizeof(pzem));
Pzem.address = 0;
} }
bool PzemReceiveReady(void) bool PzemReceiveReady(void)
@ -209,6 +210,9 @@ void PzemEvery200ms(void)
if (Pzem.phase >= Energy.phase_count) { if (Pzem.phase >= Energy.phase_count) {
Pzem.phase = 0; Pzem.phase = 0;
} }
if (Pzem.address) {
Pzem.read_state = 0; // Set address
}
Pzem.send_retry = 5; Pzem.send_retry = 5;
PzemSend(pzem_commands[Pzem.read_state]); PzemSend(pzem_commands[Pzem.read_state]);
} }
@ -230,6 +234,7 @@ void PzemSnsInit(void)
} }
Energy.phase_count = 3; // Start off with three phases Energy.phase_count = 3; // Start off with three phases
Pzem.phase = 2; Pzem.phase = 2;
Pzem.read_state = 1;
} else { } else {
energy_flg = ENERGY_NONE; 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 * Interface
\*********************************************************************************************/ \*********************************************************************************************/
@ -254,6 +271,9 @@ bool Xnrg03(uint8_t function)
case FUNC_EVERY_200_MSECOND: case FUNC_EVERY_200_MSECOND:
if (PzemSerial) { PzemEvery200ms(); } if (PzemSerial) { PzemEvery200ms(); }
break; break;
case FUNC_COMMAND:
result = PzemCommand();
break;
case FUNC_INIT: case FUNC_INIT:
PzemSnsInit(); PzemSnsInit();
break; break;

View File

@ -40,6 +40,8 @@ struct PZEMAC {
float energy = 0; float energy = 0;
uint8_t send_retry = 0; uint8_t send_retry = 0;
uint8_t phase = 0; uint8_t phase = 0;
uint8_t address = 0;
uint8_t address_step = ADDR_IDLE;
} PzemAc; } PzemAc;
void PzemAcEverySecond(void) void PzemAcEverySecond(void)
@ -49,27 +51,34 @@ void PzemAcEverySecond(void)
if (data_ready) { if (data_ready) {
uint8_t buffer[30]; // At least 5 + (2 * 10) = 25 uint8_t buffer[30]; // At least 5 + (2 * 10) = 25
uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, 10); uint8_t registers = 10;
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); 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) { if (error) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error);
} else { } else {
Energy.data_valid = 0; 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 // 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 // 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-- // 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.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.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.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.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 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 PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh
if (PzemAc.phase == Energy.phase_count -1) { if (PzemAc.phase == Energy.phase_count -1) {
EnergyUpdateTotal(PzemAc.energy, false); EnergyUpdateTotal(PzemAc.energy, false);
PzemAc.energy = 0; PzemAc.energy = 0;
}
} }
} }
} }
@ -80,7 +89,12 @@ void PzemAcEverySecond(void)
PzemAc.phase = 0; PzemAc.phase = 0;
} }
PzemAc.send_retry = ENERGY_WATCHDOG; 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 { else {
PzemAc.send_retry--; 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 * Interface
\*********************************************************************************************/ \*********************************************************************************************/
@ -122,6 +149,9 @@ bool Xnrg05(uint8_t function)
case FUNC_ENERGY_EVERY_SECOND: case FUNC_ENERGY_EVERY_SECOND:
if (uptime > 4) { PzemAcEverySecond(); } // Fix start up issue #5875 if (uptime > 4) { PzemAcEverySecond(); } // Fix start up issue #5875
break; break;
case FUNC_COMMAND:
result = PzemAcCommand();
break;
case FUNC_INIT: case FUNC_INIT:
PzemAcSnsInit(); PzemAcSnsInit();
break; break;

View File

@ -40,6 +40,8 @@ struct PZEMDC {
float energy = 0; float energy = 0;
uint8_t send_retry = 0; uint8_t send_retry = 0;
uint8_t channel = 0; uint8_t channel = 0;
uint8_t address = 0;
uint8_t address_step = ADDR_IDLE;
} PzemDc; } PzemDc;
void PzemDcEverySecond(void) void PzemDcEverySecond(void)
@ -49,25 +51,32 @@ void PzemDcEverySecond(void)
if (data_ready) { if (data_ready) {
uint8_t buffer[26]; // At least 5 + (2 * 8) = 21 uint8_t buffer[26]; // At least 5 + (2 * 8) = 21
uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, 8); uint8_t registers = 8;
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); 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) { if (error) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error);
} else { } else {
Energy.data_valid = 0; 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 // 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 // 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-- // 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.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.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 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 PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh
if (PzemDc.channel == Energy.phase_count -1) { if (PzemDc.channel == Energy.phase_count -1) {
EnergyUpdateTotal(PzemDc.energy, false); EnergyUpdateTotal(PzemDc.energy, false);
PzemDc.energy = 0; PzemDc.energy = 0;
}
} }
} }
} }
@ -78,7 +87,12 @@ void PzemDcEverySecond(void)
PzemDc.channel = 0; PzemDc.channel = 0;
} }
PzemDc.send_retry = ENERGY_WATCHDOG; 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 { else {
PzemDc.send_retry--; 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 * Interface
\*********************************************************************************************/ \*********************************************************************************************/
@ -121,6 +148,9 @@ bool Xnrg06(uint8_t function)
case FUNC_ENERGY_EVERY_SECOND: case FUNC_ENERGY_EVERY_SECOND:
if (uptime > 4) { PzemDcEverySecond(); } // Fix start up issue #5875 if (uptime > 4) { PzemDcEverySecond(); } // Fix start up issue #5875
break; break;
case FUNC_COMMAND:
result = PzemDcCommand();
break;
case FUNC_INIT: case FUNC_INIT:
PzemDcSnsInit(); PzemDcSnsInit();
break; break;