xf_controller.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. #include <sys/cdefs.h>
  2. /**
  3. * @Author: 李建
  4. * @Date: 2025/3/25 15:23
  5. * Description: 空调控制器
  6. * Copyright: Copyright (©) 2025 永续绿建. All rights reserved.
  7. */
  8. #include <esp_log.h>
  9. #include "freertos/FreeRTOS.h"
  10. #include <freertos/task.h>
  11. #include <cJSON.h>
  12. #include <esp_timer.h>
  13. #include "xf_controller.h"
  14. #include "sub_device/command.h"
  15. #include "modbus_master.h"
  16. #include "system/miscellaneous_interface.h"
  17. #include "lvgl_port.h"
  18. #include "main.h"
  19. #include "esp_modbus_master.h"
  20. #include "mb_endianness_utils.h"
  21. #include "sht30.h"
  22. #include "gateway/access.h"
  23. static const char *TAG = "ac_controller";
  24. static uint8_t err_count = 0;// 通讯失败次数
  25. static SemaphoreHandle_t report_task_sem; // 上报任务信号量,用于实现操作完成上立即上报当前状态
  26. extern void set_xf_mode(lv_ui *ui, uint16_t mode);
  27. static TaskHandle_t cloud_report_handle;
  28. static TaskHandle_t report_trigger_handle;
  29. static TaskHandle_t status_task_handle;
  30. // 初始化空调参数
  31. xf_status_t xf_status = {
  32. .power= 0,
  33. .fan_speed=3,
  34. .mode=1,
  35. .filter_used_time=0,
  36. .set_max_hum=70,
  37. .filter_life_remaining=100,
  38. };
  39. void read_xf_status(system_setting_t *setting) {
  40. uint16_t *_power = nvs_get_uint16(NVS_POWER_CONTROLLER);
  41. uint16_t *_mode = nvs_get_uint16(NVS_MODEL_CONTROLLER);
  42. uint16_t *_fan_speed = nvs_get_uint16(NVS_FAN_SPEED_SET);
  43. uint32_t *_filter_used_time = nvs_get_uint32(NVS_FILTER_USED_TIME);
  44. uint16_t *_set_hum_max = nvs_get_uint16(NVS_MAX_HUM_SET);
  45. // uint16_t *_set_hum_min = nvs_get_uint16(NVS_MIN_HUM_SET);
  46. xf_status.power= _power == NULL ? 0 : *_power;
  47. xf_status.mode = _mode == NULL ? 1 : *_mode;
  48. xf_status.fan_speed = _fan_speed == NULL ? 3 : *_fan_speed;
  49. xf_status.filter_used_time = _filter_used_time == NULL ? 0 : *_filter_used_time;
  50. xf_status.set_max_hum = _set_hum_max == NULL ? 70 : *_set_hum_max;
  51. }
  52. // 设置电源
  53. esp_err_t xf_set_power(bool saved) {
  54. esp_err_t err = ESP_OK;
  55. if(saved)
  56. nvs_set_uint16(xf_status.power, NVS_POWER_CONTROLLER);
  57. if (guider_ui.screen_main) {
  58. if(xf_status.power==0)
  59. {
  60. lvgl_port_lock(-1);
  61. lv_obj_add_flag(guider_ui.screen_main_cont_speedset, LV_OBJ_FLAG_HIDDEN);
  62. lv_obj_add_flag(guider_ui.screen_main_cont_humSet, LV_OBJ_FLAG_HIDDEN);
  63. lv_obj_add_flag(guider_ui.screen_main_cont_mode, LV_OBJ_FLAG_HIDDEN);
  64. lvgl_port_unlock();
  65. }else{
  66. lvgl_port_lock(-1);
  67. lv_obj_clear_flag(guider_ui.screen_main_cont_speedset, LV_OBJ_FLAG_HIDDEN);
  68. lv_obj_clear_flag(guider_ui.screen_main_cont_humSet, LV_OBJ_FLAG_HIDDEN);
  69. lv_obj_clear_flag(guider_ui.screen_main_cont_mode, LV_OBJ_FLAG_HIDDEN);
  70. lv_label_set_text_fmt(guider_ui.screen_main_label_humSet_vul, "%d%%", xf_status.set_max_hum);
  71. lv_slider_set_value(guider_ui.screen_main_slider_speedSet_sign, xf_status.fan_speed*20, LV_ANIM_ON);
  72. lv_label_set_text_fmt(guider_ui.screen_main_label_spedSet_vul, "%d档",xf_status.fan_speed);
  73. lv_slider_set_value(guider_ui.screen_main_slider_humSet_sign, xf_status.set_max_hum, LV_ANIM_ON);
  74. set_xf_mode(&guider_ui, xf_status.mode);
  75. lvgl_port_unlock();
  76. }
  77. }
  78. if (cloud_connected) {
  79. xSemaphoreGive(report_task_sem);
  80. }
  81. return err;
  82. }
  83. void esp_timer_cb()
  84. {
  85. if(xf_status.power==1)
  86. {
  87. xf_status.filter_used_time++;
  88. }else{
  89. }
  90. if(xf_status.filter_used_time>=system_setting.filter_life_time)
  91. {
  92. xf_status.filter_used_time=system_setting.filter_life_time;
  93. }
  94. xf_set_filter_life_remain(true);
  95. }
  96. esp_timer_handle_t esp_timer_handle = 0; //定时器句柄
  97. void timer_init_lx() {
  98. //定时器结构体初始化
  99. esp_timer_create_args_t fw_timer = {
  100. .callback = &esp_timer_cb, //定时器回调函数
  101. .arg = NULL, //传递给回调函数的参数
  102. .name = "esp_timer", //定时器名称
  103. };
  104. //创建定时器
  105. esp_timer_create(&fw_timer, &esp_timer_handle);
  106. //启动定时器,定时器周期为60秒
  107. esp_timer_start_periodic(esp_timer_handle, 60000 * 1000);
  108. }
  109. // 设置最大湿度
  110. esp_err_t xf_set_max_hum(bool saved) {
  111. esp_err_t err = ESP_OK;
  112. if(saved)
  113. nvs_set_uint16(xf_status.set_max_hum, NVS_MAX_HUM_SET);
  114. if (cloud_connected) {
  115. xSemaphoreGive(report_task_sem);
  116. }
  117. return err;
  118. }
  119. // 设置最小湿度
  120. //esp_err_t xf_set_min_hum(bool saved) {
  121. // if(saved)
  122. // nvs_set_uint16(xf_status.set_min_hum, NVS_MIN_HUM_SET);
  123. // return mm_set_param(CID_MIN_HUMIDITY, (uint8_t *)&xf_status.set_min_hum);
  124. //}
  125. //设置滤网剩余寿命
  126. void xf_set_filter_life_remain(bool saved) {
  127. if(saved)
  128. nvs_set_uint16(xf_status.filter_used_time, NVS_FILTER_USED_TIME);
  129. }
  130. //设置新风风速
  131. esp_err_t xf_set_fan_speed(bool saved) {
  132. esp_err_t err = ESP_OK;
  133. if(saved)
  134. nvs_set_uint16(xf_status.fan_speed, NVS_FAN_SPEED_SET);
  135. if (cloud_connected) {
  136. xSemaphoreGive(report_task_sem);
  137. }
  138. return err;
  139. }
  140. // 设置模式
  141. esp_err_t xf_set_mode(bool saved) {
  142. esp_err_t err = ESP_OK;
  143. if(saved)
  144. nvs_set_uint16(xf_status.mode, NVS_MODEL_CONTROLLER);
  145. if (cloud_connected) {
  146. xSemaphoreGive(report_task_sem);
  147. }
  148. return err;
  149. }
  150. static void update_power_ui(bool on_off) {
  151. // 同步更新 UI
  152. if (guider_ui.screen_main) {
  153. lvgl_port_lock(-1);
  154. lv_obj_set_state(guider_ui.screen_main_imgbtn_power, LV_IMAGEBUTTON_STATE_CHECKED_RELEASED, on_off);
  155. lvgl_port_unlock();
  156. }
  157. }
  158. // 云端: 设置上报时间间隔
  159. static void cloud_set_report_duration(char *params) {
  160. cJSON *data = cJSON_Parse(params);
  161. if (data != NULL) {
  162. cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value");
  163. if (cJSON_IsNumber(value)) {
  164. system_setting.report_data_duration = value->valueint;
  165. save_system_setting(&system_setting);
  166. }
  167. }
  168. }
  169. static void cloud_set_power(char *params) {
  170. cJSON *data = cJSON_Parse(params);
  171. if (data != NULL) {
  172. cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value");
  173. xf_status.power = value->valueint;
  174. xf_set_power(true);
  175. update_power_ui(xf_status.power);
  176. cJSON_Delete(data);
  177. }
  178. }
  179. static void cloud_set_max_hum(char *params) {
  180. cJSON *data = cJSON_Parse(params);
  181. if (data != NULL) {
  182. cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value");
  183. if((value->valueint<=70)&&(value->valueint>=system_setting.set_min_hum))
  184. {
  185. xf_status.set_max_hum = value->valueint;
  186. if (guider_ui.screen_main) {
  187. lvgl_port_lock(-1);
  188. lv_label_set_text_fmt(guider_ui.screen_main_label_humSet_vul, "%d%%", xf_status.set_max_hum);
  189. lv_slider_set_value(guider_ui.screen_main_slider_humSet_sign, xf_status.set_max_hum,LV_ANIM_OFF);
  190. lvgl_port_unlock();
  191. }
  192. xf_set_max_hum(true);
  193. }
  194. cJSON_Delete(data);
  195. }
  196. }
  197. static void cloud_set_mode(char *params) {
  198. cJSON *data = cJSON_Parse(params);
  199. if (data != NULL) {
  200. cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value");
  201. xf_status.mode = value->valueint;
  202. if (guider_ui.screen_main) {
  203. lvgl_port_lock(-1);
  204. set_xf_mode(&guider_ui, xf_status.mode);
  205. lvgl_port_unlock();
  206. }
  207. xf_set_mode(true);
  208. cJSON_Delete(data);
  209. }
  210. }
  211. static void cloud_set_fan_level(char *params) {
  212. cJSON *data = cJSON_Parse(params);
  213. if (data != NULL) {
  214. cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value");
  215. xf_status.fan_speed = value->valueint;
  216. if (guider_ui.screen_main) {
  217. lvgl_port_lock(-1);
  218. lv_slider_set_value(guider_ui.screen_main_slider_speedSet_sign, xf_status.fan_speed*20, LV_ANIM_ON);
  219. lv_label_set_text_fmt(guider_ui.screen_main_label_spedSet_vul, "%d档",xf_status.fan_speed);
  220. lvgl_port_unlock();
  221. }
  222. xf_set_fan_speed(true);
  223. cJSON_Delete(data);
  224. }
  225. }
  226. static void cloud_set_filter_life(char *params) {
  227. cJSON *data = cJSON_Parse(params);
  228. if (data != NULL) {
  229. cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value");
  230. system_setting.filter_life_time = value->valueint;
  231. save_system_setting(&system_setting);
  232. cJSON_Delete(data);
  233. }
  234. }
  235. static void cloud_reset_filter_life_remain(char *params) {
  236. cJSON *data = cJSON_Parse(params);
  237. if (data != NULL) {
  238. xf_status.filter_used_time=0;
  239. xf_set_filter_life_remain(true);
  240. cJSON_Delete(data);
  241. }
  242. }
  243. static void cloud_set_min_hum(char *params) {
  244. }
  245. //注册云端指令s
  246. void register_sparrow_commands(void) {
  247. sparrow_command set_power_cmd = {
  248. .name = "setPower",
  249. .unpack = &cloud_set_power
  250. };
  251. register_sparrow_command(set_power_cmd);
  252. sparrow_command set_max_hum_cmd = {
  253. .name = "setMaxHum",
  254. .unpack = &cloud_set_max_hum
  255. };
  256. register_sparrow_command(set_max_hum_cmd);
  257. // setMode
  258. sparrow_command set_mode_cmd = {
  259. .name = "setMode",
  260. .unpack = &cloud_set_mode
  261. };
  262. register_sparrow_command(set_mode_cmd);
  263. // setFanLevel
  264. sparrow_command set_fan_level_cmd = {
  265. .name = "setFanLevel",
  266. .unpack = &cloud_set_fan_level,
  267. };
  268. register_sparrow_command(set_fan_level_cmd);
  269. // setNewFan
  270. sparrow_command set_filter_life_cmd = {
  271. .name = "setFilterLife",
  272. .unpack = &cloud_set_filter_life,
  273. };
  274. register_sparrow_command(set_filter_life_cmd);
  275. // setFanVoltage
  276. sparrow_command set_min_hum_cmd = {
  277. .name = "setMinHumCmd",
  278. .unpack = &cloud_set_min_hum,
  279. };
  280. register_sparrow_command(set_min_hum_cmd);
  281. sparrow_command set_duration = {
  282. .name = "setReportDuration",
  283. .unpack = &cloud_set_report_duration
  284. };
  285. register_sparrow_command(set_duration);
  286. sparrow_command reset_filter_life_remain = {
  287. .name = "setReportDuration",
  288. .unpack = &cloud_reset_filter_life_remain
  289. };
  290. register_sparrow_command(reset_filter_life_remain);
  291. }
  292. LV_IMAGE_DECLARE(_power_open_RGB565A8_36x36);
  293. LV_IMAGE_DECLARE(_liang_RGB565A8_30x30);
  294. LV_IMAGE_DECLARE(_cha_RGB565A8_30x30);
  295. void Refresh_UI()
  296. {
  297. uint8_t type = PARAM_TYPE_U16;
  298. uint8_t temp_data[2] = {0}; // temporary buffer to hold maximum CID size
  299. uint16_t temp_data_u16 = 500;
  300. esp_err_t err = ESP_OK;
  301. const mb_parameter_descriptor_t *param_descriptor = NULL;
  302. err = mm_get_param(CID_RETURN_AIR_PM25, temp_data);
  303. if (err == ESP_OK) {
  304. sht30Data.air_quality=*(uint16_t *)temp_data;
  305. if (guider_ui.screen_main) {
  306. lvgl_port_lock(-1);
  307. lv_arc_set_value(guider_ui.screen_main_arc_AIQ, 500);
  308. if (*(uint16_t *) temp_data < 50) {
  309. // lv_arc_set_value(guider_ui.screen_main_arc_AIQ, *(uint16_t *) temp_data);
  310. lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_AIQ_sign, &_you_RGB565A8_30x30,
  311. LV_PART_MAIN | LV_STATE_DEFAULT);
  312. lv_obj_set_style_arc_color(guider_ui.screen_main_arc_AIQ, lv_color_hex(0x2FDA64),
  313. LV_PART_INDICATOR | LV_STATE_DEFAULT);
  314. } else if (*(uint16_t *) temp_data < 150) {
  315. // lv_arc_set_value(guider_ui.screen_main_arc_AIQ, *(uint16_t *) temp_data);
  316. lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_AIQ_sign, &_liang_RGB565A8_30x30,
  317. LV_PART_MAIN | LV_STATE_DEFAULT);
  318. lv_obj_set_style_arc_color(guider_ui.screen_main_arc_AIQ, lv_color_hex(0xFFF502),
  319. LV_PART_INDICATOR | LV_STATE_DEFAULT);
  320. } else if (*(uint16_t *) temp_data <= 500) {
  321. // lv_arc_set_value(guider_ui.screen_main_arc_AIQ, *(uint16_t *) temp_data);
  322. lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_AIQ_sign, &_cha_RGB565A8_30x30,
  323. LV_PART_MAIN | LV_STATE_DEFAULT);
  324. lv_obj_set_style_arc_color(guider_ui.screen_main_arc_AIQ, lv_color_hex(0xd81e06),
  325. LV_PART_INDICATOR | LV_STATE_DEFAULT);
  326. } else if (*(uint16_t *) temp_data > 500) {
  327. // lv_arc_set_value(guider_ui.screen_main_arc_AIQ, temp_data_u16);
  328. lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_AIQ_sign, &_cha_RGB565A8_30x30,
  329. LV_PART_MAIN | LV_STATE_DEFAULT);
  330. lv_obj_set_style_arc_color(guider_ui.screen_main_arc_AIQ, lv_color_hex(0xd81e06),
  331. LV_PART_INDICATOR | LV_STATE_DEFAULT);
  332. } else {
  333. }
  334. lv_label_set_text_fmt(guider_ui.screen_main_label_AIQ_vul, "%d", *(uint16_t *) temp_data);
  335. lvgl_port_unlock();
  336. }
  337. }
  338. err = mm_get_param(CID_RETURN_AIR_CO2, temp_data);
  339. if (err == ESP_OK) {
  340. err_count = 0;
  341. sht30Data.co2=*(uint16_t *)temp_data;
  342. if (guider_ui.screen_main) {
  343. lvgl_port_lock(-1);
  344. lv_label_set_text_fmt(guider_ui.screen_main_label_co2, "%d", *(uint16_t *) temp_data);
  345. lv_obj_add_flag(guider_ui.screen_main_lab_485_err, LV_OBJ_FLAG_HIDDEN);
  346. lvgl_port_unlock();
  347. }
  348. } else {
  349. err_count++;
  350. if (err_count >= 254)
  351. err_count = 30;
  352. }
  353. if (err_count >= 30) {
  354. if (guider_ui.screen_main) {
  355. lvgl_port_lock(-1);
  356. lv_obj_remove_flag(guider_ui.screen_main_lab_485_err, LV_OBJ_FLAG_HIDDEN);
  357. lvgl_port_unlock();
  358. }
  359. }
  360. xf_status.filter_life_remaining=(100-100*xf_status.filter_used_time/system_setting.filter_life_time);
  361. if (guider_ui.screen_main) {
  362. lvgl_port_lock(-1);
  363. lv_label_set_text_fmt(guider_ui.screen_main_label_lvxin, "%d%%", xf_status.filter_life_remaining);
  364. lvgl_port_unlock();
  365. }
  366. }
  367. /**
  368. * 状态同步任务,主要用于与分风箱主控同步当前用户的参数,并处理通讯故障
  369. * @param arg
  370. */
  371. _Noreturn static void status_sync_task(void *arg) {
  372. esp_err_t err = ESP_OK;
  373. for (;;) {
  374. mm_set_param(CID_POWER, (uint8_t *)&xf_status.power);
  375. vTaskDelay(pdTICKS_TO_MS(500));
  376. mm_set_param(CID_MODE, (uint8_t *)&xf_status.mode);
  377. vTaskDelay(pdTICKS_TO_MS(500));
  378. mm_set_param(CID_FAN_SPEED, (uint8_t *)&xf_status.fan_speed);
  379. vTaskDelay(pdTICKS_TO_MS(500));
  380. mm_set_param(CID_MAX_HUMIDITY, (uint8_t *)&xf_status.set_max_hum);
  381. vTaskDelay(pdTICKS_TO_MS(500));
  382. //
  383. // err = xf_set_power(false);
  384. // err = xf_set_mode(true);
  385. // err = xf_set_fan_speed(true);
  386. // err =xf_set_max_hum(true);
  387. // err =xf_set_min_hum(true);
  388. Refresh_UI();
  389. vTaskDelay(5000 / portTICK_PERIOD_MS);
  390. }
  391. }
  392. /**
  393. * 云端上报任务
  394. * @param arg
  395. */
  396. _Noreturn static void cloud_report_task(void *arg) {
  397. for (;;) {
  398. if (xSemaphoreTake(report_task_sem, portMAX_DELAY) == pdTRUE) {
  399. ESP_LOGD(TAG, "cloud report");
  400. cJSON *data = cJSON_CreateObject();
  401. cJSON_AddNumberToObject(data, "power", xf_status.power);
  402. cJSON_AddNumberToObject(data, "set_max_hum", xf_status.set_max_hum);
  403. cJSON_AddNumberToObject(data, "filter_life_remaining", xf_status.filter_life_remaining);
  404. cJSON_AddNumberToObject(data, "set_filter_life_time", system_setting.filter_life_time);
  405. cJSON_AddNumberToObject(data, "mode", xf_status.mode);
  406. cJSON_AddNumberToObject(data, "temperature", (int) (sht30Data.temperature));
  407. cJSON_AddNumberToObject(data, "humidity", (int) (sht30Data.humidity));
  408. cJSON_AddNumberToObject(data, "air_quality", (int)sht30Data.air_quality);
  409. cJSON_AddNumberToObject(data, "co2", (int)sht30Data.co2);
  410. cJSON_AddNumberToObject(data, "fan_speed", xf_status.fan_speed);
  411. cJSON_AddNumberToObject(data, "timer_status", system_setting.timer_status);
  412. cJSON_AddNumberToObject(data, "duration", system_setting.duration);
  413. cJSON *root = cJSON_CreateObject();
  414. cJSON_AddStringToObject(root, "cmd", "status");
  415. cJSON_AddItemToObject(root, "params", data);
  416. publish_message_t msg = {
  417. .topic = "status",
  418. .data = root,
  419. };
  420. sparrow_publish_status(msg);
  421. }
  422. }
  423. //
  424. }
  425. _Noreturn static void report_trigger_task(void *arg) {
  426. for (;;) {
  427. if (cloud_connected) {
  428. xSemaphoreGive(report_task_sem);
  429. }
  430. vTaskDelay(pdTICKS_TO_MS(system_setting.report_data_duration * 1000));
  431. }
  432. }
  433. void xf_controller_init(system_setting_t *setting) {
  434. register_sparrow_commands(); // 注册云端指令
  435. report_task_sem = xSemaphoreCreateBinary();
  436. // timer_init_lx();
  437. // 启动状态同步任务
  438. xTaskCreate(status_sync_task, "status_sync_task", 4 * 1024, NULL, 5, &status_task_handle);
  439. // 启动云端上报任务,受信号量控制,无信号时任务不作操作,只有本地有操作或定时上报任务触发时才会上报。
  440. xTaskCreate(cloud_report_task, "report_task", 6 * 1024, NULL, 5, &cloud_report_handle);
  441. xTaskCreate(report_trigger_task, "trigger_task", 2 * 1024, NULL, 5, &report_trigger_handle);
  442. }
  443. void stop_xf_controller() {
  444. vTaskDelete(status_task_handle);
  445. vTaskDelete(cloud_report_handle);
  446. vTaskDelete(report_trigger_handle);
  447. // 停止modbus 协议 stack
  448. modbus_master_destroy();
  449. }