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.

271 lines
7.9 KiB

6 years ago
  1. // -----------------------------------------------------------------------------
  2. // V9261F based power monitor
  3. // Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. // -----------------------------------------------------------------------------
  5. #if SENSOR_SUPPORT && V9261F_SUPPORT
  6. #pragma once
  7. #include <SoftwareSerial.h>
  8. #include "BaseEmonSensor.h"
  9. extern "C" {
  10. #include "../libs/fs_math.h"
  11. }
  12. class V9261FSensor : public BaseEmonSensor {
  13. public:
  14. // ---------------------------------------------------------------------
  15. // Public
  16. // ---------------------------------------------------------------------
  17. V9261FSensor(): _data() {
  18. _count = 6;
  19. _sensor_id = SENSOR_V9261F_ID;
  20. }
  21. ~V9261FSensor() {
  22. if (_serial) delete _serial;
  23. }
  24. // ---------------------------------------------------------------------
  25. void setRX(unsigned char pin_rx) {
  26. if (_pin_rx == pin_rx) return;
  27. _pin_rx = pin_rx;
  28. _dirty = true;
  29. }
  30. void setInverted(bool inverted) {
  31. if (_inverted == inverted) return;
  32. _inverted = inverted;
  33. _dirty = true;
  34. }
  35. // ---------------------------------------------------------------------
  36. unsigned char getRX() {
  37. return _pin_rx;
  38. }
  39. bool getInverted() {
  40. return _inverted;
  41. }
  42. // ---------------------------------------------------------------------
  43. // Sensor API
  44. // ---------------------------------------------------------------------
  45. // Initialization method, must be idempotent
  46. void begin() {
  47. if (!_dirty) return;
  48. if (_serial) delete _serial;
  49. _serial = new SoftwareSerial(_pin_rx, -1, _inverted);
  50. _serial->enableIntTx(false);
  51. _serial->begin(V9261F_BAUDRATE);
  52. _ready = true;
  53. _dirty = false;
  54. }
  55. // Descriptive name of the sensor
  56. String description() {
  57. char buffer[28];
  58. snprintf(buffer, sizeof(buffer), "V9261F @ SwSerial(%u,NULL)", _pin_rx);
  59. return String(buffer);
  60. }
  61. // Descriptive name of the slot # index
  62. String description(unsigned char index) {
  63. return description();
  64. };
  65. // Address of the sensor (it could be the GPIO or I2C address)
  66. String address(unsigned char index) {
  67. return String(_pin_rx);
  68. }
  69. // Loop-like method, call it in your main loop
  70. void tick() {
  71. _read();
  72. }
  73. // Type for slot # index
  74. unsigned char type(unsigned char index) {
  75. if (index == 0) return MAGNITUDE_CURRENT;
  76. if (index == 1) return MAGNITUDE_VOLTAGE;
  77. if (index == 2) return MAGNITUDE_POWER_ACTIVE;
  78. if (index == 3) return MAGNITUDE_POWER_REACTIVE;
  79. if (index == 4) return MAGNITUDE_POWER_APPARENT;
  80. if (index == 5) return MAGNITUDE_POWER_FACTOR;
  81. if (index == 6) return MAGNITUDE_ENERGY;
  82. return MAGNITUDE_NONE;
  83. }
  84. // Current value for slot # index
  85. double value(unsigned char index) {
  86. if (index == 0) return _current;
  87. if (index == 1) return _voltage;
  88. if (index == 2) return _active;
  89. if (index == 3) return _reactive;
  90. if (index == 4) return _apparent;
  91. if (index == 5) return _apparent > 0 ? 100 * _active / _apparent : 100;
  92. if (index == 6) return _energy[0].asDouble();
  93. return 0;
  94. }
  95. protected:
  96. // ---------------------------------------------------------------------
  97. // Protected
  98. // ---------------------------------------------------------------------
  99. void _read() {
  100. static unsigned char state = 0;
  101. static unsigned long last = 0;
  102. static unsigned long ts = 0;
  103. static bool found = false;
  104. static unsigned char index = 0;
  105. if (state == 0) {
  106. while (_serial->available()) {
  107. _serial->flush();
  108. found = true;
  109. ts = millis();
  110. }
  111. if (found && (millis() - ts > V9261F_SYNC_INTERVAL)) {
  112. _serial->flush();
  113. index = 0;
  114. state = 1;
  115. }
  116. } else if (state == 1) {
  117. while (_serial->available()) {
  118. _serial->read();
  119. if (index++ >= 7) {
  120. _serial->flush();
  121. index = 0;
  122. state = 2;
  123. }
  124. }
  125. } else if (state == 2) {
  126. while (_serial->available()) {
  127. _data[index] = _serial->read();
  128. if (index++ >= 19) {
  129. _serial->flush();
  130. ts = millis();
  131. state = 3;
  132. }
  133. }
  134. } else if (state == 3) {
  135. if (_checksum()) {
  136. _active = (double) (
  137. (_data[3]) +
  138. (_data[4] << 8) +
  139. (_data[5] << 16) +
  140. (_data[6] << 24)
  141. ) / _ratioP;
  142. _reactive = (double) (
  143. (_data[7]) +
  144. (_data[8] << 8) +
  145. (_data[9] << 16) +
  146. (_data[10] << 24)
  147. ) / _ratioR;
  148. _voltage = (double) (
  149. (_data[11]) +
  150. (_data[12] << 8) +
  151. (_data[13] << 16) +
  152. (_data[14] << 24)
  153. ) / _ratioV;
  154. _current = (double) (
  155. (_data[15]) +
  156. (_data[16] << 8) +
  157. (_data[17] << 16) +
  158. (_data[18] << 24)
  159. ) / _ratioC;
  160. if (_active < 0) _active = 0;
  161. if (_reactive < 0) _reactive = 0;
  162. if (_voltage < 0) _voltage = 0;
  163. if (_current < 0) _current = 0;
  164. _apparent = fs_sqrt(_reactive * _reactive + _active * _active);
  165. if (last > 0) {
  166. _energy[0] += sensor::Ws {
  167. static_cast<uint32_t>(_active * (millis() / last) / 1000)
  168. };
  169. }
  170. last = millis();
  171. }
  172. ts = millis();
  173. index = 0;
  174. state = 4;
  175. } else if (state == 4) {
  176. while (_serial->available()) {
  177. _serial->flush();
  178. ts = millis();
  179. }
  180. if (millis() - ts > V9261F_SYNC_INTERVAL) {
  181. state = 1;
  182. }
  183. }
  184. }
  185. bool _checksum() {
  186. unsigned char checksum = 0;
  187. for (unsigned char i = 0; i < 19; i++) {
  188. checksum = checksum + _data[i];
  189. }
  190. checksum = ~checksum + 0x33;
  191. return checksum == _data[19];
  192. }
  193. // ---------------------------------------------------------------------
  194. unsigned int _pin_rx = V9261F_PIN;
  195. bool _inverted = V9261F_PIN_INVERSE;
  196. SoftwareSerial * _serial = NULL;
  197. double _active = 0;
  198. double _reactive = 0;
  199. double _voltage = 0;
  200. double _current = 0;
  201. double _apparent = 0;
  202. double _ratioP = V9261F_POWER_FACTOR;
  203. double _ratioC = V9261F_CURRENT_FACTOR;
  204. double _ratioV = V9261F_VOLTAGE_FACTOR;
  205. double _ratioR = V9261F_RPOWER_FACTOR;
  206. unsigned char _data[24];
  207. };
  208. #endif // SENSOR_SUPPORT && V9261F_SUPPORT