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.

300 lines
8.6 KiB

  1. /*
  2. POWER MODULE
  3. Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if POWER_PROVIDER != POWER_PROVIDER_NONE
  6. // -----------------------------------------------------------------------------
  7. // MODULE GLOBALS AND CACHE
  8. // -----------------------------------------------------------------------------
  9. #include "power.h"
  10. #include <Hash.h>
  11. #include <ArduinoJson.h>
  12. bool _power_enabled = false;
  13. bool _power_ready = false;
  14. bool _power_newdata = false;
  15. double _power_current = 0;
  16. double _power_voltage = 0;
  17. double _power_apparent = 0;
  18. MedianFilter _filter_current = MedianFilter(POWER_REPORT_BUFFER);
  19. #if POWER_HAS_ACTIVE
  20. double _power_active = 0;
  21. double _power_reactive = 0;
  22. double _power_factor = 0;
  23. MedianFilter _filter_voltage = MedianFilter(POWER_REPORT_BUFFER);
  24. MedianFilter _filter_active = MedianFilter(POWER_REPORT_BUFFER);
  25. MedianFilter _filter_apparent = MedianFilter(POWER_REPORT_BUFFER);
  26. #endif
  27. // -----------------------------------------------------------------------------
  28. // PRIVATE METHODS
  29. // -----------------------------------------------------------------------------
  30. #if WEB_SUPPORT
  31. void _powerAPISetup() {
  32. apiRegister(MQTT_TOPIC_CURRENT, MQTT_TOPIC_CURRENT, [](char * buffer, size_t len) {
  33. if (_power_ready) {
  34. dtostrf(getCurrent(), len-1, POWER_CURRENT_PRECISION, buffer);
  35. } else {
  36. buffer = NULL;
  37. }
  38. });
  39. apiRegister(MQTT_TOPIC_VOLTAGE, MQTT_TOPIC_VOLTAGE, [](char * buffer, size_t len) {
  40. if (_power_ready) {
  41. snprintf_P(buffer, len, PSTR("%d"), getVoltage());
  42. } else {
  43. buffer = NULL;
  44. }
  45. });
  46. apiRegister(MQTT_TOPIC_POWER_APPARENT, MQTT_TOPIC_POWER_APPARENT, [](char * buffer, size_t len) {
  47. if (_power_ready) {
  48. snprintf_P(buffer, len, PSTR("%d"), getApparentPower());
  49. } else {
  50. buffer = NULL;
  51. }
  52. });
  53. #if POWER_HAS_ACTIVE
  54. apiRegister(MQTT_TOPIC_POWER_ACTIVE, MQTT_TOPIC_POWER_ACTIVE, [](char * buffer, size_t len) {
  55. if (_power_ready) {
  56. snprintf_P(buffer, len, PSTR("%d"), getActivePower());
  57. } else {
  58. buffer = NULL;
  59. }
  60. });
  61. #endif
  62. }
  63. #endif // WEB_SUPPORT
  64. void _powerReset() {
  65. _filter_current.reset();
  66. #if POWER_HAS_ACTIVE
  67. _filter_apparent.reset();
  68. _filter_voltage.reset();
  69. _filter_active.reset();
  70. #endif
  71. }
  72. // -----------------------------------------------------------------------------
  73. // MAGNITUDE API
  74. // -----------------------------------------------------------------------------
  75. bool hasActivePower() {
  76. return POWER_HAS_ACTIVE;
  77. }
  78. double getCurrent() {
  79. return _power_current;
  80. }
  81. double getVoltage() {
  82. return _power_voltage;
  83. }
  84. double getApparentPower() {
  85. return _power_apparent;
  86. }
  87. double getActivePower() {
  88. return _power_active;
  89. }
  90. double getReactivePower() {
  91. return _power_reactive;
  92. }
  93. double getPowerFactor() {
  94. return _power_factor;
  95. }
  96. // -----------------------------------------------------------------------------
  97. // PUBLIC API
  98. // -----------------------------------------------------------------------------
  99. bool powerEnabled() {
  100. return _power_enabled;
  101. }
  102. void powerEnabled(bool enabled) {
  103. if (enabled & !_power_enabled) _powerReset();
  104. _power_enabled = enabled;
  105. powerEnabledProvider();
  106. }
  107. void powerConfigure() {
  108. powerConfigureProvider();
  109. }
  110. void powerRead() {
  111. // Get instantaneous values from HAL
  112. double current = _powerCurrent();
  113. double voltage = _powerVoltage();
  114. double apparent = _powerApparentPower();
  115. #if POWER_HAS_ACTIVE
  116. double active = _powerActivePower();
  117. #endif
  118. // Filters
  119. _filter_current.add(current);
  120. #if POWER_HAS_ACTIVE
  121. _filter_apparent.add(apparent);
  122. _filter_voltage.add(voltage);
  123. _filter_active.add(active);
  124. #endif
  125. char current_buffer[10];
  126. dtostrf(current, sizeof(current_buffer)-1, POWER_CURRENT_PRECISION, current_buffer);
  127. DEBUG_MSG_P(PSTR("[POWER] Current: %sA\n"), current_buffer);
  128. DEBUG_MSG_P(PSTR("[POWER] Voltage: %sA\n"), voltage);
  129. DEBUG_MSG_P(PSTR("[POWER] Apparent Power: %dW\n"), apparent);
  130. #if POWER_HAS_ACTIVE
  131. DEBUG_MSG_P(PSTR("[POWER] Active Power: %dW\n"), active);
  132. DEBUG_MSG_P(PSTR("[POWER] Reactive Power: %dW\n"), getReactivePower());
  133. DEBUG_MSG_P(PSTR("[POWER] Power Factor: %d%%\n"), 100 * getPowerFactor());
  134. #endif
  135. // Update websocket clients
  136. #if WEB_SUPPORT
  137. {
  138. DynamicJsonBuffer jsonBuffer;
  139. JsonObject& root = jsonBuffer.createObject();
  140. root["powerVisible"] = 1;
  141. root["powerCurrent"] = String(current_buffer);
  142. root["powerVoltage"] = voltage;
  143. root["powerApparentPower"] = apparent;
  144. #if POWER_HAS_ACTIVE
  145. root["powerActivePower"] = active;
  146. root["powerReactivePower"] = getReactivePower();
  147. root["powerPowerfactor"] = int(100 * getPowerFactor());
  148. #endif
  149. String output;
  150. root.printTo(output);
  151. wsSend(output.c_str());
  152. }
  153. #endif
  154. }
  155. void powerReport() {
  156. // Get the fitered values
  157. _power_current = _filter_current.average(true);
  158. #if POWER_HAS_ACTIVE
  159. _power_apparent = _filter_apparent.average(true);
  160. _power_voltage = _filter_voltage.average(true);
  161. _power_active = _filter_active.average(true);
  162. #else
  163. _power_apparent = _power_current * _power_voltage;
  164. _power_active = _power_apparent;
  165. #endif
  166. _power_reactive = (_power_apparent > _power_active) ? sqrt(_power_apparent * _power_apparent - _power_active * _power_active) : 0;
  167. _power_factor = (_power_apparent > 0) ? _power_active / _power_apparent : 1;
  168. _power_ready = true;
  169. char buf_current[10];
  170. dtostrf(_power_current, 6, POWER_CURRENT_PRECISION, buf_current);
  171. double energy_delta = _power_active * POWER_ENERGY_FACTOR;
  172. char buf_energy[10];
  173. dtostrf(energy_delta, 6, POWER_CURRENT_PRECISION, buf_energy);
  174. {
  175. mqttSend(MQTT_TOPIC_CURRENT, buf_current);
  176. mqttSend(MQTT_TOPIC_POWER_APPARENT, String((int) _power_apparent).c_str());
  177. mqttSend(MQTT_TOPIC_ENERGY, buf_energy);
  178. #if POWER_HAS_ACTIVE
  179. mqttSend(MQTT_TOPIC_POWER_ACTIVE, String((int) _power_active).c_str());
  180. mqttSend(MQTT_TOPIC_POWER_REACTIVE, String((int) _power_reactive).c_str());
  181. mqttSend(MQTT_TOPIC_VOLTAGE, String((int) _power_voltage).c_str());
  182. mqttSend(MQTT_TOPIC_POWER_FACTOR, String((int) 100 * _power_factor).c_str());
  183. #endif
  184. }
  185. #if DOMOTICZ_SUPPORT
  186. {
  187. char buffer[20];
  188. snprintf_P(buffer, sizeof(buffer), PSTR("%d;%s"), _power_active, buf_energy);
  189. domoticzSend("dczPowIdx", 0, buffer);
  190. domoticzSend("dczCurrentIdx", 0, buf_current);
  191. domoticzSend("dczEnergyIdx", 0, buf_energy);
  192. #if POWER_HAS_ACTIVE
  193. snprintf_P(buffer, sizeof(buffer), PSTR("%d"), _power_voltage);
  194. domoticzSend("dczVoltIdx", 0, buffer);
  195. #endif
  196. }
  197. #endif
  198. #if INFLUXDB_SUPPORT
  199. {
  200. influxDBSend(MQTT_TOPIC_CURRENT, buf_current);
  201. influxDBSend(MQTT_TOPIC_POWER_APPARENT, String((int) _power_apparent).c_str());
  202. influxDBSend(MQTT_TOPIC_ENERGY, buf_energy);
  203. #if POWER_HAS_ACTIVE
  204. influxDBSend(MQTT_TOPIC_POWER_ACTIVE, String((int) _power_active).c_str());
  205. influxDBSend(MQTT_TOPIC_POWER_REACTIVE, String((int) _power_reactive).c_str());
  206. influxDBSend(MQTT_TOPIC_VOLTAGE, String((int) _power_voltage).c_str());
  207. influxDBSend(MQTT_TOPIC_POWER_FACTOR, String((int) 100 * _power_factor).c_str());
  208. #endif
  209. }
  210. #endif
  211. }
  212. void powerSetup() {
  213. // backwards compatibility
  214. moveSetting("pwMainsVoltage", "powerVoltage");
  215. moveSetting("emonMains", "powerVoltage");
  216. moveSetting("emonVoltage", "powerVoltage");
  217. moveSetting("pwCurrentRatio", "powerRatioC");
  218. moveSetting("emonRatio", "powerRatioC");
  219. moveSetting("powPowerMult", "powerRatioP");
  220. moveSetting("powCurrentMult", "powerRatioC");
  221. moveSetting("powVoltageMult", "powerRatioV");
  222. powerSetupProvider();
  223. // API
  224. #if WEB_SUPPORT
  225. _powerAPISetup();
  226. #endif
  227. DEBUG_MSG_P(PSTR("[POWER] POWER_PROVIDER = %d\n"), POWER_PROVIDER);
  228. }
  229. void powerLoop() {
  230. powerLoopProvider(true);
  231. if (_power_newdata) {
  232. _power_newdata = false;
  233. powerRead();
  234. }
  235. static unsigned long last = 0;
  236. if (millis() - last > POWER_REPORT_INTERVAL) {
  237. last = millis();
  238. powerReport();
  239. }
  240. powerLoopProvider(false);
  241. }
  242. #endif // POWER_PROVIDER != POWER_PROVIDER_NONE