Add initial support for up to three PZEM-004T

Add initial support for up to three PZEM-004T on serial connection with addresses x.x.x.1 (default), 2 and 3 (#2315)
This commit is contained in:
Theo Arends 2019-09-15 15:40:18 +02:00
parent 098a2b27c3
commit 7e192cf624
2 changed files with 45 additions and 18 deletions

View File

@ -9,6 +9,7 @@
* Change energy sensors for three phase/channel support * Change energy sensors for three phase/channel support
* Add support for Shelly 2.5 dual energy (#6160) * Add support for Shelly 2.5 dual energy (#6160)
* Add initial support for up to three PZEM-014/-016 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) * Add initial support for up to three PZEM-014/-016 on serial modbus connection with addresses 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)
* *
* 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

@ -56,6 +56,13 @@ TasmotaSerial *PzemSerial = nullptr;
/*********************************************************************************************/ /*********************************************************************************************/
struct PZEM {
float energy = 0;
uint8_t send_retry = 0;
uint8_t read_state = 0;
uint8_t phase = 0;
} Pzem;
struct PZEMCommand { struct PZEMCommand {
uint8_t command; uint8_t command;
uint8_t addr[4]; uint8_t addr[4];
@ -68,7 +75,9 @@ 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;
for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) crc += *data++; for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) {
crc += *data++;
}
return (uint8_t)(crc & 0xFF); return (uint8_t)(crc & 0xFF);
} }
@ -77,7 +86,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); i++) pzem.addr[i] = pzem_ip[i]; 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.data = 0; pzem.data = 0;
uint8_t *bytes = (uint8_t*)&pzem; uint8_t *bytes = (uint8_t*)&pzem;
@ -159,42 +171,52 @@ bool PzemRecieve(uint8_t resp, float *data)
const uint8_t pzem_commands[] { PZEM_SET_ADDRESS, PZEM_VOLTAGE, PZEM_CURRENT, PZEM_POWER, PZEM_ENERGY }; const uint8_t pzem_commands[] { PZEM_SET_ADDRESS, PZEM_VOLTAGE, PZEM_CURRENT, PZEM_POWER, PZEM_ENERGY };
const uint8_t pzem_responses[] { RESP_SET_ADDRESS, RESP_VOLTAGE, RESP_CURRENT, RESP_POWER, RESP_ENERGY }; const uint8_t pzem_responses[] { RESP_SET_ADDRESS, RESP_VOLTAGE, RESP_CURRENT, RESP_POWER, RESP_ENERGY };
uint8_t pzem_read_state = 0;
uint8_t pzem_sendRetry = 0;
void PzemEvery200ms(void) void PzemEvery200ms(void)
{ {
bool data_ready = PzemReceiveReady(); bool data_ready = PzemReceiveReady();
if (data_ready) { if (data_ready) {
float value = 0; float value = 0;
if (PzemRecieve(pzem_responses[pzem_read_state], &value)) { if (PzemRecieve(pzem_responses[Pzem.read_state], &value)) {
Energy.data_valid = 0; Energy.data_valid = 0;
switch (pzem_read_state) { switch (Pzem.read_state) {
case 1: // Voltage as 230.2V case 1: // Voltage as 230.2V
Energy.voltage[0] = value; Energy.voltage[Pzem.phase] = value;
break; break;
case 2: // Current as 17.32A case 2: // Current as 17.32A
Energy.current[0] = value; Energy.current[Pzem.phase] = value;
break; break;
case 3: // Power as 20W case 3: // Power as 20W
Energy.active_power[0] = value; Energy.active_power[Pzem.phase] = value;
break; break;
case 4: // Total energy as 99999Wh case 4: // Total energy as 99999Wh
EnergyUpdateTotal(value, false); Pzem.energy += value;
if (Pzem.phase == Energy.phase_count -1) {
EnergyUpdateTotal(Pzem.energy, false);
Pzem.energy = 0;
}
break; break;
} }
pzem_read_state++; Pzem.read_state++;
if (5 == pzem_read_state) pzem_read_state = 1; if (5 == Pzem.read_state) {
Pzem.read_state = 1;
}
} }
} }
if (0 == pzem_sendRetry || data_ready) { if (0 == Pzem.send_retry || data_ready) {
pzem_sendRetry = 5; Pzem.phase++;
PzemSend(pzem_commands[pzem_read_state]); if (Pzem.phase >= Energy.phase_count) {
Pzem.phase = 0;
}
Pzem.send_retry = 5;
PzemSend(pzem_commands[Pzem.read_state]);
} }
else { else {
pzem_sendRetry--; Pzem.send_retry--;
if ((Energy.phase_count > 1) && (0 == Pzem.send_retry)) {
Energy.phase_count--; // Decrement phases if no response after retry
}
} }
} }
@ -203,7 +225,11 @@ void PzemSnsInit(void)
// Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions
PzemSerial = new TasmotaSerial(pin[GPIO_PZEM004_RX], pin[GPIO_PZEM0XX_TX], 1); PzemSerial = new TasmotaSerial(pin[GPIO_PZEM004_RX], pin[GPIO_PZEM0XX_TX], 1);
if (PzemSerial->begin(9600)) { if (PzemSerial->begin(9600)) {
if (PzemSerial->hardwareSerial()) { ClaimSerial(); } if (PzemSerial->hardwareSerial()) {
ClaimSerial();
}
Energy.phase_count = 3; // Start off with three phases
Pzem.phase = 2;
} else { } else {
energy_flg = ENERGY_NONE; energy_flg = ENERGY_NONE;
} }