xnrg_01_hlw8012.ino 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. xnrg_01_hlw8012.ino - HLW8012 (Sonoff Pow) energy sensor support for Sonoff-Tasmota
  3. Copyright (C) 2018 Theo Arends
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #ifdef USE_ENERGY_SENSOR
  16. #ifdef USE_HLW8012
  17. /*********************************************************************************************\
  18. * HLW8012, BL0937 or HJL-01 - Energy (Sonoff Pow, HuaFan, KMC70011, BlitzWolf)
  19. *
  20. * Based on Source: Shenzhen Heli Technology Co., Ltd
  21. \*********************************************************************************************/
  22. #define XNRG_01 1
  23. // Energy model type 0 (GPIO_HLW_CF) - HLW8012 based (Sonoff Pow, KMC70011, HuaFan, AplicWDP303075)
  24. #define HLW_PREF 10000 // 1000.0W
  25. #define HLW_UREF 2200 // 220.0V
  26. #define HLW_IREF 4545 // 4.545A
  27. // Energy model type 1 (GPIO_HJL_CF) - HJL-01/BL0937 based (BlitzWolf, Homecube, Gosund, Teckin)
  28. #define HJL_PREF 1362
  29. #define HJL_UREF 822
  30. #define HJL_IREF 3300
  31. #define HLW_POWER_PROBE_TIME 10 // Number of seconds to probe for power before deciding none used
  32. byte hlw_select_ui_flag;
  33. byte hlw_ui_flag = 1;
  34. byte hlw_model_type = 0;
  35. byte hlw_load_off;
  36. byte hlw_cf1_timer;
  37. unsigned long hlw_cf_pulse_length;
  38. unsigned long hlw_cf_pulse_last_time;
  39. unsigned long hlw_cf1_pulse_length;
  40. unsigned long hlw_cf1_pulse_last_time;
  41. unsigned long hlw_cf1_summed_pulse_length;
  42. unsigned long hlw_cf1_pulse_counter;
  43. unsigned long hlw_cf1_voltage_pulse_length;
  44. unsigned long hlw_cf1_current_pulse_length;
  45. unsigned long hlw_energy_period_counter;
  46. unsigned long hlw_power_ratio = 0;
  47. unsigned long hlw_voltage_ratio = 0;
  48. unsigned long hlw_current_ratio = 0;
  49. unsigned long hlw_cf1_voltage_max_pulse_counter;
  50. unsigned long hlw_cf1_current_max_pulse_counter;
  51. #ifndef USE_WS2812_DMA // Collides with Neopixelbus but solves exception
  52. void HlwCfInterrupt(void) ICACHE_RAM_ATTR;
  53. void HlwCf1Interrupt(void) ICACHE_RAM_ATTR;
  54. #endif // USE_WS2812_DMA
  55. void HlwCfInterrupt(void) // Service Power
  56. {
  57. unsigned long us = micros();
  58. if (hlw_load_off) { // Restart plen measurement
  59. hlw_cf_pulse_last_time = us;
  60. hlw_load_off = 0;
  61. } else {
  62. hlw_cf_pulse_length = us - hlw_cf_pulse_last_time;
  63. hlw_cf_pulse_last_time = us;
  64. hlw_energy_period_counter++;
  65. }
  66. }
  67. void HlwCf1Interrupt(void) // Service Voltage and Current
  68. {
  69. unsigned long us = micros();
  70. hlw_cf1_pulse_length = us - hlw_cf1_pulse_last_time;
  71. hlw_cf1_pulse_last_time = us;
  72. if ((hlw_cf1_timer > 2) && (hlw_cf1_timer < 8)) { // Allow for 300 mSec set-up time and measure for up to 1 second
  73. hlw_cf1_summed_pulse_length += hlw_cf1_pulse_length;
  74. hlw_cf1_pulse_counter++;
  75. if (10 == hlw_cf1_pulse_counter) {
  76. hlw_cf1_timer = 8; // We need up to ten samples within 1 second (low current could take up to 0.3 second)
  77. }
  78. }
  79. }
  80. /********************************************************************************************/
  81. void HlwEvery200ms(void)
  82. {
  83. unsigned long hlw_w = 0;
  84. unsigned long hlw_u = 0;
  85. unsigned long hlw_i = 0;
  86. if (micros() - hlw_cf_pulse_last_time > (HLW_POWER_PROBE_TIME * 1000000)) {
  87. hlw_cf_pulse_length = 0; // No load for some time
  88. hlw_load_off = 1;
  89. }
  90. if (hlw_cf_pulse_length && energy_power_on && !hlw_load_off) {
  91. hlw_w = (hlw_power_ratio * Settings.energy_power_calibration) / hlw_cf_pulse_length;
  92. energy_active_power = (float)hlw_w / 10;
  93. } else {
  94. energy_active_power = 0;
  95. }
  96. hlw_cf1_timer++;
  97. if (hlw_cf1_timer >= 8) {
  98. hlw_cf1_timer = 0;
  99. hlw_select_ui_flag = (hlw_select_ui_flag) ? 0 : 1;
  100. digitalWrite(pin[GPIO_NRG_SEL], hlw_select_ui_flag);
  101. if (hlw_cf1_pulse_counter) {
  102. hlw_cf1_pulse_length = hlw_cf1_summed_pulse_length / hlw_cf1_pulse_counter;
  103. } else {
  104. hlw_cf1_pulse_length = 0;
  105. }
  106. if (hlw_select_ui_flag == hlw_ui_flag) {
  107. hlw_cf1_voltage_pulse_length = hlw_cf1_pulse_length;
  108. hlw_cf1_voltage_max_pulse_counter = hlw_cf1_pulse_counter;
  109. if (hlw_cf1_voltage_pulse_length && energy_power_on) { // If powered on always provide voltage
  110. hlw_u = (hlw_voltage_ratio * Settings.energy_voltage_calibration) / hlw_cf1_voltage_pulse_length;
  111. energy_voltage = (float)hlw_u / 10;
  112. } else {
  113. energy_voltage = 0;
  114. }
  115. } else {
  116. hlw_cf1_current_pulse_length = hlw_cf1_pulse_length;
  117. hlw_cf1_current_max_pulse_counter = hlw_cf1_pulse_counter;
  118. if (hlw_cf1_current_pulse_length && energy_active_power) { // No current if no power being consumed
  119. hlw_i = (hlw_current_ratio * Settings.energy_current_calibration) / hlw_cf1_current_pulse_length;
  120. energy_current = (float)hlw_i / 1000;
  121. } else {
  122. energy_current = 0;
  123. }
  124. }
  125. hlw_cf1_summed_pulse_length = 0;
  126. hlw_cf1_pulse_counter = 0;
  127. }
  128. }
  129. void HlwEverySecond(void)
  130. {
  131. unsigned long hlw_len;
  132. if (hlw_energy_period_counter) {
  133. hlw_len = 10000 / hlw_energy_period_counter;
  134. hlw_energy_period_counter = 0;
  135. if (hlw_len) {
  136. energy_kWhtoday_delta += ((hlw_power_ratio * Settings.energy_power_calibration) / hlw_len) / 36;
  137. EnergyUpdateToday();
  138. }
  139. }
  140. }
  141. void HlwSnsInit(void)
  142. {
  143. if (!Settings.energy_power_calibration || (4975 == Settings.energy_power_calibration)) {
  144. Settings.energy_power_calibration = HLW_PREF_PULSE;
  145. Settings.energy_voltage_calibration = HLW_UREF_PULSE;
  146. Settings.energy_current_calibration = HLW_IREF_PULSE;
  147. }
  148. if (hlw_model_type) {
  149. hlw_power_ratio = HJL_PREF;
  150. hlw_voltage_ratio = HJL_UREF;
  151. hlw_current_ratio = HJL_IREF;
  152. } else {
  153. hlw_power_ratio = HLW_PREF;
  154. hlw_voltage_ratio = HLW_UREF;
  155. hlw_current_ratio = HLW_IREF;
  156. }
  157. hlw_cf_pulse_length = 0;
  158. hlw_cf_pulse_last_time = 0;
  159. hlw_cf1_pulse_length = 0;
  160. hlw_cf1_pulse_last_time = 0;
  161. hlw_cf1_voltage_pulse_length = 0;
  162. hlw_cf1_current_pulse_length = 0;
  163. hlw_cf1_voltage_max_pulse_counter = 0;
  164. hlw_cf1_current_max_pulse_counter = 0;
  165. hlw_load_off = 1;
  166. hlw_energy_period_counter = 0;
  167. hlw_select_ui_flag = 0; // Voltage;
  168. pinMode(pin[GPIO_NRG_SEL], OUTPUT);
  169. digitalWrite(pin[GPIO_NRG_SEL], hlw_select_ui_flag);
  170. pinMode(pin[GPIO_NRG_CF1], INPUT_PULLUP);
  171. attachInterrupt(pin[GPIO_NRG_CF1], HlwCf1Interrupt, FALLING);
  172. pinMode(pin[GPIO_HLW_CF], INPUT_PULLUP);
  173. attachInterrupt(pin[GPIO_HLW_CF], HlwCfInterrupt, FALLING);
  174. hlw_cf1_timer = 0;
  175. }
  176. void HlwDrvInit(void)
  177. {
  178. if (!energy_flg) {
  179. hlw_model_type = 0;
  180. if (pin[GPIO_HJL_CF] < 99) {
  181. pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF];
  182. pin[GPIO_HJL_CF] = 99;
  183. hlw_model_type = 1;
  184. }
  185. hlw_ui_flag = 1;
  186. if (pin[GPIO_NRG_SEL_INV] < 99) {
  187. pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV];
  188. pin[GPIO_NRG_SEL_INV] = 99;
  189. hlw_ui_flag = 0;
  190. }
  191. if ((pin[GPIO_NRG_SEL] < 99) && (pin[GPIO_NRG_CF1] < 99) && (pin[GPIO_HLW_CF] < 99)) { // HLW8012 or HJL-01 based device
  192. energy_flg = XNRG_01;
  193. }
  194. }
  195. }
  196. boolean HlwCommand(void)
  197. {
  198. boolean serviced = true;
  199. if (CMND_POWERSET == energy_command_code) {
  200. if (XdrvMailbox.data_len && hlw_cf_pulse_length) {
  201. Settings.energy_power_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf_pulse_length) / hlw_power_ratio;
  202. }
  203. }
  204. else if (CMND_VOLTAGESET == energy_command_code) {
  205. if (XdrvMailbox.data_len && hlw_cf1_voltage_pulse_length) {
  206. Settings.energy_voltage_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf1_voltage_pulse_length) / hlw_voltage_ratio;
  207. }
  208. }
  209. else if (CMND_CURRENTSET == energy_command_code) {
  210. if (XdrvMailbox.data_len && hlw_cf1_current_pulse_length) {
  211. Settings.energy_current_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data)) * hlw_cf1_current_pulse_length) / hlw_current_ratio;
  212. }
  213. }
  214. else serviced = false; // Unknown command
  215. return serviced;
  216. }
  217. /*********************************************************************************************\
  218. * Interface
  219. \*********************************************************************************************/
  220. int Xnrg01(byte function)
  221. {
  222. int result = 0;
  223. if (FUNC_PRE_INIT == function) {
  224. HlwDrvInit();
  225. }
  226. else if (XNRG_01 == energy_flg) {
  227. switch (function) {
  228. case FUNC_INIT:
  229. HlwSnsInit();
  230. break;
  231. case FUNC_EVERY_SECOND:
  232. HlwEverySecond();
  233. break;
  234. case FUNC_EVERY_200_MSECOND:
  235. HlwEvery200ms();
  236. break;
  237. case FUNC_COMMAND:
  238. result = HlwCommand();
  239. break;
  240. }
  241. }
  242. return result;
  243. }
  244. #endif // USE_HLW8012
  245. #endif // USE_ENERGY_SENSOR