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
* 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-004T on serial connection with addresses x.x.x.1 (default), 2 and 3 (#2315)
*
* 6.6.0.11 20190907
* 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 {
uint8_t command;
uint8_t addr[4];
@ -68,7 +75,9 @@ IPAddress pzem_ip(192, 168, 1, 1);
uint8_t PzemCrc(uint8_t *data)
{
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);
}
@ -77,7 +86,10 @@ void PzemSend(uint8_t cmd)
PZEMCommand pzem;
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;
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_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)
{
bool data_ready = PzemReceiveReady();
if (data_ready) {
float value = 0;
if (PzemRecieve(pzem_responses[pzem_read_state], &value)) {
if (PzemRecieve(pzem_responses[Pzem.read_state], &value)) {
Energy.data_valid = 0;
switch (pzem_read_state) {
switch (Pzem.read_state) {
case 1: // Voltage as 230.2V
Energy.voltage[0] = value;
Energy.voltage[Pzem.phase] = value;
break;
case 2: // Current as 17.32A
Energy.current[0] = value;
Energy.current[Pzem.phase] = value;
break;
case 3: // Power as 20W
Energy.active_power[0] = value;
Energy.active_power[Pzem.phase] = value;
break;
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;
}
pzem_read_state++;
if (5 == pzem_read_state) pzem_read_state = 1;
Pzem.read_state++;
if (5 == Pzem.read_state) {
Pzem.read_state = 1;
}
}
}
if (0 == pzem_sendRetry || data_ready) {
pzem_sendRetry = 5;
PzemSend(pzem_commands[pzem_read_state]);
if (0 == Pzem.send_retry || data_ready) {
Pzem.phase++;
if (Pzem.phase >= Energy.phase_count) {
Pzem.phase = 0;
}
Pzem.send_retry = 5;
PzemSend(pzem_commands[Pzem.read_state]);
}
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
PzemSerial = new TasmotaSerial(pin[GPIO_PZEM004_RX], pin[GPIO_PZEM0XX_TX], 1);
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 {
energy_flg = ENERGY_NONE;
}