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.

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