Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix and Improve PZEM017 (PZEM_DC) driver #19402

Merged
merged 1 commit into from
Aug 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions lib/lib_basic/TasmotaModbus-3.6.0/src/TasmotaModbus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ uint8_t TasmotaModbus::Send(uint8_t device_address, uint8_t function_code, uint1
uint8_t *frame;
uint8_t framepointer = 0;

#ifdef TASMOTAMODBUSDEBUG
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MBS: Serial Send: @%02X f:%02X r:%04X c:%u &:%08X"), device_address, function_code, start_address, count, (uint32)write_data);
if (write_data) AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MBS: Serial Send: Write data 0x%04X"), write_data[0]);
#endif

uint16_t byte_count = count * 2; // In register mode count is nr of registers (2 bytes)
if ((function_code == 1) || (function_code == 2) || (function_code == 15)) byte_count = ((count-1) / 8) + 1; // In bitmode count is nr of bits

Expand Down Expand Up @@ -107,11 +112,17 @@ uint8_t TasmotaModbus::Send(uint8_t device_address, uint8_t function_code, uint1
if (write_data == NULL)
{
free(frame);
#ifdef TASMOTAMODBUSDEBUG
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MBS: Serial Send: no data (13.1)"));
#endif
return 13; // Register data not specified
}
if (count != 1)
{
free(frame);
#ifdef TASMOTAMODBUSDEBUG
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MBS: Serial Send: wrong count (12.1)"));
#endif
return 12; // Wrong register count
}
frame[framepointer++] = (uint8_t)(write_data[0] >> 8); // MSB
Expand All @@ -127,11 +138,17 @@ uint8_t TasmotaModbus::Send(uint8_t device_address, uint8_t function_code, uint1
if (write_data == NULL)
{
free(frame);
#ifdef TASMOTAMODBUSDEBUG
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MBS: Serial Send: no data (13.2)"));
#endif
return 13; // Register data not specified
}
if (count == 0)
{
free(frame);
#ifdef TASMOTAMODBUSDEBUG
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MBS: Serial Send: wrong count (12.2)"));
#endif
return 12; // Wrong register count
}
for (uint16_t bytepointer = 0; bytepointer < byte_count; bytepointer++)
Expand All @@ -142,6 +159,9 @@ uint8_t TasmotaModbus::Send(uint8_t device_address, uint8_t function_code, uint1
else
{
free(frame);
#ifdef TASMOTAMODBUSDEBUG
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MBS: Serial Send: wrong fct (1)"));
#endif
return 1; // Wrong function code
}

Expand Down
109 changes: 72 additions & 37 deletions tasmota/tasmota_xnrg_energy/xnrg_06_pzem_dc.ino
Original file line number Diff line number Diff line change
Expand Up @@ -35,47 +35,53 @@ const uint8_t PZEM_DC_DEVICE_ADDRESS = 0x01; // PZEM default address
const uint32_t PZEM_DC_STABILIZE = 10; // Number of seconds to stabilize 1 pzem

#include <TasmotaModbus.h>
TasmotaModbus *PzemDcModbus;

struct PZEMDC {
float energy = 0;
float last_energy = 0;
uint8_t send_retry = 0;
uint8_t channel = 0;
uint8_t address = 0;
uint8_t address_step = ADDR_IDLE;
} PzemDc;
struct PZEMDC_Data {
TasmotaModbus *modbus;
float energy;
float last_energy;
uint8_t send_retry;
uint8_t channel;
uint8_t address;
uint8_t range;
uint8_t address_step;
} *PzemDc = nullptr; // Will be dynamically allocated in PzemDcDrvInit() if GPIO in use

const char PZEMDC_Commands[] PROGMEM = "range";
enum PZEMDC_COMMANDS { // commands for Console
CMND_PZEMDC_RANGE=0 // Set current range register
};

void PzemDcEverySecond(void)
{
bool data_ready = PzemDcModbus->ReceiveReady();
bool data_ready = PzemDc->modbus->ReceiveReady();

if (data_ready) {
uint8_t buffer[26]; // At least 5 + (2 * 8) = 21

uint8_t registers = 8;
if (ADDR_RECEIVE == PzemDc.address_step) {
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--;
PzemDc->address_step--;
}
uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, registers);
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemDcModbus->ReceiveCount());
uint8_t error = PzemDc->modbus->ReceiveBuffer(buffer, registers);
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemDc->modbus->ReceiveCount());

if (error) {
AddLog(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error);
AddLog(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc->channel, error);
} else {
Energy->data_valid[PzemDc.channel] = 0;
Energy->data_valid[PzemDc->channel] = 0;
if (8 == registers) {

// 0 1 2 3 4 5 6 7 = ModBus register
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 = Buffer index
// 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.0f; // 655.00 V
Energy->current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0f; // 655.00 A
Energy->active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0f; // 429496729.0 W
Energy->import_active[PzemDc.channel] = (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]) / 1000.0f; // 4294967.295 kWh
if (PzemDc.channel == Energy->phase_count -1) {
Energy->voltage[PzemDc->channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0f; // 655.00 V
Energy->current[PzemDc->channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0f; // 655.00 A
Energy->active_power[PzemDc->channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0f; // 429496729.0 W
Energy->import_active[PzemDc->channel] = (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]) / 1000.0f; // 4294967.295 kWh
if (PzemDc->channel == Energy->phase_count -1) {
if (TasmotaGlobal.uptime > (PZEM_DC_STABILIZE * ENERGY_MAX_PHASES)) {
EnergyUpdateTotal();
}
Expand All @@ -84,23 +90,30 @@ void PzemDcEverySecond(void)
}
}

if (0 == PzemDc.send_retry || data_ready) {
if (0 == PzemDc.channel) {
PzemDc.channel = Energy->phase_count -1;
if (0 == PzemDc->send_retry || data_ready) {
if (0 == PzemDc->channel) {
PzemDc->channel = Energy->phase_count -1;
} else {
PzemDc.channel--;
PzemDc->channel--;
}
PzemDc.send_retry = ENERGY_WATCHDOG;
if (ADDR_SEND == PzemDc.address_step) {
PzemDcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemDc.address);
PzemDc.address_step--;
PzemDc->send_retry = ENERGY_WATCHDOG;
if (PzemDc->address) {
uint16_t addr = PzemDc->address;
PzemDc->modbus->Send(0xF8, 0x06, 0x0002, 1, &addr);
PzemDc->address = 0;
PzemDc->address_step = ADDR_RECEIVE;
} else if (PzemDc->range) {
uint16_t range = PzemDc->range>>1;
PzemDc->modbus->Send(0xF8, 0x06, 0x0003, 1, &range);
PzemDc->range = 0;
PzemDc->address_step = ADDR_RECEIVE;
} else {
PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8);
PzemDc->modbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc->channel, 0x04, 0, 8);
}
}
else {
PzemDc.send_retry--;
if ((Energy->phase_count > 1) && (0 == PzemDc.send_retry) && (TasmotaGlobal.uptime < (PZEM_DC_STABILIZE * ENERGY_MAX_PHASES))) {
PzemDc->send_retry--;
if ((Energy->phase_count > 1) && (0 == PzemDc->send_retry) && (TasmotaGlobal.uptime < (PZEM_DC_STABILIZE * ENERGY_MAX_PHASES))) {
Energy->phase_count--; // Decrement channels if no response after retry within 30 seconds after restart
if (TasmotaGlobal.discovery_counter) {
TasmotaGlobal.discovery_counter += ENERGY_WATCHDOG + 1; // Don't send Discovery yet, delay by 4s + 1s
Expand All @@ -111,13 +124,13 @@ void PzemDcEverySecond(void)

void PzemDcSnsInit(void)
{
PzemDcModbus = new TasmotaModbus(Pin(GPIO_PZEM017_RX), Pin(GPIO_PZEM0XX_TX), Pin(GPIO_NRG_MBS_TX_ENA));
uint8_t result = PzemDcModbus->Begin(9600, SERIAL_8N2);
PzemDc->modbus = new TasmotaModbus(Pin(GPIO_PZEM017_RX), Pin(GPIO_PZEM0XX_TX), Pin(GPIO_NRG_MBS_TX_ENA));
uint8_t result = PzemDc->modbus->Begin(9600, SERIAL_8N2);
if (result) {
if (2 == result) { ClaimSerial(); }
Energy->type_dc = true;
Energy->phase_count = ENERGY_MAX_PHASES; // Start off with three channels
PzemDc.channel = 0;
PzemDc->channel = 0;
} else {
TasmotaGlobal.energy_driver = ENERGY_NONE;
}
Expand All @@ -126,6 +139,11 @@ void PzemDcSnsInit(void)
void PzemDcDrvInit(void)
{
if (PinUsed(GPIO_PZEM017_RX) && PinUsed(GPIO_PZEM0XX_TX)) {
PzemDc = (struct PZEMDC_Data *)calloc(1, sizeof(struct PZEMDC_Data));
if (!PzemDc) {
AddLog(LOG_LEVEL_ERROR, PSTR("PDC: Memory allocation failed"));
return;
}
TasmotaGlobal.energy_driver = XNRG_06;
}
}
Expand All @@ -135,8 +153,25 @@ 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;
PzemDc->address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3
}
else if (CMND_ENERGYCONFIG == Energy->command_code && XdrvMailbox.data_len > 0)
{
char *keyword, *savekeyword, *value, *savevalue, *str = XdrvMailbox.data;
for(;;str=NULL) {
keyword = strtok_r(str, " ,", &savekeyword);
if (!keyword) break;
value = strtok_r(keyword, "=", &savevalue);
value = strtok_r(NULL, "=", &savevalue);
if (!value) break;
char command[CMDSZ];
int command_code = GetCommandCode(command, sizeof(command), keyword, PZEMDC_Commands);
switch (command_code) {
case CMND_PZEMDC_RANGE:
PzemDc->range = atoi(value)/50;
break;
}
}
}
else serviced = false; // Unknown command

Expand Down