Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
9.1 KiB

6 years ago
  1. // -----------------------------------------------------------------------------
  2. // PZEM004T based power monitor
  3. // Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. // -----------------------------------------------------------------------------
  5. #if SENSOR_SUPPORT && PZEM004T_SUPPORT
  6. #pragma once
  7. #include "Arduino.h"
  8. #include "BaseSensor.h"
  9. #include <PZEM004T.h>
  10. #define PZ_MAGNITUDE_COUNT 4
  11. #define PZ_MAGNITUDE_CURRENT_INDEX 0
  12. #define PZ_MAGNITUDE_VOLTAGE_INDEX 1
  13. #define PZ_MAGNITUDE_POWER_ACTIVE_INDEX 2
  14. #define PZ_MAGNITUDE_ENERGY_INDEX 3
  15. class PZEM004TSensor : public BaseSensor {
  16. public:
  17. // ---------------------------------------------------------------------
  18. // Public
  19. // ---------------------------------------------------------------------
  20. PZEM004TSensor(): BaseSensor() {
  21. _sensor_id = SENSOR_PZEM004T_ID;
  22. }
  23. ~PZEM004TSensor() {
  24. if (_pzem) delete _pzem;
  25. }
  26. // ---------------------------------------------------------------------
  27. void setRX(unsigned char pin_rx) {
  28. if (_pin_rx == pin_rx) return;
  29. _pin_rx = pin_rx;
  30. _dirty = true;
  31. }
  32. void setTX(unsigned char pin_tx) {
  33. if (_pin_tx == pin_tx) return;
  34. _pin_tx = pin_tx;
  35. _dirty = true;
  36. }
  37. void setSerial(HardwareSerial * serial) {
  38. _serial = serial;
  39. _dirty = true;
  40. }
  41. // Set the devices physical addresses managed by this sensor
  42. void setAddresses(const char *addresses) {
  43. char const * sep = " ";
  44. char tokens[strlen(addresses) + 1];
  45. strlcpy(tokens, addresses, sizeof(tokens));
  46. char *address = tokens;
  47. int i = 0;
  48. address = strtok(address, sep);
  49. while (address != 0 && i++ < PZEM004T_MAX_DEVICES) {
  50. IPAddress addr;
  51. reading_t reading;
  52. reading.current = PZEM_ERROR_VALUE;
  53. reading.voltage = PZEM_ERROR_VALUE;
  54. reading.power = PZEM_ERROR_VALUE;
  55. reading.energy = PZEM_ERROR_VALUE;
  56. if (addr.fromString(address)) {
  57. _devices.push_back(addr);
  58. _energy_offsets.push_back(0);
  59. _readings.push_back(reading);
  60. }
  61. address = strtok(0, sep);
  62. }
  63. _count = _devices.size() * PZ_MAGNITUDE_COUNT;
  64. _dirty = true;
  65. }
  66. // Return the number of devices managed by this sensor
  67. unsigned char getAddressesCount() {
  68. return _devices.size();
  69. }
  70. // Get device physical address based on the device index
  71. String getAddress(unsigned char dev) {
  72. return _devices[dev].toString();
  73. }
  74. // Set the device physical address
  75. bool setDeviceAddress(IPAddress *addr) {
  76. while(_busy) { yield(); };
  77. _busy = true;
  78. bool res = _pzem->setAddress(*addr);
  79. _busy = false;
  80. return res;
  81. }
  82. // ---------------------------------------------------------------------
  83. unsigned char getRX() {
  84. return _pin_rx;
  85. }
  86. unsigned char getTX() {
  87. return _pin_tx;
  88. }
  89. // ---------------------------------------------------------------------
  90. // If called with value = -1, the offset will be the last energy reading
  91. // otherwise, it will be the value provided
  92. float resetEnergy(unsigned char dev, float value = -1) {
  93. _energy_offsets[dev] = value != -1 ? value : _readings[dev].energy;
  94. return _energy_offsets[dev];
  95. }
  96. // ---------------------------------------------------------------------
  97. // Sensor API
  98. // ---------------------------------------------------------------------
  99. // Initialization method, must be idempotent
  100. void begin() {
  101. if (!_dirty) return;
  102. if (_pzem) delete _pzem;
  103. if (_serial) {
  104. _pzem = new PZEM004T(_serial);
  105. } else {
  106. _pzem = new PZEM004T(_pin_rx, _pin_tx);
  107. }
  108. if(_devices.size() == 1) _pzem->setAddress(_devices[0]);
  109. _ready = true;
  110. _dirty = false;
  111. }
  112. // Descriptive name of the sensor
  113. String description() {
  114. char buffer[27];
  115. if (_serial) {
  116. snprintf(buffer, sizeof(buffer), "PZEM004T @ HwSerial");
  117. } else {
  118. snprintf(buffer, sizeof(buffer), "PZEM004T @ SwSerial(%u,%u)", _pin_rx, _pin_tx);
  119. }
  120. return String(buffer);
  121. }
  122. // Descriptive name of the slot # index
  123. String slot(unsigned char index) {
  124. int dev = index / PZ_MAGNITUDE_COUNT;
  125. char buffer[25];
  126. snprintf(buffer, sizeof(buffer), "(%u/%s)", dev, getAddress(dev).c_str());
  127. return description() + String(buffer);
  128. };
  129. // Address of the sensor (it could be the GPIO or I2C address)
  130. String address(unsigned char index) {
  131. int dev = index / PZ_MAGNITUDE_COUNT;
  132. return _devices[dev].toString();
  133. }
  134. // Type for slot # index
  135. unsigned char type(unsigned char index) {
  136. int dev = index / PZ_MAGNITUDE_COUNT;
  137. index = index - (dev * PZ_MAGNITUDE_COUNT);
  138. if (index == PZ_MAGNITUDE_CURRENT_INDEX) return MAGNITUDE_CURRENT;
  139. if (index == PZ_MAGNITUDE_VOLTAGE_INDEX) return MAGNITUDE_VOLTAGE;
  140. if (index == PZ_MAGNITUDE_POWER_ACTIVE_INDEX) return MAGNITUDE_POWER_ACTIVE;
  141. if (index == PZ_MAGNITUDE_ENERGY_INDEX) return MAGNITUDE_ENERGY;
  142. return MAGNITUDE_NONE;
  143. }
  144. // Current value for slot # index
  145. double value(unsigned char index) {
  146. int dev = index / PZ_MAGNITUDE_COUNT;
  147. index = index - (dev * PZ_MAGNITUDE_COUNT);
  148. double response = 0;
  149. if (index == PZ_MAGNITUDE_CURRENT_INDEX) response = _readings[dev].current;
  150. if (index == PZ_MAGNITUDE_VOLTAGE_INDEX) response = _readings[dev].voltage;
  151. if (index == PZ_MAGNITUDE_POWER_ACTIVE_INDEX) response = _readings[dev].power;
  152. if (index == PZ_MAGNITUDE_ENERGY_INDEX) response = (_readings[dev].energy * 3600) - _energy_offsets[dev];
  153. if (response < 0) response = 0;
  154. return response;
  155. }
  156. // Post-read hook (usually to reset things)
  157. void post() {
  158. _error = SENSOR_ERROR_OK;
  159. }
  160. // Loop-like method, call it in your main loop
  161. void tick() {
  162. static unsigned char dev = 0;
  163. static unsigned char magnitude = 0;
  164. static unsigned long last_millis = 0;
  165. if (_busy || millis() - last_millis < PZEM004T_READ_INTERVAL) return;
  166. _busy = true;
  167. // Clear buffer in case of late response(Timeout)
  168. while(Serial.available() > 0) Serial.read();
  169. float read;
  170. float* readings_p;
  171. switch(magnitude) {
  172. case PZ_MAGNITUDE_CURRENT_INDEX:
  173. read = _pzem->current(_devices[dev]);
  174. readings_p = &_readings[dev].current;
  175. break;
  176. case PZ_MAGNITUDE_VOLTAGE_INDEX:
  177. read = _pzem->voltage(_devices[dev]);
  178. readings_p = &_readings[dev].voltage;
  179. break;
  180. case PZ_MAGNITUDE_POWER_ACTIVE_INDEX:
  181. read = _pzem->power(_devices[dev]);
  182. readings_p = &_readings[dev].power;
  183. break;
  184. case PZ_MAGNITUDE_ENERGY_INDEX:
  185. read = _pzem->energy(_devices[dev]);
  186. readings_p = &_readings[dev].energy;
  187. break;
  188. default:
  189. _busy = false;
  190. return;
  191. }
  192. if(read == PZEM_ERROR_VALUE) {
  193. _error = SENSOR_ERROR_TIMEOUT;
  194. } else {
  195. *readings_p = read;
  196. }
  197. if(++dev == _devices.size()) {
  198. dev = 0;
  199. last_millis = millis();
  200. if(++magnitude == PZ_MAGNITUDE_COUNT) {
  201. magnitude = 0;
  202. }
  203. }
  204. _busy = false;
  205. }
  206. protected:
  207. // ---------------------------------------------------------------------
  208. // Protected
  209. // ---------------------------------------------------------------------
  210. unsigned int _pin_rx = PZEM004T_RX_PIN;
  211. unsigned int _pin_tx = PZEM004T_TX_PIN;
  212. bool _busy = false;
  213. typedef struct {
  214. float voltage;
  215. float current;
  216. float power;
  217. float energy;
  218. } reading_t;
  219. std::vector<reading_t> _readings;
  220. std::vector<float> _energy_offsets;
  221. std::vector<IPAddress> _devices;
  222. HardwareSerial * _serial = NULL;
  223. PZEM004T * _pzem = NULL;
  224. };
  225. #endif // SENSOR_SUPPORT && PZEM004T_SUPPORT