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.

259 lines
7.4 KiB

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