#include /** * @Author: 李建 * @Date: 2025/3/25 15:23 * Description: 空调控制器 * Copyright: Copyright (©) 2025 永续绿建. All rights reserved. */ #include #include "freertos/FreeRTOS.h" #include #include #include #include "xf_controller.h" #include "sub_device/command.h" #include "modbus_master.h" #include "system/miscellaneous_interface.h" #include "lvgl_port.h" #include "main.h" #include "esp_modbus_master.h" #include "mb_endianness_utils.h" #include "sht30.h" #include "gateway/access.h" static const char *TAG = "ac_controller"; static uint8_t err_count = 0;// 通讯失败次数 static SemaphoreHandle_t report_task_sem; // 上报任务信号量,用于实现操作完成上立即上报当前状态 extern void set_xf_mode(lv_ui *ui, uint16_t mode); static TaskHandle_t cloud_report_handle; static TaskHandle_t report_trigger_handle; static TaskHandle_t status_task_handle; // 初始化空调参数 xf_status_t xf_status = { .power= 0, .fan_speed=3, .mode=1, .filter_used_time=0, .set_max_hum=70, .filter_life_remaining=100, }; void read_xf_status(system_setting_t *setting) { uint16_t *_power = nvs_get_uint16(NVS_POWER_CONTROLLER); uint16_t *_mode = nvs_get_uint16(NVS_MODEL_CONTROLLER); uint16_t *_fan_speed = nvs_get_uint16(NVS_FAN_SPEED_SET); uint32_t *_filter_used_time = nvs_get_uint32(NVS_FILTER_USED_TIME); uint16_t *_set_hum_max = nvs_get_uint16(NVS_MAX_HUM_SET); // uint16_t *_set_hum_min = nvs_get_uint16(NVS_MIN_HUM_SET); xf_status.power= _power == NULL ? 0 : *_power; xf_status.mode = _mode == NULL ? 1 : *_mode; xf_status.fan_speed = _fan_speed == NULL ? 3 : *_fan_speed; xf_status.filter_used_time = _filter_used_time == NULL ? 0 : *_filter_used_time; xf_status.set_max_hum = _set_hum_max == NULL ? 70 : *_set_hum_max; } // 设置电源 esp_err_t xf_set_power(bool saved) { esp_err_t err = ESP_OK; if(saved) nvs_set_uint16(xf_status.power, NVS_POWER_CONTROLLER); if (guider_ui.screen_main) { if(xf_status.power==0) { lvgl_port_lock(-1); lv_obj_add_flag(guider_ui.screen_main_cont_speedset, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(guider_ui.screen_main_cont_humSet, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(guider_ui.screen_main_cont_mode, LV_OBJ_FLAG_HIDDEN); lvgl_port_unlock(); }else{ lvgl_port_lock(-1); lv_obj_clear_flag(guider_ui.screen_main_cont_speedset, LV_OBJ_FLAG_HIDDEN); lv_obj_clear_flag(guider_ui.screen_main_cont_humSet, LV_OBJ_FLAG_HIDDEN); lv_obj_clear_flag(guider_ui.screen_main_cont_mode, LV_OBJ_FLAG_HIDDEN); lv_label_set_text_fmt(guider_ui.screen_main_label_humSet_vul, "%d%%", xf_status.set_max_hum); lv_slider_set_value(guider_ui.screen_main_slider_speedSet_sign, xf_status.fan_speed*20, LV_ANIM_ON); lv_label_set_text_fmt(guider_ui.screen_main_label_spedSet_vul, "%d档",xf_status.fan_speed); lv_slider_set_value(guider_ui.screen_main_slider_humSet_sign, xf_status.set_max_hum, LV_ANIM_ON); set_xf_mode(&guider_ui, xf_status.mode); lvgl_port_unlock(); } } if (cloud_connected) { xSemaphoreGive(report_task_sem); } return err; } void esp_timer_cb() { if(xf_status.power==1) { xf_status.filter_used_time++; }else{ } if(xf_status.filter_used_time>=system_setting.filter_life_time) { xf_status.filter_used_time=system_setting.filter_life_time; } xf_set_filter_life_remain(true); } esp_timer_handle_t esp_timer_handle = 0; //定时器句柄 void timer_init_lx() { //定时器结构体初始化 esp_timer_create_args_t fw_timer = { .callback = &esp_timer_cb, //定时器回调函数 .arg = NULL, //传递给回调函数的参数 .name = "esp_timer", //定时器名称 }; //创建定时器 esp_timer_create(&fw_timer, &esp_timer_handle); //启动定时器,定时器周期为60秒 esp_timer_start_periodic(esp_timer_handle, 60000 * 1000); } // 设置最大湿度 esp_err_t xf_set_max_hum(bool saved) { esp_err_t err = ESP_OK; if(saved) nvs_set_uint16(xf_status.set_max_hum, NVS_MAX_HUM_SET); if (cloud_connected) { xSemaphoreGive(report_task_sem); } return err; } // 设置最小湿度 //esp_err_t xf_set_min_hum(bool saved) { // if(saved) // nvs_set_uint16(xf_status.set_min_hum, NVS_MIN_HUM_SET); // return mm_set_param(CID_MIN_HUMIDITY, (uint8_t *)&xf_status.set_min_hum); //} //设置滤网剩余寿命 void xf_set_filter_life_remain(bool saved) { if(saved) nvs_set_uint16(xf_status.filter_used_time, NVS_FILTER_USED_TIME); } //设置新风风速 esp_err_t xf_set_fan_speed(bool saved) { esp_err_t err = ESP_OK; if(saved) nvs_set_uint16(xf_status.fan_speed, NVS_FAN_SPEED_SET); if (cloud_connected) { xSemaphoreGive(report_task_sem); } return err; } // 设置模式 esp_err_t xf_set_mode(bool saved) { esp_err_t err = ESP_OK; if(saved) nvs_set_uint16(xf_status.mode, NVS_MODEL_CONTROLLER); if (cloud_connected) { xSemaphoreGive(report_task_sem); } return err; } static void update_power_ui(bool on_off) { // 同步更新 UI if (guider_ui.screen_main) { lvgl_port_lock(-1); lv_obj_set_state(guider_ui.screen_main_imgbtn_power, LV_IMAGEBUTTON_STATE_CHECKED_RELEASED, on_off); lvgl_port_unlock(); } } // 云端: 设置上报时间间隔 static void cloud_set_report_duration(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); if (cJSON_IsNumber(value)) { system_setting.report_data_duration = value->valueint; save_system_setting(&system_setting); } } } static void cloud_set_power(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); xf_status.power = value->valueint; xf_set_power(true); update_power_ui(xf_status.power); cJSON_Delete(data); } } static void cloud_set_max_hum(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); if((value->valueint<=70)&&(value->valueint>=system_setting.set_min_hum)) { xf_status.set_max_hum = value->valueint; if (guider_ui.screen_main) { lvgl_port_lock(-1); lv_label_set_text_fmt(guider_ui.screen_main_label_humSet_vul, "%d%%", xf_status.set_max_hum); lv_slider_set_value(guider_ui.screen_main_slider_humSet_sign, xf_status.set_max_hum,LV_ANIM_OFF); lvgl_port_unlock(); } xf_set_max_hum(true); } cJSON_Delete(data); } } static void cloud_set_mode(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); xf_status.mode = value->valueint; if (guider_ui.screen_main) { lvgl_port_lock(-1); set_xf_mode(&guider_ui, xf_status.mode); lvgl_port_unlock(); } xf_set_mode(true); cJSON_Delete(data); } } static void cloud_set_fan_level(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); xf_status.fan_speed = value->valueint; if (guider_ui.screen_main) { lvgl_port_lock(-1); lv_slider_set_value(guider_ui.screen_main_slider_speedSet_sign, xf_status.fan_speed*20, LV_ANIM_ON); lv_label_set_text_fmt(guider_ui.screen_main_label_spedSet_vul, "%d档",xf_status.fan_speed); lvgl_port_unlock(); } xf_set_fan_speed(true); cJSON_Delete(data); } } static void cloud_set_filter_life(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); system_setting.filter_life_time = value->valueint; save_system_setting(&system_setting); cJSON_Delete(data); } } static void cloud_reset_filter_life_remain(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { xf_status.filter_used_time=0; xf_set_filter_life_remain(true); cJSON_Delete(data); } } static void cloud_set_min_hum(char *params) { } //注册云端指令s void register_sparrow_commands(void) { sparrow_command set_power_cmd = { .name = "setPower", .unpack = &cloud_set_power }; register_sparrow_command(set_power_cmd); sparrow_command set_max_hum_cmd = { .name = "setMaxHum", .unpack = &cloud_set_max_hum }; register_sparrow_command(set_max_hum_cmd); // setMode sparrow_command set_mode_cmd = { .name = "setMode", .unpack = &cloud_set_mode }; register_sparrow_command(set_mode_cmd); // setFanLevel sparrow_command set_fan_level_cmd = { .name = "setFanLevel", .unpack = &cloud_set_fan_level, }; register_sparrow_command(set_fan_level_cmd); // setNewFan sparrow_command set_filter_life_cmd = { .name = "setFilterLife", .unpack = &cloud_set_filter_life, }; register_sparrow_command(set_filter_life_cmd); // setFanVoltage sparrow_command set_min_hum_cmd = { .name = "setMinHumCmd", .unpack = &cloud_set_min_hum, }; register_sparrow_command(set_min_hum_cmd); sparrow_command set_duration = { .name = "setReportDuration", .unpack = &cloud_set_report_duration }; register_sparrow_command(set_duration); sparrow_command reset_filter_life_remain = { .name = "setReportDuration", .unpack = &cloud_reset_filter_life_remain }; register_sparrow_command(reset_filter_life_remain); } LV_IMAGE_DECLARE(_power_open_RGB565A8_36x36); LV_IMAGE_DECLARE(_liang_RGB565A8_30x30); LV_IMAGE_DECLARE(_cha_RGB565A8_30x30); void Refresh_UI() { uint8_t type = PARAM_TYPE_U16; uint8_t temp_data[2] = {0}; // temporary buffer to hold maximum CID size uint16_t temp_data_u16 = 500; esp_err_t err = ESP_OK; const mb_parameter_descriptor_t *param_descriptor = NULL; err = mm_get_param(CID_RETURN_AIR_PM25, temp_data); if (err == ESP_OK) { sht30Data.air_quality=*(uint16_t *)temp_data; if (guider_ui.screen_main) { lvgl_port_lock(-1); lv_arc_set_value(guider_ui.screen_main_arc_AIQ, 500); if (*(uint16_t *) temp_data < 50) { // lv_arc_set_value(guider_ui.screen_main_arc_AIQ, *(uint16_t *) temp_data); lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_AIQ_sign, &_you_RGB565A8_30x30, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_arc_color(guider_ui.screen_main_arc_AIQ, lv_color_hex(0x2FDA64), LV_PART_INDICATOR | LV_STATE_DEFAULT); } else if (*(uint16_t *) temp_data < 150) { // lv_arc_set_value(guider_ui.screen_main_arc_AIQ, *(uint16_t *) temp_data); lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_AIQ_sign, &_liang_RGB565A8_30x30, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_arc_color(guider_ui.screen_main_arc_AIQ, lv_color_hex(0xFFF502), LV_PART_INDICATOR | LV_STATE_DEFAULT); } else if (*(uint16_t *) temp_data <= 500) { // lv_arc_set_value(guider_ui.screen_main_arc_AIQ, *(uint16_t *) temp_data); lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_AIQ_sign, &_cha_RGB565A8_30x30, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_arc_color(guider_ui.screen_main_arc_AIQ, lv_color_hex(0xd81e06), LV_PART_INDICATOR | LV_STATE_DEFAULT); } else if (*(uint16_t *) temp_data > 500) { // lv_arc_set_value(guider_ui.screen_main_arc_AIQ, temp_data_u16); lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_AIQ_sign, &_cha_RGB565A8_30x30, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_arc_color(guider_ui.screen_main_arc_AIQ, lv_color_hex(0xd81e06), LV_PART_INDICATOR | LV_STATE_DEFAULT); } else { } lv_label_set_text_fmt(guider_ui.screen_main_label_AIQ_vul, "%d", *(uint16_t *) temp_data); lvgl_port_unlock(); } } err = mm_get_param(CID_RETURN_AIR_CO2, temp_data); if (err == ESP_OK) { err_count = 0; sht30Data.co2=*(uint16_t *)temp_data; if (guider_ui.screen_main) { lvgl_port_lock(-1); lv_label_set_text_fmt(guider_ui.screen_main_label_co2, "%d", *(uint16_t *) temp_data); lv_obj_add_flag(guider_ui.screen_main_lab_485_err, LV_OBJ_FLAG_HIDDEN); lvgl_port_unlock(); } } else { err_count++; if (err_count >= 254) err_count = 30; } if (err_count >= 30) { if (guider_ui.screen_main) { lvgl_port_lock(-1); lv_obj_remove_flag(guider_ui.screen_main_lab_485_err, LV_OBJ_FLAG_HIDDEN); lvgl_port_unlock(); } } xf_status.filter_life_remaining=(100-100*xf_status.filter_used_time/system_setting.filter_life_time); if (guider_ui.screen_main) { lvgl_port_lock(-1); lv_label_set_text_fmt(guider_ui.screen_main_label_lvxin, "%d%%", xf_status.filter_life_remaining); lvgl_port_unlock(); } } /** * 状态同步任务,主要用于与分风箱主控同步当前用户的参数,并处理通讯故障 * @param arg */ _Noreturn static void status_sync_task(void *arg) { esp_err_t err = ESP_OK; for (;;) { mm_set_param(CID_POWER, (uint8_t *)&xf_status.power); vTaskDelay(pdTICKS_TO_MS(500)); mm_set_param(CID_MODE, (uint8_t *)&xf_status.mode); vTaskDelay(pdTICKS_TO_MS(500)); mm_set_param(CID_FAN_SPEED, (uint8_t *)&xf_status.fan_speed); vTaskDelay(pdTICKS_TO_MS(500)); mm_set_param(CID_MAX_HUMIDITY, (uint8_t *)&xf_status.set_max_hum); vTaskDelay(pdTICKS_TO_MS(500)); // // err = xf_set_power(false); // err = xf_set_mode(true); // err = xf_set_fan_speed(true); // err =xf_set_max_hum(true); // err =xf_set_min_hum(true); Refresh_UI(); vTaskDelay(5000 / portTICK_PERIOD_MS); } } /** * 云端上报任务 * @param arg */ _Noreturn static void cloud_report_task(void *arg) { for (;;) { if (xSemaphoreTake(report_task_sem, portMAX_DELAY) == pdTRUE) { ESP_LOGD(TAG, "cloud report"); cJSON *data = cJSON_CreateObject(); cJSON_AddNumberToObject(data, "power", xf_status.power); cJSON_AddNumberToObject(data, "set_max_hum", xf_status.set_max_hum); cJSON_AddNumberToObject(data, "filter_life_remaining", xf_status.filter_life_remaining); cJSON_AddNumberToObject(data, "set_filter_life_time", system_setting.filter_life_time); cJSON_AddNumberToObject(data, "mode", xf_status.mode); cJSON_AddNumberToObject(data, "temperature", (int) (sht30Data.temperature)); cJSON_AddNumberToObject(data, "humidity", (int) (sht30Data.humidity)); cJSON_AddNumberToObject(data, "air_quality", (int)sht30Data.air_quality); cJSON_AddNumberToObject(data, "co2", (int)sht30Data.co2); cJSON_AddNumberToObject(data, "fan_speed", xf_status.fan_speed); cJSON_AddNumberToObject(data, "timer_status", system_setting.timer_status); cJSON_AddNumberToObject(data, "duration", system_setting.duration); cJSON *root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "cmd", "status"); cJSON_AddItemToObject(root, "params", data); publish_message_t msg = { .topic = "status", .data = root, }; sparrow_publish_status(msg); } } // } _Noreturn static void report_trigger_task(void *arg) { for (;;) { if (cloud_connected) { xSemaphoreGive(report_task_sem); } vTaskDelay(pdTICKS_TO_MS(system_setting.report_data_duration * 1000)); } } void xf_controller_init(system_setting_t *setting) { register_sparrow_commands(); // 注册云端指令 report_task_sem = xSemaphoreCreateBinary(); timer_init_lx(); // 启动状态同步任务 xTaskCreate(status_sync_task, "status_sync_task", 4 * 1024, NULL, 5, &status_task_handle); // 启动云端上报任务,受信号量控制,无信号时任务不作操作,只有本地有操作或定时上报任务触发时才会上报。 xTaskCreate(cloud_report_task, "report_task", 6 * 1024, NULL, 5, &cloud_report_handle); xTaskCreate(report_trigger_task, "trigger_task", 2 * 1024, NULL, 5, &report_trigger_handle); } void stop_xf_controller() { vTaskDelete(status_task_handle); vTaskDelete(cloud_report_handle); vTaskDelete(report_trigger_handle); // 停止modbus 协议 stack modbus_master_destroy(); }