heatpump_controller.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /**
  2. * @Author: 李建
  3. * @Date: 2025/9/9 13:55
  4. * Description: 热泵控制器
  5. * Copyright: Copyright (©) 2025 永续绿建. All rights reserved.
  6. */
  7. #include "heatpump_controller.h"
  8. #include "system/miscellaneous_interface.h"
  9. #include "freertos/FreeRTOS.h"
  10. #include <freertos/task.h>
  11. #include <cJSON.h>
  12. #include <esp_log.h>
  13. #include "freertos/semphr.h"
  14. #include "sht30.h"
  15. #include "gateway/access.h"
  16. #include "sub_device/command.h"
  17. #include "lvgl_port.h"
  18. #include "modbus_master.h"
  19. hp_controller_t hp_controller = {0};
  20. static SemaphoreHandle_t report_task_sem; // 上报任务信号量,用于实现操作完成上立即上报当前状态
  21. static TaskHandle_t cloud_report_handle;
  22. static const char *TAG = "hp_controller";
  23. static TaskHandle_t report_trigger_handle;
  24. static TaskHandle_t status_task_handle;
  25. void set_bit(uint16_t *num, uint8_t bit, bool value) {
  26. if (bit > 15) return;
  27. if (value) {
  28. *num |= (1 << bit);
  29. } else {
  30. *num &= ~(1 << bit);
  31. }
  32. }
  33. bool get_bit(uint16_t num, uint8_t bit) {
  34. if (bit > 15) return false;
  35. return (num >> bit) & 1;
  36. }
  37. esp_err_t set_relay_status(uint8_t no, uint8_t value) {
  38. esp_err_t err = ESP_OK;
  39. set_bit(&hp_controller.relay_status, no, value);
  40. nvs_set_uint16(hp_controller.relay_status, NVS_RELEY_STATUS_KEY);
  41. if (cloud_connected) {
  42. xSemaphoreGive(report_task_sem);
  43. }
  44. return err;
  45. }
  46. static void update_power_ui(bool on_off) {
  47. // 同步更新 UI
  48. if (guider_ui.screen) {
  49. lvgl_port_lock(-1);
  50. //lv_obj_set_state(guider_ui.screen_ib_power, LV_IMAGEBUTTON_STATE_CHECKED_RELEASED, on_off);
  51. if (on_off) {
  52. lv_obj_remove_flag(guider_ui.screen_cont_power_on, LV_OBJ_FLAG_HIDDEN);
  53. lv_obj_add_flag(guider_ui.screen_cont_power_off, LV_OBJ_FLAG_HIDDEN);
  54. } else {
  55. lv_obj_remove_flag(guider_ui.screen_cont_power_off, LV_OBJ_FLAG_HIDDEN);
  56. lv_obj_add_flag(guider_ui.screen_cont_power_on, LV_OBJ_FLAG_HIDDEN);
  57. }
  58. lvgl_port_unlock();
  59. }
  60. }
  61. /******************************************云端指令开始*************************************************************/
  62. // 云端指令:设置电源开关
  63. static void cloud_set_power(char *params) {
  64. cJSON *data = cJSON_Parse(params);
  65. if (data != NULL) {
  66. cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value");
  67. hp_controller.power = value->valueint;
  68. heatpump_set_power(true);
  69. update_power_ui(hp_controller.power);
  70. cJSON_Delete(data);
  71. }
  72. }
  73. // 云端指令:设置模式
  74. static void cloud_set_mode(char *params) {
  75. cJSON *data = cJSON_Parse(params);
  76. if (data != NULL) {
  77. cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value");
  78. hp_controller.mode = value->valueint;
  79. if (guider_ui.screen) {
  80. if (hp_controller.mode == 1) {
  81. lvgl_port_lock(0);
  82. lv_label_set_text_static(guider_ui.screen_lab_mode, "制冷");
  83. lv_obj_set_style_arc_color(guider_ui.screen_arc_temp, lv_color_hex(0x00d9ff),
  84. LV_PART_INDICATOR | LV_STATE_DEFAULT);
  85. lv_label_set_text_fmt(guider_ui.screen_lab_ac_temp, "%d", hp_controller.cool_set_temp);
  86. lv_obj_add_state(guider_ui.screen_ib_cool, LV_STATE_CHECKED);
  87. lv_obj_remove_state(guider_ui.screen_ib_heat, LV_STATE_CHECKED);
  88. lv_arc_set_range(guider_ui.screen_arc_temp, 7 * 2, 22 * 2);
  89. lv_arc_set_value(guider_ui.screen_arc_temp, hp_controller.cool_set_temp * 2);
  90. lvgl_port_unlock();
  91. } else if (hp_controller.mode == 2) {
  92. lvgl_port_lock(0);
  93. lv_obj_set_style_arc_color(guider_ui.screen_arc_temp, lv_color_hex(0xfa890a),
  94. LV_PART_INDICATOR | LV_STATE_DEFAULT);
  95. lv_label_set_text_static(guider_ui.screen_lab_mode, "制热");
  96. lv_label_set_text_fmt(guider_ui.screen_lab_ac_temp, "%d", hp_controller.heat_set_temp);
  97. lv_obj_remove_state(guider_ui.screen_ib_cool, LV_STATE_CHECKED);
  98. lv_obj_add_state(guider_ui.screen_ib_heat, LV_STATE_CHECKED);
  99. lv_arc_set_range(guider_ui.screen_arc_temp, 30 * 2, 60 * 2);
  100. lv_arc_set_value(guider_ui.screen_arc_temp, hp_controller.heat_set_temp * 2);
  101. lvgl_port_unlock();
  102. }
  103. }
  104. heatpump_set_mode(hp_controller.mode);
  105. cJSON_Delete(data);
  106. }
  107. }
  108. // 云端指令:设置水温
  109. static void cloud_set_water_temp(char *params) {
  110. cJSON *data = cJSON_Parse(params);
  111. if (data != NULL) {
  112. cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value");
  113. heatpump_set_temp(value->valueint);
  114. // 更新UI
  115. if (guider_ui.screen) {
  116. lvgl_port_lock(0);
  117. lv_label_set_text_fmt(guider_ui.screen_lab_ac_temp, "%d", value->valueint);
  118. lv_arc_set_value(guider_ui.screen_arc_temp, value->valueint);
  119. lvgl_port_unlock();
  120. }
  121. cJSON_Delete(data);
  122. }
  123. }
  124. // 云端指令 :设置分集水器某一路开关
  125. static void cloud_set_IO(char *params) {
  126. cJSON *data = cJSON_Parse(params);
  127. if (data != NULL) {
  128. cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value");
  129. cJSON *no = cJSON_GetObjectItemCaseSensitive(data, "no");
  130. set_relay_status(no->valueint - 1, value->valueint); // 要减1
  131. cJSON_Delete(data);
  132. }
  133. }
  134. // 云端指令:联动开关
  135. static void cloud_set_link(char *params) {
  136. cJSON *data = cJSON_Parse(params);
  137. if (data != NULL) {
  138. cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value");
  139. hp_controller.linkage = value->valueint;
  140. heatpump_set_linkage();
  141. cJSON_Delete(data);
  142. }
  143. }
  144. /******************************************云端指令END*************************************************************/
  145. static void register_sparrow_commands() {
  146. sparrow_command set_power_cmd = {
  147. .name = "setPower",
  148. .unpack = &cloud_set_power
  149. };
  150. register_sparrow_command(set_power_cmd);
  151. // setMode
  152. sparrow_command set_mode_cmd = {
  153. .name = "setMode",
  154. .unpack = &cloud_set_mode
  155. };
  156. register_sparrow_command(set_mode_cmd);
  157. // setWaterTemp
  158. sparrow_command set_water_temp = {
  159. .name = "setWaterTemp",
  160. .unpack = &cloud_set_water_temp
  161. };
  162. register_sparrow_command(set_water_temp);
  163. // setIO
  164. sparrow_command set_io = {
  165. .name = "setIO",
  166. .unpack = &cloud_set_IO
  167. };
  168. register_sparrow_command(set_io);
  169. // setLinkage
  170. sparrow_command set_link = {
  171. .name = "setLinkage",
  172. .unpack = &cloud_set_link
  173. };
  174. register_sparrow_command(set_link);
  175. }
  176. _Noreturn static void report_trigger_task(void *arg) {
  177. for (;;) {
  178. if (cloud_connected) {
  179. xSemaphoreGive(report_task_sem);
  180. }
  181. vTaskDelay(pdTICKS_TO_MS(system_setting.report_data_duration * 1000));
  182. }
  183. }
  184. /**
  185. * 状态同步任务
  186. * @param arg
  187. */
  188. _Noreturn static void status_sync_task(void *arg) {
  189. esp_err_t err = ESP_OK;
  190. for (;;) {
  191. // mm_set_param(CID_HP_POWER, &hp_controller.power);
  192. // mm_set_param(CID_HP_SET_MODE, &hp_controller.mode);
  193. // if (hp_controller.mode == MODE_COOL) mm_set_param(CID_HP_COOL_SET_TEMP, &hp_controller.cool_set_temp);
  194. // if (hp_controller.mode == MODE_HEAT) mm_set_param(CID_HP_HEAT_SET_TEMP, &hp_controller.heat_set_temp);
  195. // mm_get_param(CID_HP_OUTDOOR_TEMP, &hp_controller.outdoor_temp);
  196. // mm_get_param(CID_HP_OUT_WATER_TEMP, &hp_controller.out_water_temp);
  197. // mm_get_param(CID_HP_IN_WATER_TEMP, &hp_controller.in_water_temp);
  198. uint8_t status = get_bit(hp_controller.relay_status, 0);
  199. mm_set_param(CID_RELAY_1_STATUS, &status);
  200. status = get_bit(hp_controller.relay_status, 1);
  201. mm_set_param(CID_RELAY_2_STATUS, &status);
  202. status = get_bit(hp_controller.relay_status, 2);
  203. mm_set_param(CID_RELAY_3_STATUS, &status);
  204. status = get_bit(hp_controller.relay_status, 3);
  205. mm_set_param(CID_RELAY_4_STATUS, &status);
  206. status = get_bit(hp_controller.relay_status, 4);
  207. mm_set_param(CID_RELAY_5_STATUS, &status);
  208. status = get_bit(hp_controller.relay_status, 5);
  209. mm_set_param(CID_RELAY_6_STATUS, &status);
  210. status = get_bit(hp_controller.relay_status, 6);
  211. mm_set_param(CID_RELAY_7_STATUS, &status);
  212. status = get_bit(hp_controller.relay_status, 7);
  213. mm_set_param(CID_RELAY_8_STATUS, &status);
  214. vTaskDelay(5000 / portTICK_PERIOD_MS);
  215. }
  216. }
  217. /**
  218. * 云端上报任务
  219. * @param arg
  220. */
  221. _Noreturn static void cloud_report_task(void *arg) {
  222. for (;;) {
  223. if (xSemaphoreTake(report_task_sem, portMAX_DELAY) == pdTRUE) {
  224. ESP_LOGD(TAG, "cloud report");
  225. cJSON *data = cJSON_CreateObject();
  226. cJSON_AddNumberToObject(data, "power", hp_controller.power);
  227. cJSON_AddNumberToObject(data, "cool_set_water_temp", hp_controller.cool_set_temp);
  228. cJSON_AddNumberToObject(data, "heat_set_water_temp", hp_controller.heat_set_temp);
  229. cJSON_AddNumberToObject(data, "mode", hp_controller.mode);
  230. cJSON_AddNumberToObject(data, "temperature", (int) sht30Data.temperature);
  231. cJSON_AddNumberToObject(data, "humidity", (int) sht30Data.humidity);
  232. cJSON_AddNumberToObject(data, "outdoor_temp", hp_controller.outdoor_temp);
  233. cJSON_AddNumberToObject(data, "out_water_temp", hp_controller.out_water_temp);
  234. cJSON_AddNumberToObject(data, "in_water_temp", hp_controller.in_water_temp);
  235. cJSON_AddNumberToObject(data, "fjsq_exist", hp_controller.fjsq_exist);
  236. cJSON_AddNumberToObject(data, "fjsq_status", hp_controller.relay_status);
  237. cJSON *root = cJSON_CreateObject();
  238. cJSON_AddStringToObject(root, "cmd", "status");
  239. cJSON_AddItemToObject(root, "params", data);
  240. publish_message_t msg = {
  241. .topic = "status",
  242. .data = root,
  243. };
  244. sparrow_publish_status(msg);
  245. }
  246. }
  247. }
  248. void heatpump_controller_init(system_setting_t *setting) {
  249. hp_controller.mode = setting->hp_mode;
  250. hp_controller.power = setting->hp_power;
  251. hp_controller.cool_set_temp = setting->hp_cool_set_temp;
  252. hp_controller.heat_set_temp = setting->hp_heat_set_temp;
  253. hp_controller.relay_status = setting->reley_status;
  254. hp_controller.linkage = setting->allow_linkage;
  255. register_sparrow_commands();
  256. report_task_sem = xSemaphoreCreateBinary();
  257. // 启动状态同步任务
  258. xTaskCreate(status_sync_task, "status_sync_task", 4 * 1024, NULL, 5, &status_task_handle);
  259. // 启动云端上报任务,受信号量控制,无信号时任务不作操作,只有本地有操作或定时上报任务触发时才会上报。
  260. xTaskCreate(cloud_report_task, "report_task", 6 * 1024, NULL, 5, &cloud_report_handle);
  261. // 启动云端上报任务
  262. xTaskCreate(report_trigger_task, "trigger_task", 2 * 1024, NULL, 5, &report_trigger_handle);
  263. }
  264. esp_err_t heatpump_set_power(bool saved) {
  265. esp_err_t err = ESP_OK;
  266. if (saved)
  267. nvs_set_uint16(hp_controller.power, NVS_HP_POWER_KEY);
  268. if (cloud_connected) {
  269. xSemaphoreGive(report_task_sem);
  270. }
  271. return err;
  272. }
  273. esp_err_t heatpump_set_mode(uint8_t mode) {
  274. esp_err_t err = ESP_OK;
  275. nvs_set_uint16(hp_controller.mode, NVS_HP_MODE_KEY);
  276. if (cloud_connected) {
  277. xSemaphoreGive(report_task_sem);
  278. }
  279. return err;
  280. }
  281. esp_err_t heatpump_set_temp(uint16_t temp) {
  282. esp_err_t err = ESP_OK;
  283. if (hp_controller.mode == MODE_COOL)nvs_set_uint16(hp_controller.cool_set_temp, NVS_HP_COOL_SET_TEMP_KEY);
  284. if (hp_controller.mode == MODE_HEAT)nvs_set_uint16(hp_controller.heat_set_temp, NVS_HP_HEAT_SET_TEMP_KEY);
  285. if (cloud_connected) {
  286. xSemaphoreGive(report_task_sem);
  287. }
  288. return err;
  289. }
  290. esp_err_t heatpump_set_linkage() {
  291. esp_err_t err = ESP_OK;
  292. nvs_set_uint16(hp_controller.linkage, NVS_ALLOW_LINKAGE_KEY);
  293. return err;
  294. }