/** * @Author: 李建 * @Date: 2025/9/9 13:55 * Description: 热泵控制器 * Copyright: Copyright (©) 2025 永续绿建. All rights reserved. */ #include "heatpump_controller.h" #include "system/miscellaneous_interface.h" #include "freertos/FreeRTOS.h" #include #include #include #include "freertos/semphr.h" #include "sht30.h" #include "gateway/access.h" #include "sub_device/command.h" #include "lvgl_port.h" #include "modbus_master.h" hp_controller_t hp_controller = {0}; static SemaphoreHandle_t report_task_sem; // 上报任务信号量,用于实现操作完成上立即上报当前状态 static TaskHandle_t cloud_report_handle; static const char *TAG = "hp_controller"; static TaskHandle_t report_trigger_handle; static TaskHandle_t status_task_handle; void set_bit(uint16_t *num, uint8_t bit, bool value) { if (bit > 15) return; if (value) { *num |= (1 << bit); } else { *num &= ~(1 << bit); } } bool get_bit(uint16_t num, uint8_t bit) { if (bit > 15) return false; return (num >> bit) & 1; } esp_err_t set_relay_status(uint8_t no, uint8_t value) { esp_err_t err = ESP_OK; set_bit(&hp_controller.relay_status, no, value); nvs_set_uint16(hp_controller.relay_status, NVS_RELEY_STATUS_KEY); if (cloud_connected) { xSemaphoreGive(report_task_sem); } return err; } static void update_power_ui(bool on_off) { // 同步更新 UI if (guider_ui.screen) { lvgl_port_lock(-1); //lv_obj_set_state(guider_ui.screen_ib_power, LV_IMAGEBUTTON_STATE_CHECKED_RELEASED, on_off); if (on_off) { lv_obj_remove_flag(guider_ui.screen_cont_power_on, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(guider_ui.screen_cont_power_off, LV_OBJ_FLAG_HIDDEN); } else { lv_obj_remove_flag(guider_ui.screen_cont_power_off, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(guider_ui.screen_cont_power_on, LV_OBJ_FLAG_HIDDEN); } lvgl_port_unlock(); } } /******************************************云端指令开始*************************************************************/ // 云端指令:设置电源开关 static void cloud_set_power(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); hp_controller.power = value->valueint; heatpump_set_power(true); update_power_ui(hp_controller.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"); hp_controller.mode = value->valueint; if (guider_ui.screen) { if (hp_controller.mode == 1) { lvgl_port_lock(0); lv_label_set_text_static(guider_ui.screen_lab_mode, "制冷"); lv_obj_set_style_arc_color(guider_ui.screen_arc_temp, lv_color_hex(0x00d9ff), LV_PART_INDICATOR | LV_STATE_DEFAULT); lv_label_set_text_fmt(guider_ui.screen_lab_ac_temp, "%d", hp_controller.cool_set_temp); lv_obj_add_state(guider_ui.screen_ib_cool, LV_STATE_CHECKED); lv_obj_remove_state(guider_ui.screen_ib_heat, LV_STATE_CHECKED); lv_arc_set_range(guider_ui.screen_arc_temp, 7 * 2, 22 * 2); lv_arc_set_value(guider_ui.screen_arc_temp, hp_controller.cool_set_temp * 2); lvgl_port_unlock(); } else if (hp_controller.mode == 2) { lvgl_port_lock(0); lv_obj_set_style_arc_color(guider_ui.screen_arc_temp, lv_color_hex(0xfa890a), LV_PART_INDICATOR | LV_STATE_DEFAULT); lv_label_set_text_static(guider_ui.screen_lab_mode, "制热"); lv_label_set_text_fmt(guider_ui.screen_lab_ac_temp, "%d", hp_controller.heat_set_temp); lv_obj_remove_state(guider_ui.screen_ib_cool, LV_STATE_CHECKED); lv_obj_add_state(guider_ui.screen_ib_heat, LV_STATE_CHECKED); lv_arc_set_range(guider_ui.screen_arc_temp, 30 * 2, 60 * 2); lv_arc_set_value(guider_ui.screen_arc_temp, hp_controller.heat_set_temp * 2); lvgl_port_unlock(); } } heatpump_set_mode(hp_controller.mode); cJSON_Delete(data); } } // 云端指令:设置水温 static void cloud_set_water_temp(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); heatpump_set_temp(value->valueint); // 更新UI if (guider_ui.screen) { lvgl_port_lock(0); lv_label_set_text_fmt(guider_ui.screen_lab_ac_temp, "%d", value->valueint); lv_arc_set_value(guider_ui.screen_arc_temp, value->valueint); lvgl_port_unlock(); } cJSON_Delete(data); } } // 云端指令 :设置分集水器某一路开关 static void cloud_set_IO(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); cJSON *no = cJSON_GetObjectItemCaseSensitive(data, "no"); set_relay_status(no->valueint - 1, value->valueint); // 要减1 cJSON_Delete(data); } } // 云端指令:联动开关 static void cloud_set_link(char *params) { cJSON *data = cJSON_Parse(params); if (data != NULL) { cJSON *value = cJSON_GetObjectItemCaseSensitive(data, "value"); hp_controller.linkage = value->valueint; heatpump_set_linkage(); cJSON_Delete(data); } } /******************************************云端指令END*************************************************************/ static void register_sparrow_commands() { 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); // setWaterTemp sparrow_command set_water_temp = { .name = "setWaterTemp", .unpack = &cloud_set_water_temp }; register_sparrow_command(set_water_temp); // setIO sparrow_command set_io = { .name = "setIO", .unpack = &cloud_set_IO }; register_sparrow_command(set_io); // setLinkage sparrow_command set_link = { .name = "setLinkage", .unpack = &cloud_set_link }; register_sparrow_command(set_link); } _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 status_sync_task(void *arg) { esp_err_t err = ESP_OK; for (;;) { // mm_set_param(CID_HP_POWER, &hp_controller.power); // mm_set_param(CID_HP_SET_MODE, &hp_controller.mode); // if (hp_controller.mode == MODE_COOL) mm_set_param(CID_HP_COOL_SET_TEMP, &hp_controller.cool_set_temp); // if (hp_controller.mode == MODE_HEAT) mm_set_param(CID_HP_HEAT_SET_TEMP, &hp_controller.heat_set_temp); // mm_get_param(CID_HP_OUTDOOR_TEMP, &hp_controller.outdoor_temp); // mm_get_param(CID_HP_OUT_WATER_TEMP, &hp_controller.out_water_temp); // mm_get_param(CID_HP_IN_WATER_TEMP, &hp_controller.in_water_temp); uint8_t status = get_bit(hp_controller.relay_status, 0); mm_set_param(CID_RELAY_1_STATUS, &status); status = get_bit(hp_controller.relay_status, 1); mm_set_param(CID_RELAY_2_STATUS, &status); status = get_bit(hp_controller.relay_status, 2); mm_set_param(CID_RELAY_3_STATUS, &status); status = get_bit(hp_controller.relay_status, 3); mm_set_param(CID_RELAY_4_STATUS, &status); status = get_bit(hp_controller.relay_status, 4); mm_set_param(CID_RELAY_5_STATUS, &status); status = get_bit(hp_controller.relay_status, 5); mm_set_param(CID_RELAY_6_STATUS, &status); status = get_bit(hp_controller.relay_status, 6); mm_set_param(CID_RELAY_7_STATUS, &status); status = get_bit(hp_controller.relay_status, 7); mm_set_param(CID_RELAY_8_STATUS, &status); 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", hp_controller.power); cJSON_AddNumberToObject(data, "cool_set_water_temp", hp_controller.cool_set_temp); cJSON_AddNumberToObject(data, "heat_set_water_temp", hp_controller.heat_set_temp); cJSON_AddNumberToObject(data, "mode", hp_controller.mode); cJSON_AddNumberToObject(data, "temperature", (int) sht30Data.temperature); cJSON_AddNumberToObject(data, "humidity", (int) sht30Data.humidity); cJSON_AddNumberToObject(data, "outdoor_temp", hp_controller.outdoor_temp); cJSON_AddNumberToObject(data, "out_water_temp", hp_controller.out_water_temp); cJSON_AddNumberToObject(data, "in_water_temp", hp_controller.in_water_temp); cJSON_AddNumberToObject(data, "fjsq_exist", hp_controller.fjsq_exist); cJSON_AddNumberToObject(data, "fjsq_status", hp_controller.relay_status); 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); } } } void heatpump_controller_init(system_setting_t *setting) { hp_controller.mode = setting->hp_mode; hp_controller.power = setting->hp_power; hp_controller.cool_set_temp = setting->hp_cool_set_temp; hp_controller.heat_set_temp = setting->hp_heat_set_temp; hp_controller.relay_status = setting->reley_status; hp_controller.linkage = setting->allow_linkage; register_sparrow_commands(); report_task_sem = xSemaphoreCreateBinary(); // 启动状态同步任务 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); } esp_err_t heatpump_set_power(bool saved) { esp_err_t err = ESP_OK; if (saved) nvs_set_uint16(hp_controller.power, NVS_HP_POWER_KEY); if (cloud_connected) { xSemaphoreGive(report_task_sem); } return err; } esp_err_t heatpump_set_mode(uint8_t mode) { esp_err_t err = ESP_OK; nvs_set_uint16(hp_controller.mode, NVS_HP_MODE_KEY); if (cloud_connected) { xSemaphoreGive(report_task_sem); } return err; } esp_err_t heatpump_set_temp(uint16_t temp) { esp_err_t err = ESP_OK; if (hp_controller.mode == MODE_COOL)nvs_set_uint16(hp_controller.cool_set_temp, NVS_HP_COOL_SET_TEMP_KEY); if (hp_controller.mode == MODE_HEAT)nvs_set_uint16(hp_controller.heat_set_temp, NVS_HP_HEAT_SET_TEMP_KEY); if (cloud_connected) { xSemaphoreGive(report_task_sem); } return err; } esp_err_t heatpump_set_linkage() { esp_err_t err = ESP_OK; nvs_set_uint16(hp_controller.linkage, NVS_ALLOW_LINKAGE_KEY); return err; }