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.

305 lines
8.4 KiB

  1. /*
  2. V9261F MODULE
  3. Support for V9261D-based power monitors
  4. Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
  5. */
  6. /*
  7. #if V9261F_SUPPORT
  8. #include <SoftwareSerial.h>
  9. #include <ArduinoJson.h>
  10. SoftwareSerial * _v9261f_uart;
  11. bool _v9261f_enabled = false;
  12. bool _v9261f_ready = false;
  13. bool _v9261f_newdata = false;
  14. int _v9261f_power = 0;
  15. int _v9261f_rpower = 0;
  16. int _v9261f_voltage = 0;
  17. double _v9261f_current = 0;
  18. unsigned char _v9261f_data[24];
  19. // -----------------------------------------------------------------------------
  20. // PRIVATE
  21. // -----------------------------------------------------------------------------
  22. void v9261fRead() {
  23. static unsigned char state = 0;
  24. static unsigned long last = 0;
  25. static bool found = false;
  26. static unsigned char index = 0;
  27. if (state == 0) {
  28. while (_v9261f_uart->available()) {
  29. _v9261f_uart->flush();
  30. found = true;
  31. last = millis();
  32. }
  33. if (found && (millis() - last > V9261F_SYNC_INTERVAL)) {
  34. _v9261f_uart->flush();
  35. index = 0;
  36. state = 1;
  37. }
  38. } else if (state == 1) {
  39. while (_v9261f_uart->available()) {
  40. _v9261f_uart->read();
  41. if (index++ >= 7) {
  42. _v9261f_uart->flush();
  43. index = 0;
  44. state = 2;
  45. }
  46. }
  47. } else if (state == 2) {
  48. while (_v9261f_uart->available()) {
  49. _v9261f_data[index] = _v9261f_uart->read();
  50. if (index++ >= 19) {
  51. _v9261f_uart->flush();
  52. last = millis();
  53. state = 3;
  54. }
  55. }
  56. } else if (state == 3) {
  57. if (checksumOK()) {
  58. _v9261f_power = (double) (
  59. (_v9261f_data[3]) +
  60. (_v9261f_data[4] << 8) +
  61. (_v9261f_data[5] << 16) +
  62. (_v9261f_data[6] << 24)
  63. ) / V9261F_POWER_FACTOR;
  64. _v9261f_rpower = (double) (
  65. (_v9261f_data[7]) +
  66. (_v9261f_data[8] << 8) +
  67. (_v9261f_data[9] << 16) +
  68. (_v9261f_data[10] << 24)
  69. ) / V9261F_RPOWER_FACTOR;
  70. _v9261f_voltage = (double) (
  71. (_v9261f_data[11]) +
  72. (_v9261f_data[12] << 8) +
  73. (_v9261f_data[13] << 16) +
  74. (_v9261f_data[14] << 24)
  75. ) / V9261F_VOLTAGE_FACTOR;
  76. _v9261f_current = (double) (
  77. (_v9261f_data[15]) +
  78. (_v9261f_data[16] << 8) +
  79. (_v9261f_data[17] << 16) +
  80. (_v9261f_data[18] << 24)
  81. ) / V9261F_CURRENT_FACTOR;
  82. _v9261f_newdata = true;
  83. }
  84. last = millis();
  85. index = 0;
  86. state = 4;
  87. } else if (state == 4) {
  88. while (_v9261f_uart->available()) {
  89. _v9261f_uart->flush();
  90. last = millis();
  91. }
  92. if (millis() - last > V9261F_SYNC_INTERVAL) {
  93. state = 1;
  94. }
  95. }
  96. }
  97. boolean checksumOK() {
  98. unsigned char checksum = 0;
  99. for (unsigned char i = 0; i < 19; i++) {
  100. checksum = checksum + _v9261f_data[i];
  101. }
  102. checksum = ~checksum + 0x33;
  103. return checksum == _v9261f_data[19];
  104. }
  105. // -----------------------------------------------------------------------------
  106. // HAL
  107. // -----------------------------------------------------------------------------
  108. unsigned int getActivePower() {
  109. return _v9261f_power;
  110. }
  111. unsigned int getReactivePower() {
  112. return _v9261f_rpower;
  113. }
  114. unsigned int getApparentPower() {
  115. return sqrt(_v9261f_rpower * _v9261f_rpower + _v9261f_power * _v9261f_power);
  116. }
  117. unsigned int getVoltage() {
  118. return _v9261f_voltage;
  119. }
  120. double getCurrent() {
  121. return _v9261f_current;
  122. }
  123. double getPowerFactor() {
  124. unsigned int apparent = getApparentPower();
  125. if (apparent > 0) return getActivePower() / getApparentPower();
  126. return 1;
  127. }
  128. // -----------------------------------------------------------------------------
  129. void v9261fSetup() {
  130. _v9261f_uart = new SoftwareSerial(V9261F_PIN, SW_SERIAL_UNUSED_PIN, V9261F_PIN_INVERSE, 256);
  131. _v9261f_uart->begin(V9261F_BAUDRATE);
  132. // API definitions
  133. #if WEB_SUPPORT
  134. apiRegister(HLW8012_POWER_TOPIC, HLW8012_POWER_TOPIC, [](char * buffer, size_t len) {
  135. snprintf_P(buffer, len, PSTR("%d"), _v9261f_power);
  136. });
  137. apiRegister(HLW8012_CURRENT_TOPIC, HLW8012_CURRENT_TOPIC, [](char * buffer, size_t len) {
  138. dtostrf(_v9261f_current, len-1, 3, buffer);
  139. });
  140. apiRegister(HLW8012_VOLTAGE_TOPIC, HLW8012_VOLTAGE_TOPIC, [](char * buffer, size_t len) {
  141. snprintf_P(buffer, len, PSTR("%d"), _v9261f_voltage);
  142. });
  143. #endif // WEB_SUPPORT
  144. }
  145. void v9261fLoop() {
  146. static int sum_power = 0;
  147. static int sum_rpower = 0;
  148. static int sum_voltage = 0;
  149. static double sum_current = 0;
  150. static int count = 0;
  151. // Sniff data in the UART interface
  152. v9261fRead();
  153. // Do we have new data?
  154. if (_v9261f_newdata) {
  155. _v9261f_newdata = false;
  156. sum_power += getActivePower();
  157. sum_rpower += getReactivePower();
  158. sum_voltage += getVoltage();
  159. sum_current += getCurrent();
  160. count++;
  161. #if WEB_SUPPORT
  162. {
  163. DynamicJsonBuffer jsonBuffer;
  164. JsonObject& root = jsonBuffer.createObject();
  165. char buf_current[10];
  166. dtostrf(getCurrent(), 6, 3, buf_current);
  167. root["powVisible"] = 1;
  168. root["powActivePower"] = getActivePower();
  169. root["powCurrent"] = String(ltrim(buf_current));
  170. root["powVoltage"] = getVoltage();
  171. root["powApparentPower"] = getApparentPower();
  172. root["powReactivePower"] = getReactivePower();
  173. root["powPowerFactor"] = 100 * getPowerFactor();
  174. String output;
  175. root.printTo(output);
  176. wsSend(output.c_str());
  177. }
  178. #endif
  179. }
  180. // Do we have to report?
  181. static unsigned long last = 0;
  182. if ((count == 0) || (millis() - last < V9261F_REPORT_INTERVAL)) return;
  183. last = millis();
  184. {
  185. unsigned int power = sum_power / count;
  186. unsigned int reactive = sum_rpower / count;
  187. unsigned int voltage = sum_voltage / count;
  188. double current = sum_current / count;
  189. char buf_current[10];
  190. dtostrf(current, 6, 3, buf_current);
  191. unsigned int apparent = sqrt(power * power + reactive * reactive);
  192. double energy_delta = (double) power * V9261F_REPORT_INTERVAL / 1000.0 / 3600.0;
  193. char buf_energy[10];
  194. dtostrf(energy_delta, 6, 3, buf_energy);
  195. unsigned int factor = 100 * ((double) power / apparent);
  196. // Report values to MQTT broker
  197. mqttSend(HLW8012_POWER_TOPIC, String(power).c_str());
  198. mqttSend(HLW8012_CURRENT_TOPIC, buf_current);
  199. mqttSend(HLW8012_VOLTAGE_TOPIC, String(voltage).c_str());
  200. mqttSend(HLW8012_ENERGY_TOPIC, buf_energy);
  201. mqttSend(HLW8012_APOWER_TOPIC, String(apparent).c_str());
  202. mqttSend(HLW8012_RPOWER_TOPIC, String(reactive).c_str());
  203. mqttSend(HLW8012_PFACTOR_TOPIC, String(factor).c_str());
  204. // Report values to Domoticz
  205. #if DOMOTICZ_SUPPORT
  206. {
  207. char buffer[20];
  208. snprintf_P(buffer, sizeof(buffer), PSTR("%d;%s"), power, buf_energy);
  209. domoticzSend("dczPowIdx", 0, buffer);
  210. snprintf_P(buffer, sizeof(buffer), PSTR("%s"), buf_energy);
  211. domoticzSend("dczEnergyIdx", 0, buffer);
  212. snprintf_P(buffer, sizeof(buffer), PSTR("%d"), voltage);
  213. domoticzSend("dczVoltIdx", 0, buffer);
  214. snprintf_P(buffer, sizeof(buffer), PSTR("%s"), buf_current);
  215. domoticzSend("dczCurrentIdx", 0, buffer);
  216. }
  217. #endif
  218. #if INFLUXDB_SUPPORT
  219. {
  220. influxDBSend(HLW8012_POWER_TOPIC, String(power).c_str());
  221. influxDBSend(HLW8012_CURRENT_TOPIC, buf_current);
  222. influxDBSend(HLW8012_VOLTAGE_TOPIC, String(voltage).c_str());
  223. influxDBSend(HLW8012_ENERGY_TOPIC, buf_energy);
  224. influxDBSend(HLW8012_APOWER_TOPIC, String(apparent).c_str());
  225. influxDBSend(HLW8012_RPOWER_TOPIC, String(reactive).c_str());
  226. influxDBSend(HLW8012_PFACTOR_TOPIC, String(factor).c_str());
  227. }
  228. #endif
  229. // Reset counters
  230. sum_power = sum_rpower = sum_voltage = sum_current = count = 0;
  231. }
  232. }
  233. #endif
  234. */