#include /** * @Author: 李建 * @Date: 2025/3/25 15:23 * Description: 空调控制器 * Copyright: Copyright (©) 2025 永续绿建. All rights reserved. */ #include #include "freertos/FreeRTOS.h" #include #include #include #include "fp_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 "modbus_slave.h" #include "pwm0_10.h" #include "sht30.h" #include "relay.h" #include "gateway/access.h" #define FAN_RELAY 1 // #define FAN_PWM 1 static const char *TAG = "xf_controller"; static uint8_t err_count = 0;// 通讯失败次数 static SemaphoreHandle_t report_task_sem; // 上报任务信号量,用于实现操作完成上立即上报当前状态 extern void set_ac_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; // 初始化空调参数 ac_status_t ac_status = { .power= 0, .fan_speed=3, .mode=1, .set_temp = 16, .filter_used_time=0, .filter_life_remaining=100, .error_code=0, }; xf_fault_t xf_fault[9] = { { .code="E1", .description="传感器故障", }, { .code="E2", .description="高水位故障", }, { .code="E3", .description="进水电磁阀故障", }, { .code="E4", .description="送风风机故障", }, { .code="E5", .description="排风风机故障", }, { .code="E6", .description="排水泵故障", }, { .code="E7", .description="通讯故障", }, { .code="E8", .description="预留", } }; 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); uint16_t *_filter_used_time = nvs_get_uint16(NVS_FILTER_USED_TIME); uint16_t *_set_temp = nvs_get_uint16(NVS_AC_SET_TEMP_KEY); uint16_t *_filter_life_remaining = nvs_get_uint16(NVS_FILTER_USED_TIME); uint16_t *_tw_status = nvs_get_uint16(NVS_TW_STATUS); uint16_t *_xf_mode = nvs_get_uint16(NVS_AC_SET_TEMP_KEY); ac_status.power= _power == NULL ? 0 : *_power; ac_status.mode = _mode == NULL ? 1 : *_mode; ac_status.fan_speed = _fan_speed == NULL ? 3 : *_fan_speed; ac_status.set_temp = _set_temp == NULL ? 16 : *_set_temp; ac_status.filter_used_time = _filter_used_time == NULL ? 0 : *_filter_used_time; ac_status.filter_life_remaining=_filter_life_remaining == NULL ? 0 : *_filter_life_remaining; ac_status.tw_status=_tw_status == NULL ? 0 : *_tw_status; ac_status.xf_mode=_xf_mode == NULL ? 0 : *_xf_mode; } esp_err_t ac_set_temp(bool saved) { if (saved) nvs_set_uint16(ac_status.set_temp, NVS_AC_SET_TEMP_KEY); if (cloud_connected) { xSemaphoreGive(report_task_sem); } modbus_set_reg(SLAVE_SET_TEMP_REG_ADDRESS, ac_status.set_temp); return ESP_OK; } // 设置电源 esp_err_t ac_set_power(bool saved) { esp_err_t err = ESP_OK; if(saved) nvs_set_uint16(ac_status.power, NVS_POWER_CONTROLLER); if (guider_ui.screen_main) { if(ac_status.power==0) { lvgl_port_lock(-1); // 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_humSet, LV_OBJ_FLAG_HIDDEN); lv_obj_clear_flag(guider_ui.screen_main_cont_mode, LV_OBJ_FLAG_HIDDEN); set_ac_mode(&guider_ui, ac_status.mode); lvgl_port_unlock(); } } if (cloud_connected) { xSemaphoreGive(report_task_sem); } modbus_set_reg(SLAVE_POWER_REG_ADDRESS, ac_status.power); return err; } void esp_timer_cb() { if(ac_status.power==1) { ac_status.filter_used_time++; }else{ } if(ac_status.filter_used_time>=system_setting.filter_life_time) { ac_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*5秒 esp_timer_start_periodic(esp_timer_handle, 5*60000 * 1000); // esp_timer_start_periodic(esp_timer_handle, 5000 * 1000); } //设置滤网剩余寿命 void xf_set_filter_life_remain(bool saved) { esp_err_t str=ESP_OK; if(saved) nvs_set_uint16(ac_status.filter_used_time, NVS_FILTER_USED_TIME); } // uint16_t get_fan_gear_vol(uint16_t value) { switch (value) { case 1: return modbus_get_reg(AIR_FIRST_VOL_ADDRESS_REGISTER); case 2: return modbus_get_reg(AIR_SECOND_VOL_ADDRESS_REGISTER); case 3: return modbus_get_reg(AIR_THIRD_VOL_ADDRESS_REGISTER); default: ; } return 0; } //设置新风风速 esp_err_t xf_set_fan_speed(bool saved) { esp_err_t err = ESP_OK; if(saved) nvs_set_uint16(ac_status.fan_speed, NVS_FAN_SPEED_SET); if (cloud_connected) { xSemaphoreGive(report_task_sem); } modbus_set_reg(SLAVE_FAN_LEVEL_REG_ADDRESS, ac_status.fan_speed); return err; } //设置手动新风 esp_err_t ac_set_xf_mode(bool saved) { esp_err_t err = ESP_OK; if(saved) nvs_set_uint16(ac_status.xf_mode, NVS_XF_MODE_SET); if (cloud_connected) { xSemaphoreGive(report_task_sem); } modbus_set_reg(SLAVE_FRESH_MODE_ADDRESS_REGISTER, ac_status.xf_mode); return err; } // 设置模式 esp_err_t xf_set_mode(bool saved) { esp_err_t err = ESP_OK; if(saved) nvs_set_uint16(ac_status.mode, NVS_MODEL_CONTROLLER); if (cloud_connected) { xSemaphoreGive(report_task_sem); } modbus_set_reg(SLAVE_WORK_MODE_REG_ADDRESS, ac_status.mode); 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"); ac_status.power = value->valueint; ac_set_power(true); update_power_ui(ac_status.power); cJSON_Delete(data); } } static void cloud_set_mode(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); ac_status.mode = value->valueint; if (guider_ui.screen_main) { lvgl_port_lock(-1); set_ac_mode(&guider_ui, ac_status.mode); lvgl_port_unlock(); } xf_set_mode(true); cJSON_Delete(data); } } static void cloud_set_temp(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); ac_status.set_temp = value->valueint; if (guider_ui.screen_main) { lvgl_port_lock(-1); lv_label_set_text_fmt(guider_ui.screen_main_label_temp_set, "%d",value->valueint); lv_arc_set_value(guider_ui.screen_main_arc_temp,value->valueint*10); lvgl_port_unlock(); } ac_set_temp(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"); ac_status.fan_speed = value->valueint; if (guider_ui.screen_main) { lvgl_port_lock(-1); set_fan_level(&guider_ui, ac_status.fan_speed); lvgl_port_unlock(); } xf_set_fan_speed(true); cJSON_Delete(data); } } static void cloud_set_xf_mode(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); ac_status.xf_mode = value->valueint; if (system_setting.xf_have==1) { if (guider_ui.screen_main) { lvgl_port_lock(-1); // if (ac_status.xf_mode==2) { // lv_obj_remove_flag(guider_ui.screen_main_lab_new_fan_mode, LV_OBJ_FLAG_HIDDEN); // }else { // lv_obj_add_flag(guider_ui.screen_main_lab_new_fan_mode, LV_OBJ_FLAG_HIDDEN); // } lvgl_port_unlock(); } ac_set_xf_mode(true); } cJSON_Delete(data); } } esp_err_t set_fan_vol(uint8_t gear, uint16_t *value) { uint16_t addr = SLAVE_FAV_FIRST_VOL; switch (gear) { case 1: addr = SLAVE_FAV_FIRST_VOL; break; case 2: addr = SLAVE_FAN_SECOND_VOL; break; case 3: addr = SLAVE_FAN_THIRD_VOL; break; default: break; } modbus_set_reg(addr,*value); return ESP_OK; } //设置电压档位 static void cloud_set_fan_vol(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); cJSON *gear = cJSON_GetObjectItemCaseSensitive(data, "gear"); set_fan_vol(gear->valueint, (uint16_t *) &value->valueint); 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) { ac_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); // 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); sparrow_command set_fan_vol = { .name = "setFanVoltage", .unpack = &cloud_set_fan_vol }; register_sparrow_command(set_fan_vol); sparrow_command set_xf_mode = { .name = "setNewFan", .unpack = &cloud_set_xf_mode }; register_sparrow_command(set_xf_mode); sparrow_command set_temp = { .name = "setTemp", .unpack = &cloud_set_temp }; register_sparrow_command(set_temp); } LV_IMAGE_DECLARE(_power_open_RGB565A8_36x36); LV_IMAGE_DECLARE(_liang_RGB565A8_30x30); LV_IMAGE_DECLARE(_cha_RGB565A8_30x30); /** * TODO:处理风故障信息 */ void handle_err_code() { uint8_t error_code[2] = {0}; esp_err_t err = ESP_OK; //err = mm_get_param(CID_ERROR_CODE, error_code); if (err == ESP_OK) { // ESP_LOGE(TAG, "error_code1:%d",*(uint16_t *)error_code); if (guider_ui.screen_main) { if((*(uint16_t *)error_code&0x0ff)==0) { lvgl_port_lock(-1); lv_obj_add_flag(guider_ui.screen_main_lab_err, LV_OBJ_FLAG_HIDDEN); lvgl_port_unlock(); } else{ lvgl_port_lock(-1); lv_obj_clear_flag(guider_ui.screen_main_lab_err, LV_OBJ_FLAG_HIDDEN); lvgl_port_unlock(); } } ac_status.error_code=(uint8_t)(*(uint16_t *)error_code&0x0ff); // ESP_LOGE(TAG, "error_code:%d",ac_status.error_code); } } void Refresh_UI() { //ac_status.filter_life_remaining=(100-100*ac_status.filter_used_time/system_setting.filter_life_time); modbus_set_reg(SLAVE_ENV_TEMP_REG_ADDRESS,sht30Data.temperature); modbus_set_reg(SLAVE_ENV_HUMIDITY_REG_ADDRESS,sht30Data.humidity); if (guider_ui.screen_main) { lvgl_port_lock(-1); // if (ac_status.xf_mode==2) { // lv_obj_remove_flag(guider_ui.screen_main_lab_new_fan_mode, LV_OBJ_FLAG_HIDDEN); // }else { // lv_obj_add_flag(guider_ui.screen_main_lab_new_fan_mode, LV_OBJ_FLAG_HIDDEN); // } // if(ac_status.filter_life_remaining<=10) // { // lv_obj_remove_flag(guider_ui.screen_main_label_filter_error, LV_OBJ_FLAG_HIDDEN); // }else{ // lv_obj_add_flag(guider_ui.screen_main_label_filter_error, LV_OBJ_FLAG_HIDDEN); // } // lv_label_set_text_fmt(guider_ui.screen_main_label_lvxin, "%d%%", ac_status.filter_life_remaining); lvgl_port_unlock(); } } void set_fan_speed(uint16_t value) { #ifdef FAN_RELAY set_fan_relay( value); #endif #ifdef FAN_PWM pwm0_10_set_val(get_fan_gear_vol()); pwm0_10_start(); #endif } void user_main_control() { uint16_t set_temp=0; uint16_t env_temp=0; set_temp=modbus_get_reg(SLAVE_SET_TEMP_REG_ADDRESS); env_temp=modbus_get_reg(SLAVE_ENV_TEMP_REG_ADDRESS); ESP_LOGE(TAG, "mode:%d.\n",modbus_get_reg(SLAVE_WORK_MODE_REG_ADDRESS)); if (modbus_get_reg(SLAVE_POWER_REG_ADDRESS)==1) { set_fan_speed(modbus_get_reg(SLAVE_FAN_LEVEL_REG_ADDRESS)); switch (modbus_get_reg(SLAVE_WORK_MODE_REG_ADDRESS)) { case COLD: if (env_temp-set_temp>=1) { tw_valve_open(); }else if(env_temp-set_temp<-1){ tw_valve_close(); }else { } break; case HEAT: if (env_temp-set_temp<-1) { //ESP_LOGE(TAG, "HEAT env_temp-set_temp<-1).\n"); tw_valve_open(); }else if(env_temp-set_temp>=1) { //ESP_LOGE(TAG, " HEAT env_temp-set_temp>=1.\n"); tw_valve_close(); }else { } break; case TF: tw_valve_close(); break; default: ; } }else { tw_valve_close(); set_fan_speed(0); pwm0_10_set_val(0); pwm0_10_stop(); } } #define SLAVE_POWER_REG_ADDRESS (0) // 电源 #define SLAVE_WORK_MODE_REG_ADDRESS (1) // 工作模式 #define SLAVE_FRESH_MODE_ADDRESS_REGISTER (2) // 新风模式寄存器 #define SLAVE_FAN_LEVEL_REG_ADDRESS (4) // 风速档位 #define SLAVE_TW_VALVE_STAT_ADDRESS (5) // 二通阀状态 #define SLAVE_FAV_FIRST_VOL (0x15) // 风机一档电压 #define SLAVE_FAN_SECOND_VOL (0x16) // 风机二档电压 #define SLAVE_FAN_THIRD_VOL (0x17) // 风机三档电压 #define SLAVE_SET_TEMP_REG_ADDRESS (0x0E) // 设置温度 #define SLAVE_ENV_TEMP_REG_ADDRESS (0x1B) // 环境温度 #define SLAVE_ENV_HUMIDITY_REG_ADDRESS (0x1C) // 环境湿度 #define SLAVE_COMMUNICATION_STATUS_REG_ADDRESS (8) // 通讯状态 /** * 状态同步任务 * @param arg */ _Noreturn static void status_sync_task(void *arg) { esp_err_t err = ESP_OK; modbus_set_reg(SLAVE_POWER_REG_ADDRESS,ac_status.power); modbus_set_reg(SLAVE_WORK_MODE_REG_ADDRESS,ac_status.mode); modbus_set_reg(SLAVE_FRESH_MODE_ADDRESS_REGISTER,ac_status.xf_mode); modbus_set_reg(SLAVE_FAN_LEVEL_REG_ADDRESS,ac_status.fan_speed); modbus_set_reg(SLAVE_TW_VALVE_STAT_ADDRESS,ac_status.tw_status); modbus_set_reg(SLAVE_FAV_FIRST_VOL,system_setting.fan_first_vol); modbus_set_reg(SLAVE_FAN_SECOND_VOL,system_setting.fan_second_vol); modbus_set_reg(SLAVE_FAN_THIRD_VOL,system_setting.fan_third_vol); modbus_set_reg(SLAVE_SET_TEMP_REG_ADDRESS,ac_status.set_temp); for (;;) { user_main_control(); Refresh_UI(); //ESP_LOGE(TAG, "status_sync_task.\n"); //handle_err_code(); vTaskDelay(3000 / 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", ac_status.power); cJSON_AddNumberToObject(data, "temperature", (int) (sht30Data.temperature)); cJSON_AddNumberToObject(data, "humidity", (int) (sht30Data.humidity)); cJSON_AddNumberToObject(data, "mode", ac_status.mode); cJSON_AddNumberToObject(data, "fan_speed", ac_status.fan_speed); cJSON_AddNumberToObject(data, "set_temp", ac_status.set_temp);//设置温度 cJSON_AddNumberToObject(data, "tw_status", ac_status.tw_status);//二通阀状态 cJSON_AddNumberToObject(data, "xf_have", system_setting.xf_have);//是否有新风模块 cJSON_AddNumberToObject(data, "xf_mode", ac_status.xf_mode);//手动新风 cJSON_AddNumberToObject(data, "filter_life_remaining", ac_status.filter_life_remaining); cJSON_AddNumberToObject(data, "set_filter_life_time", system_setting.filter_life_time); cJSON_AddNumberToObject(data, "air_quality", (int)sht30Data.air_quality); cJSON_AddNumberToObject(data, "co2", (int)sht30Data.co2); 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)); } } /** * 云端上报任务 * @param arg */ _Noreturn static void cloud_fault_report_task(void *arg) { for (;;) { if (cloud_connected) { if (xSemaphoreTake(report_task_sem, portMAX_DELAY) == pdFALSE) { ESP_LOGD(TAG, "cloud report"); cJSON *data = cJSON_CreateArray(); cJSON *fault = cJSON_CreateObject(); for (int i = 0; i < 8; i++) { if (ac_status.error_code & (1 << i)) { cJSON_AddStringToObject(fault, "code", (const char *) xf_fault[i].code); cJSON_AddStringToObject(fault, "desc", (const char *) xf_fault[i].description); cJSON_AddItemToArray(data, fault); } } 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); } vTaskDelay(60*1000 / portTICK_PERIOD_MS); } } } void ac_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); //故障上报 // xTaskCreate(cloud_fault_report_task, "report_task", 6 * 1024, NULL, 5, &cloud_report_handle); } void stop_xf_controller() { vTaskDelete(status_task_handle); vTaskDelete(cloud_report_handle); vTaskDelete(report_trigger_handle); // 停止modbus 协议 stack modbus_master_destroy(); }