Browse Source

Merge pull request #1304 from 0x3333/multipzem

Multiple PZEM004T on same UART
pull/1327/head
Xose Pérez 5 years ago
committed by GitHub
parent
commit
ecc56c4f2a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 243 additions and 25 deletions
  1. +12
    -0
      code/espurna/config/sensors.h
  2. +85
    -1
      code/espurna/sensor.ino
  3. +146
    -24
      code/espurna/sensors/PZEM004TSensor.h

+ 12
- 0
code/espurna/config/sensors.h View File

@ -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


+ 85
- 1
code/espurna/sensor.ino View File

@ -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


+ 146
- 24
code/espurna/sensors/PZEM004TSensor.h View File

@ -12,6 +12,13 @@
#include <PZEM004T.h>
#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 != -1 ? 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<reading_t> _readings;
std::vector<float> _energy_offsets;
std::vector<IPAddress> _devices;
HardwareSerial * _serial = NULL;
PZEM004T * _pzem = NULL;
double _energy_offset = 0;
};


Loading…
Cancel
Save