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.

179 lines
5.3 KiB

  1. /*
  2. POWER HLW8012 MODULE
  3. Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if POWER_PROVIDER == POWER_PROVIDER_HLW8012
  6. // -----------------------------------------------------------------------------
  7. // MODULE GLOBALS AND CACHE
  8. // -----------------------------------------------------------------------------
  9. #include <HLW8012.h>
  10. #include <ESP8266WiFi.h>
  11. HLW8012 _hlw8012;
  12. WiFiEventHandler _power_wifi_onconnect;
  13. WiFiEventHandler _power_wifi_ondisconnect;
  14. // -----------------------------------------------------------------------------
  15. // HAL
  16. // -----------------------------------------------------------------------------
  17. void ICACHE_RAM_ATTR _hlw_cf1_isr() {
  18. _hlw8012.cf1_interrupt();
  19. }
  20. void ICACHE_RAM_ATTR _hlw_cf_isr() {
  21. _hlw8012.cf_interrupt();
  22. }
  23. void _hlwSetCalibration() {
  24. double value;
  25. value = getSetting("powerRatioP", 0).toFloat();
  26. if (value > 0) _hlw8012.setPowerMultiplier(value);
  27. value = getSetting("powerRatioC", 0).toFloat();
  28. if (value > 0) _hlw8012.setCurrentMultiplier(value);
  29. value = getSetting("powerRatioV", 0).toFloat();
  30. if (value > 0) _hlw8012.setVoltageMultiplier(value);
  31. }
  32. void _hlwGetCalibration() {
  33. setSetting("powerRatioP", _hlw8012.getPowerMultiplier());
  34. setSetting("powerRatioC", _hlw8012.getCurrentMultiplier());
  35. setSetting("powerRatioV", _hlw8012.getVoltageMultiplier());
  36. saveSettings();
  37. }
  38. void _hlwResetCalibration() {
  39. _hlw8012.resetMultipliers();
  40. _hlwGetCalibration();
  41. }
  42. void _hlwExpectedPower(unsigned int power) {
  43. if (power > 0) {
  44. _hlw8012.expectedActivePower(power);
  45. _hlwGetCalibration();
  46. }
  47. }
  48. void _hlwExpectedCurrent(double current) {
  49. if (current > 0) {
  50. _hlw8012.expectedCurrent(current);
  51. _hlwGetCalibration();
  52. }
  53. }
  54. void _hlwExpectedVoltage(unsigned int voltage) {
  55. if (voltage > 0) {
  56. _hlw8012.expectedVoltage(voltage);
  57. _hlwGetCalibration();
  58. }
  59. }
  60. // -----------------------------------------------------------------------------
  61. double _powerCurrent() {
  62. return _hlw8012.getCurrent();
  63. }
  64. double _powerVoltage() {
  65. return _hlw8012.getVoltage();
  66. }
  67. double _powerActivePower() {
  68. return _hlw8012.getActivePower();
  69. }
  70. double _powerApparentPower() {
  71. return _hlw8012.getApparentPower();
  72. }
  73. double _powerReactivePower() {
  74. double active = _powerActivePower();
  75. double apparent = _powerApparentPower();
  76. if (apparent > active) return sqrt(apparent * apparent - active * active);
  77. return 0;
  78. }
  79. double _powerPowerFactor() {
  80. double apparent = _powerApparentPower();
  81. if (apparent > 0) return _powerActivePower() / apparent;
  82. return 1;
  83. }
  84. // -----------------------------------------------------------------------------
  85. // PUBLIC API
  86. // -----------------------------------------------------------------------------
  87. void powerEnabledProvider() {
  88. if (_power_enabled) {
  89. attachInterrupt(HLW8012_CF1_PIN, _hlw_cf1_isr, CHANGE);
  90. attachInterrupt(HLW8012_CF_PIN, _hlw_cf_isr, CHANGE);
  91. } else {
  92. detachInterrupt(HLW8012_CF1_PIN);
  93. detachInterrupt(HLW8012_CF_PIN);
  94. }
  95. }
  96. void powerConfigureProvider() {
  97. _hlwSetCalibration();
  98. _hlwGetCalibration();
  99. }
  100. void powerSetupProvider() {
  101. // Initialize HLW8012
  102. // void begin(unsigned char cf_pin, unsigned char cf1_pin, unsigned char sel_pin, unsigned char currentWhen = HIGH, bool use_interrupts = false, unsigned long pulse_timeout = PULSE_TIMEOUT);
  103. // * cf_pin, cf1_pin and sel_pin are GPIOs to the HLW8012 IC
  104. // * currentWhen is the value in sel_pin to select current sampling
  105. // * set use_interrupts to true to use interrupts to monitor pulse widths
  106. // * leave pulse_timeout to the default value, recommended when using interrupts
  107. #if HLW8012_USE_INTERRUPTS
  108. _hlw8012.begin(HLW8012_CF_PIN, HLW8012_CF1_PIN, HLW8012_SEL_PIN, HLW8012_SEL_CURRENT, true);
  109. #else
  110. _hlw8012.begin(HLW8012_CF_PIN, HLW8012_CF1_PIN, HLW8012_SEL_PIN, HLW8012_SEL_CURRENT, false, 1000000);
  111. #endif
  112. // These values are used to calculate current, voltage and power factors as per datasheet formula
  113. // These are the nominal values for the Sonoff POW resistors:
  114. // * The CURRENT_RESISTOR is the 1milliOhm copper-manganese resistor in series with the main line
  115. // * The VOLTAGE_RESISTOR_UPSTREAM are the 5 470kOhm resistors in the voltage divider that feeds the V2P pin in the HLW8012
  116. // * The VOLTAGE_RESISTOR_DOWNSTREAM is the 1kOhm resistor in the voltage divider that feeds the V2P pin in the HLW8012
  117. _hlw8012.setResistors(HLW8012_CURRENT_R, HLW8012_VOLTAGE_R_UP, HLW8012_VOLTAGE_R_DOWN);
  118. powerConfigureProvider();
  119. _power_wifi_onconnect = WiFi.onStationModeGotIP([](WiFiEventStationModeGotIP ipInfo) {
  120. powerEnabled(true);
  121. });
  122. _power_wifi_ondisconnect = WiFi.onStationModeDisconnected([](WiFiEventStationModeDisconnected ipInfo) {
  123. powerEnabled(false);
  124. });
  125. }
  126. void powerLoopProvider(bool before) {
  127. if (before) {
  128. static unsigned long last = 0;
  129. if (millis() - last > POWER_READ_INTERVAL) {
  130. last = millis();
  131. _power_newdata = true;
  132. }
  133. } else {
  134. // Toggle between current and voltage monitoring
  135. #if (HLW8012_USE_INTERRUPTS == 0)
  136. _hlw8012.toggleMode();
  137. #endif // (HLW8012_USE_INTERRUPTS == 0)
  138. }
  139. }
  140. #endif // POWER_PROVIDER == POWER_PROVIDER_HLW8012