From 9d440fd216d5b156dd9e168d6f8793874f21e5dd Mon Sep 17 00:00:00 2001 From: Tercio Gaudencio Filho Date: Thu, 18 Oct 2018 12:35:56 -0300 Subject: [PATCH] Initial implementation of Multiple PZEM004T devices in the same UART(Multidrop bus). Tested with 3 devices. --- code/espurna/config/sensors.h | 12 ++ code/espurna/sensor.ino | 86 ++++++++++++- code/espurna/sensors/PZEM004TSensor.h | 170 ++++++++++++++++++++++---- 3 files changed, 243 insertions(+), 25 deletions(-) diff --git a/code/espurna/config/sensors.h b/code/espurna/config/sensors.h index b0ea4dcc..6a7d8715 100644 --- a/code/espurna/config/sensors.h +++ b/code/espurna/config/sensors.h @@ -600,6 +600,18 @@ #define PZEM004T_HW_PORT Serial // Hardware serial port (if PZEM004T_USE_SOFT == 0) #endif +#ifndef PZEM004T_ADDRESSES +#define PZEM004T_ADDRESSES "192.168.1.1" // Device(s) address(es), separated by space, "192.168.1.1 192.168.1.2 192.168.1.3" +#endif + +#ifndef PZEM004T_READ_INTERVAL +#define PZEM004T_READ_INTERVAL 1500 // Read interval between same device +#endif + +#ifndef PZEM004T_MAX_DEVICES +#define PZEM004T_MAX_DEVICES 3 +#endif + //------------------------------------------------------------------------------ // SHT3X I2C (Wemos) temperature & humidity sensor // Enable support by passing SHT3X_I2C_SUPPORT=1 build flag diff --git a/code/espurna/sensor.ino b/code/espurna/sensor.ino index 48fef408..f2983eda 100644 --- a/code/espurna/sensor.ino +++ b/code/espurna/sensor.ino @@ -42,6 +42,10 @@ unsigned char _sensor_temperature_units = SENSOR_TEMPERATURE_UNITS; double _sensor_temperature_correction = SENSOR_TEMPERATURE_CORRECTION; double _sensor_humidity_correction = SENSOR_HUMIDITY_CORRECTION; +#if PZEM004T_SUPPORT +PZEM004TSensor *pzem004t_sensor; +#endif + String _sensor_energy_reset_ts = String(); // ----------------------------------------------------------------------------- @@ -272,6 +276,63 @@ void _sensorInitCommands() { } DEBUG_MSG_P(PSTR("+OK\n")); }); + #if PZEM004T_SUPPORT + settingsRegisterCommand(F("PZ.ADDRESS"), [](Embedis* e) { + if (e->argc == 1) { + DEBUG_MSG_P(PSTR("[SENSOR] PZEM004T\n")); + unsigned char dev_count = pzem004t_sensor->getAddressesCount(); + for(unsigned char dev = 0; dev < dev_count; dev++) { + DEBUG_MSG_P(PSTR("Device %d Address %s\n"), dev, pzem004t_sensor->getAddress(dev).c_str()); + } + DEBUG_MSG_P(PSTR("+OK\n")); + } else if(e->argc == 2) { + IPAddress addr; + if (addr.fromString(String(e->argv[1]))) { + if(pzem004t_sensor->setDeviceAddress(&addr)) { + DEBUG_MSG_P(PSTR("+OK\n")); + } + } else { + DEBUG_MSG_P(PSTR("-ERROR: Invalid address argument\n")); + } + } else { + DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n")); + } + }); + settingsRegisterCommand(F("PZ.RESET"), [](Embedis* e) { + if(e->argc > 2) { + DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n")); + } else { + unsigned char init = e->argc == 2 ? String(e->argv[1]).toInt() : 0; + unsigned char limit = e->argc == 2 ? init +1 : pzem004t_sensor->getAddressesCount(); + DEBUG_MSG_P(PSTR("[SENSOR] PZEM004T\n")); + for(unsigned char dev = init; dev < limit; dev++) { + float offset = pzem004t_sensor->resetEnergy(dev); + setSetting("pzEneTotal", dev, offset); + DEBUG_MSG_P(PSTR("Device %d Address %s - Offset: %s\n"), dev, pzem004t_sensor->getAddress(dev).c_str(), String(offset).c_str()); + } + DEBUG_MSG_P(PSTR("+OK\n")); + } + }); + settingsRegisterCommand(F("PZ.VALUE"), [](Embedis* e) { + if(e->argc > 2) { + DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n")); + } else { + unsigned char init = e->argc == 2 ? String(e->argv[1]).toInt() : 0; + unsigned char limit = e->argc == 2 ? init +1 : pzem004t_sensor->getAddressesCount(); + DEBUG_MSG_P(PSTR("[SENSOR] PZEM004T\n")); + for(unsigned char dev = init; dev < limit; dev++) { + DEBUG_MSG_P(PSTR("Device %d/%s - Current: %s Voltage: %s Power: %s Energy: %s\n"), // + dev, + pzem004t_sensor->getAddress(dev).c_str(), + String(pzem004t_sensor->value(dev * PZ_MAGNITUDE_CURRENT_INDEX)).c_str(), + String(pzem004t_sensor->value(dev * PZ_MAGNITUDE_VOLTAGE_INDEX)).c_str(), + String(pzem004t_sensor->value(dev * PZ_MAGNITUDE_POWER_ACTIVE_INDEX)).c_str(), + String(pzem004t_sensor->value(dev * PZ_MAGNITUDE_ENERGY_INDEX)).c_str()); + } + DEBUG_MSG_P(PSTR("+OK\n")); + } + }); + #endif } #endif @@ -593,13 +654,20 @@ void _sensorLoad() { #if PZEM004T_SUPPORT { - PZEM004TSensor * sensor = new PZEM004TSensor(); + PZEM004TSensor * sensor = pzem004t_sensor = new PZEM004TSensor(); #if PZEM004T_USE_SOFT sensor->setRX(PZEM004T_RX_PIN); sensor->setTX(PZEM004T_TX_PIN); #else sensor->setSerial(& PZEM004T_HW_PORT); #endif + sensor->setAddresses(PZEM004T_ADDRESSES); + // Read saved energy offset + unsigned char dev_count = sensor->getAddressesCount(); + for(unsigned char dev = 0; dev < dev_count; dev++) { + float value = getSetting("pzEneTotal", dev, 0).toFloat(); + if (value > 0) sensor->resetEnergy(dev, value); + } _sensors.push_back(sensor); } #endif @@ -970,6 +1038,22 @@ void _sensorConfigure() { #endif // CSE7766_SUPPORT + #if PZEM004T_SUPPORT + + if (_sensors[i]->getID() == SENSOR_PZEM004T_ID) { + PZEM004TSensor * sensor = (PZEM004TSensor *) _sensors[i]; + if (getSetting("pwrResetE", 0).toInt() == 1) { + unsigned char dev_count = sensor->getAddressesCount(); + for(unsigned char dev = 0; dev < dev_count; dev++) { + sensor->resetEnergy(dev, 0); + delSetting("pzEneTotal", dev); + } + _sensorResetTS(); + } + } + + #endif // PZEM004T_SUPPORT + } // Update filter sizes diff --git a/code/espurna/sensors/PZEM004TSensor.h b/code/espurna/sensors/PZEM004TSensor.h index 57d5ba3e..85a60f87 100644 --- a/code/espurna/sensors/PZEM004TSensor.h +++ b/code/espurna/sensors/PZEM004TSensor.h @@ -12,6 +12,13 @@ #include +#define PZ_MAGNITUDE_COUNT 4 + +#define PZ_MAGNITUDE_CURRENT_INDEX 0 +#define PZ_MAGNITUDE_VOLTAGE_INDEX 1 +#define PZ_MAGNITUDE_POWER_ACTIVE_INDEX 2 +#define PZ_MAGNITUDE_ENERGY_INDEX 3 + class PZEM004TSensor : public BaseSensor { public: @@ -21,9 +28,7 @@ class PZEM004TSensor : public BaseSensor { // --------------------------------------------------------------------- PZEM004TSensor(): BaseSensor() { - _count = 4; _sensor_id = SENSOR_PZEM004T_ID; - _ip = IPAddress(192,168,1,1); } ~PZEM004TSensor() { @@ -49,6 +54,53 @@ class PZEM004TSensor : public BaseSensor { _dirty = true; } + // Set the devices physical addresses managed by this sensor + void setAddresses(const char *addresses) { + char const * sep = " "; + char tokens[strlen(addresses) + 1]; + strlcpy(tokens, addresses, sizeof(tokens)); + char *address = tokens; + + int i = 0; + address = strtok(address, sep); + while (address != 0 && i++ < PZEM004T_MAX_DEVICES) { + IPAddress addr; + reading_t reading; + reading.current = PZEM_ERROR_VALUE; + reading.voltage = PZEM_ERROR_VALUE; + reading.power = PZEM_ERROR_VALUE; + reading.energy = PZEM_ERROR_VALUE; + if (addr.fromString(address)) { + _devices.push_back(addr); + _energy_offsets.push_back(0); + _readings.push_back(reading); + } + address = strtok(0, sep); + } + _count = _devices.size() * PZ_MAGNITUDE_COUNT; + _dirty = true; + } + + // Return the number of devices managed by this sensor + unsigned char getAddressesCount() { + return _devices.size(); + } + + // Get device physical address based on the device index + String getAddress(unsigned char dev) { + return _devices[dev].toString(); + } + + // Set the device physical address + bool setDeviceAddress(IPAddress *addr) { + while(_busy) { yield(); }; + + _busy = true; + bool res = _pzem->setAddress(*addr); + _busy = false; + return res; + } + // --------------------------------------------------------------------- unsigned char getRX() { @@ -61,12 +113,11 @@ class PZEM004TSensor : public BaseSensor { // --------------------------------------------------------------------- - void resetEnergy(double value = 0) { - if (_ready) { - _energy_offset = value - (_pzem->energy(_ip) * 3600); - } else { - _energy_offset = value; - } + // If called with value = -1, the offset will be the last energy reading + // otherwise, it will be the value provided + float resetEnergy(unsigned char dev, float value = -1) { + _energy_offsets[dev] = value != 0 ? value : _readings[dev].energy; + return _energy_offsets[dev]; } // --------------------------------------------------------------------- @@ -75,7 +126,6 @@ class PZEM004TSensor : public BaseSensor { // Initialization method, must be idempotent void begin() { - if (!_dirty) return; if (_pzem) delete _pzem; @@ -84,16 +134,15 @@ class PZEM004TSensor : public BaseSensor { } else { _pzem = new PZEM004T(_pin_rx, _pin_tx); } - _pzem->setAddress(_ip); + if(_devices.size() == 1) _pzem->setAddress(_devices[0]); _ready = true; _dirty = false; - } // Descriptive name of the sensor String description() { - char buffer[28]; + char buffer[27]; if (_serial) { snprintf(buffer, sizeof(buffer), "PZEM004T @ HwSerial"); } else { @@ -104,34 +153,99 @@ class PZEM004TSensor : public BaseSensor { // Descriptive name of the slot # index String slot(unsigned char index) { - return description(); + int dev = index / PZ_MAGNITUDE_COUNT; + char buffer[25]; + snprintf(buffer, sizeof(buffer), "(%u/%s)", dev, getAddress(dev).c_str()); + return description() + String(buffer); }; // Address of the sensor (it could be the GPIO or I2C address) String address(unsigned char index) { - return _ip.toString(); + int dev = index / PZ_MAGNITUDE_COUNT; + return _devices[dev].toString(); } // Type for slot # index unsigned char type(unsigned char index) { - if (index == 0) return MAGNITUDE_CURRENT; - if (index == 1) return MAGNITUDE_VOLTAGE; - if (index == 2) return MAGNITUDE_POWER_ACTIVE; - if (index == 3) return MAGNITUDE_ENERGY; + int dev = index / PZ_MAGNITUDE_COUNT; + index = index - (dev * PZ_MAGNITUDE_COUNT); + if (index == PZ_MAGNITUDE_CURRENT_INDEX) return MAGNITUDE_CURRENT; + if (index == PZ_MAGNITUDE_VOLTAGE_INDEX) return MAGNITUDE_VOLTAGE; + if (index == PZ_MAGNITUDE_POWER_ACTIVE_INDEX) return MAGNITUDE_POWER_ACTIVE; + if (index == PZ_MAGNITUDE_ENERGY_INDEX) return MAGNITUDE_ENERGY; return MAGNITUDE_NONE; } // Current value for slot # index double value(unsigned char index) { + int dev = index / PZ_MAGNITUDE_COUNT; + index = index - (dev * PZ_MAGNITUDE_COUNT); double response = 0; - if (index == 0) response = _pzem->current(_ip); - if (index == 1) response = _pzem->voltage(_ip); - if (index == 2) response = _pzem->power(_ip); - if (index == 3) response = _energy_offset + (_pzem->energy(_ip) * 3600); + if (index == PZ_MAGNITUDE_CURRENT_INDEX) response = _readings[dev].current; + if (index == PZ_MAGNITUDE_VOLTAGE_INDEX) response = _readings[dev].voltage; + if (index == PZ_MAGNITUDE_POWER_ACTIVE_INDEX) response = _readings[dev].power; + if (index == PZ_MAGNITUDE_ENERGY_INDEX) response = (_readings[dev].energy * 3600) - _energy_offsets[dev]; if (response < 0) response = 0; return response; } + // Post-read hook (usually to reset things) + void post() { + _error = SENSOR_ERROR_OK; + } + + // Loop-like method, call it in your main loop + void tick() { + static unsigned char dev = 0; + static unsigned char magnitude = 0; + static unsigned long last_millis = 0; + + if (_busy || millis() - last_millis < PZEM004T_READ_INTERVAL) return; + + _busy = true; + + // Clear buffer in case of late response(Timeout) + while(Serial.available() > 0) Serial.read(); + + float read; + float* readings_p; + switch(magnitude) { + case PZ_MAGNITUDE_CURRENT_INDEX: + read = _pzem->current(_devices[dev]); + readings_p = &_readings[dev].current; + break; + case PZ_MAGNITUDE_VOLTAGE_INDEX: + read = _pzem->voltage(_devices[dev]); + readings_p = &_readings[dev].voltage; + break; + case PZ_MAGNITUDE_POWER_ACTIVE_INDEX: + read = _pzem->power(_devices[dev]); + readings_p = &_readings[dev].power; + break; + case PZ_MAGNITUDE_ENERGY_INDEX: + read = _pzem->energy(_devices[dev]); + readings_p = &_readings[dev].energy; + break; + default: + _busy = false; + return; + } + if(read == PZEM_ERROR_VALUE) { + _error = SENSOR_ERROR_TIMEOUT; + } else { + *readings_p = read; + } + + if(++dev == _devices.size()) { + dev = 0; + last_millis = millis(); + if(++magnitude == PZ_MAGNITUDE_COUNT) { + magnitude = 0; + } + } + _busy = false; + } + protected: // --------------------------------------------------------------------- @@ -140,10 +254,18 @@ class PZEM004TSensor : public BaseSensor { unsigned int _pin_rx = PZEM004T_RX_PIN; unsigned int _pin_tx = PZEM004T_TX_PIN; - IPAddress _ip; + bool _busy = false; + typedef struct { + float voltage; + float current; + float power; + float energy; + } reading_t; + std::vector _readings; + std::vector _energy_offsets; + std::vector _devices; HardwareSerial * _serial = NULL; PZEM004T * _pzem = NULL; - double _energy_offset = 0; };