@ -3,6 +3,48 @@
/ / Copyright ( C ) 2018 by Xose Pérez < xose dot perez at gmail dot com >
/ / Copyright ( C ) 2018 by Xose Pérez < xose dot perez at gmail dot com >
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/ / Connection Diagram :
/ / - - - - - - - - - - - - - - - - - - -
/ /
/ / Needed when connecting multiple PZEM004T devices on the same UART
/ / * You must set the PZEM004T device address prior using this configuration *
/ /
/ / + - - - - - - - - - +
/ / | ESPurna | + VCC
/ / | Node | ^
/ / | G T R | |
/ / + - + - - + - - + - + R ( 10 K )
/ / | | | |
/ / | | + - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - +
/ / | + - - - - - - - - - - - - - - - - - + - - | - - - - - - - - - - - - + - - | - - - - - - - - - - - - + |
/ / + - - - - - - - - - - - - - - - - - + - - | - - | - - - - - - - - - + - - | - - | - - - - - - - - - + | |
/ / | | | | | | | | |
/ / | | V | | V | | V
/ / | | - | | - | | -
/ / + - + - - + - - + - + + - + - - + - - + - + + - + - - + - - + - +
/ / | G R T | | G R T | | G R T |
/ / | PZEM - 004 T | | PZEM - 004 T | | PZEM - 004 T |
/ / | Module | | Module | | Module |
/ / + - - - - - - - - - + + - - - - - - - - - + + - - - - - - - - - +
/ /
/ / Where :
/ / - - - - - -
/ / G = GND
/ / R = ESPurna UART RX
/ / T = ESPurna UART TX
/ / V = Small Signal Schottky Diode , like BAT43 ,
/ / Cathode to PZEM TX , Anode to Espurna RX
/ / R = Resistor to VCC , 10 K
/ /
/ / More Info :
/ / - - - - - - - - - -
/ / See ESPurna Wiki - https : / / github . com / xoseperez / espurna / wiki / Sensor - PZEM004T
/ /
/ / Reference :
/ / - - - - - - - - - -
/ / UART / TTL - Serial network with single master and multiple slaves :
/ / http : / / cool - emerald . blogspot . com / 2009 / 10 / multidrop - network - for - rs232 . html
# if SENSOR_SUPPORT && PZEM004T_SUPPORT
# if SENSOR_SUPPORT && PZEM004T_SUPPORT
# pragma once
# pragma once
@ -12,6 +54,13 @@
# include <PZEM004T.h>
# 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 {
class PZEM004TSensor : public BaseSensor {
public :
public :
@ -21,9 +70,7 @@ class PZEM004TSensor : public BaseSensor {
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PZEM004TSensor ( ) : BaseSensor ( ) {
PZEM004TSensor ( ) : BaseSensor ( ) {
_count = 4 ;
_sensor_id = SENSOR_PZEM004T_ID ;
_sensor_id = SENSOR_PZEM004T_ID ;
_ip = IPAddress ( 192 , 168 , 1 , 1 ) ;
}
}
~ PZEM004TSensor ( ) {
~ PZEM004TSensor ( ) {
@ -49,6 +96,53 @@ class PZEM004TSensor : public BaseSensor {
_dirty = true ;
_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 ( ) {
unsigned char getRX ( ) {
@ -61,12 +155,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 +168,6 @@ class PZEM004TSensor : public BaseSensor {
/ / Initialization method , must be idempotent
/ / Initialization method , must be idempotent
void begin ( ) {
void begin ( ) {
if ( ! _dirty ) return ;
if ( ! _dirty ) return ;
if ( _pzem ) delete _pzem ;
if ( _pzem ) delete _pzem ;
@ -84,16 +176,15 @@ class PZEM004TSensor : public BaseSensor {
} else {
} else {
_pzem = new PZEM004T ( _pin_rx , _pin_tx ) ;
_pzem = new PZEM004T ( _pin_rx , _pin_tx ) ;
}
}
_pzem - > setAddress ( _ip ) ;
if ( _devices . size ( ) = = 1 ) _pzem - > setAddress ( _dev ices [ 0 ] ) ;
_ready = true ;
_ready = true ;
_dirty = false ;
_dirty = false ;
}
}
/ / Descriptive name of the sensor
/ / Descriptive name of the sensor
String description ( ) {
String description ( ) {
char buffer [ 28 ] ;
char buffer [ 27 ] ;
if ( _serial ) {
if ( _serial ) {
snprintf ( buffer , sizeof ( buffer ) , " PZEM004T @ HwSerial " ) ;
snprintf ( buffer , sizeof ( buffer ) , " PZEM004T @ HwSerial " ) ;
} else {
} else {
@ -104,34 +195,99 @@ class PZEM004TSensor : public BaseSensor {
/ / Descriptive name of the slot # index
/ / Descriptive name of the slot # index
String slot ( unsigned char 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 )
/ / Address of the sensor ( it could be the GPIO or I2C address )
String address ( unsigned char index ) {
String address ( unsigned char index ) {
return _ip . toString ( ) ;
int dev = index / PZ_MAGNITUDE_COUNT ;
return _devices [ dev ] . toString ( ) ;
}
}
/ / Type for slot # index
/ / Type for slot # index
unsigned char type ( unsigned char 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 ;
return MAGNITUDE_NONE ;
}
}
/ / Current value for slot # index
/ / Current value for slot # index
double value ( unsigned char index ) {
double value ( unsigned char index ) {
int dev = index / PZ_MAGNITUDE_COUNT ;
index = index - ( dev * PZ_MAGNITUDE_COUNT ) ;
double response = 0 ;
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 ;
if ( response < 0 ) response = 0 ;
return response ;
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 :
protected :
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/ / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -140,10 +296,18 @@ class PZEM004TSensor : public BaseSensor {
unsigned int _pin_rx = PZEM004T_RX_PIN ;
unsigned int _pin_rx = PZEM004T_RX_PIN ;
unsigned int _pin_tx = PZEM004T_TX_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 ;
HardwareSerial * _serial = NULL ;
PZEM004T * _pzem = NULL ;
PZEM004T * _pzem = NULL ;
double _energy_offset = 0 ;
} ;
} ;