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.

256 lines
7.4 KiB

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