燕建军 1 ماه پیش
والد
کامیت
55fe73d3f7
6فایلهای تغییر یافته به همراه812 افزوده شده و 2 حذف شده
  1. 121 0
      main/app/include/ac_controller.h
  2. 152 0
      main/app/include/modbus_master.h
  3. 79 0
      main/app/include/sht30.h
  4. 205 0
      main/app/modbus_master.c
  5. 252 0
      main/app/sht30.c
  6. 3 2
      main/main.c

+ 121 - 0
main/app/include/ac_controller.h

@@ -0,0 +1,121 @@
+/**
+ * @Author: 李建
+ * @Date: 2025/3/25 15:23
+ * Description: 空调控制器
+ * Copyright: Copyright (©) 2025 永续绿建. All rights reserved.
+ */
+#ifndef WIRE_CONTROLLER_AC_CONTROLLER_H
+#define WIRE_CONTROLLER_AC_CONTROLLER_H
+
+#include "stdint.h"
+//#include "setting.h"
+//#include "esp_err.h"
+
+#define NVS_AC_POWER_KEY "AC_POWER"
+#define NVS_AC_SET_TEMP_KEY "AC_SET_TEMP"
+#define NVS_AC_MODE_KEY "AC_MODE"
+#define NVS_AC_WIND_SPEED_KEY "AC_WIND_SPEED"
+#define NVS_AC_NEW_FAN_MODE "AC_NF_M"
+
+
+// 风口序号
+enum {
+    FAN_VALVE_1 = 1,
+    FAN_VALVE_2,
+    FAN_VALVE_3,
+    FAN_VALVE_4,
+    FAN_VALVE_5,
+};
+/**
+ * 定义分控参数状态结构体
+ */
+typedef struct {
+    uint16_t power;
+    uint16_t humidity;
+    uint16_t temperature;
+    uint16_t fan_value;
+    uint16_t set_temp;
+    uint16_t id;
+} sub_controller_status_t;
+
+/**
+ * 定义保存空调设置参数的结构体
+ */
+typedef struct {
+    uint16_t power; // 电源状态
+    uint16_t set_temp;
+    uint16_t mode;
+    uint16_t wind_speed;
+    uint16_t new_fan_mode; // 新风模式控制(1:通风模式 2:新风模式)
+    uint16_t pm25;
+    uint16_t co2;
+    // 数组[1,0,0,1,0]表示1 号风阀和4号风阀受控
+    uint8_t main_fan_valve[5];
+    uint8_t sub_1_fan_valve[5];
+    uint8_t sub_2_fan_valve[5];
+    uint8_t sub_3_fan_valve[5];
+    uint8_t sub_4_fan_valve[5];
+    sub_controller_status_t sub_controllers[4];
+    uint16_t gmv_fault_code; //  多联机机内机故障
+    uint16_t fg_fault_code; // 风管机故障码
+} ac_status_t;
+enum {
+    COOL = 0,
+    HEAT,
+    DEHUM,
+    WIND,
+    HUM,
+};
+
+// 保存当前空调状态
+extern ac_status_t ac_status;
+
+/**
+ * 控制器初始化
+ * @param setting 系统设置参数
+ */
+void ac_controller_init(system_setting_t *setting);
+
+/**
+ * 设置风档
+ * @param saved 是否保存到 nvs 中
+ */
+esp_err_t ac_set_wind_speed(bool saved);
+
+/**
+ * 设置电源
+ * @param saved 是否保存到 nvs 中
+ */
+esp_err_t ac_set_power(bool saved);
+
+/**
+ * 设置温度
+ * @param saved 是否保存到 nvs 中
+ */
+esp_err_t ac_set_temp(bool saved);
+
+/**
+ * 设置模式
+ * @param saved 是否保存到 nvs 中
+ */
+esp_err_t ac_set_mode(bool saved);
+
+/**
+ * 设置对应风阀开度
+ * @param no 风阀序号
+ * @param lv 风阀开度
+ * @param is_sub_controller 是否为分控控制
+ */
+esp_err_t ac_set_fan_valve(uint8_t no, uint16_t lv, bool is_sub_controller);
+
+/**
+ * 注册云端指令
+ */
+void register_sparrow_commands(void);
+
+/**
+ * 停止所有任务,OTA时调用
+ */
+void stop_ac_controller();
+
+#endif //WIRE_CONTROLLER_AC_CONTROLLER_H

+ 152 - 0
main/app/include/modbus_master.h

@@ -0,0 +1,152 @@
+/**
+ * @Author: 李建
+ * @Date: 2025/4/27 15:35
+ * Description: 485主站
+ * Copyright: Copyright (©) 2025 永续绿建. All rights reserved.
+ */
+#ifndef WIRE_CONTROLLER_MODBUS_MASTER_H
+#define WIRE_CONTROLLER_MODBUS_MASTER_H
+
+#include "esp_err.h"
+#include "esp_modbus_master.h"
+
+#define MB_PORT_NUM     2   // Number of UART port used for Modbus connection
+#define MB_DEV_SPEED    9600  // The communication speed of the UART
+#define MB_GPIO_RX 2 // uart rx gpio port
+#define MB_GPIO_TX 1 // uart tx gpio port
+
+#define POWER_REG_ADDRESS 0x00 // 电源寄存器
+#define WORK_MOD_REG_ADDRESS 0x01 // 工作模式寄存器
+#define FAN_MODE_REG_ADDRESS 0x02 // 新风模式(0x01通风模式0x02新风模式)
+#define FAN_LEVEL_REG_ADDRESS 0x04 // 风速档位
+#define CHS_TYPE_REG_ADDRESS 0xB // 冷热源类型
+#define FAN_VALVE_1_REG_ADDRESS 0x07 // 风阀一开度
+#define FAN_VALVE_2_REG_ADDRESS 0x08 // 风阀二开度
+#define FAN_VALVE_3_REG_ADDRESS 0x09 // 风阀三开度
+#define FAN_VALVE_4_REG_ADDRESS 0x0A // 风阀四开度
+#define FAN_VALVE_5_REG_ADDRESS 0x43 // 风阀五开度
+#define FAN_VALVE_6_REG_ADDRESS 0x44 // 风阀六开度
+#define PM25_NUMBER_REG_ADDRESS 0x23 // PM2.5浓度
+#define CO2_NUMBER_REG_ADDRESS 0x24 // CO2浓度
+#define HUMIDITY_LIMIT_LOW_REG_ADDRESS 0x11
+#define HUMIDITY_LIMIT_HIGH_REG_ADDRESS 0x12
+// 电压一档寄存器地址
+#define VOLTAGE_1_REG_ADDRESS 0x15
+#define VOLTAGE_2_REG_ADDRESS 0x16
+#define VOLTAGE_3_REG_ADDRESS 0x17
+#define VOLTAGE_4_REG_ADDRESS 0x18
+#define VOLTAGE_5_REG_ADDRESS 0x19
+
+#define SET_TEMP_REG_ADDRESS 0x0E // 设置温度
+#define ENV_TEMP_REG_ADDRESS 0x1B // 环境温度
+#define ENV_HUMIDITY_REG_ADDRESS 0x1C // 环境湿度
+#define BOARD_ERR_REG_ADDRESS 0x2D // 错误码
+#define GMV_INTERNAL_NUMBER_REG_ADDRESS 0x59 // 多联机内机编号
+#define INTERNAL_ERR_REG_ADDRESS 0x39 // 内机错误码
+#define FG_ERR_2_REG_ADDRESS 0x3A // FG故障码
+#define FG_ERR_3_REG_ADDRESS 0x3B // FG故障码
+#define FG_ERR_4_REG_ADDRESS 0x3C // FG故障码
+#define FG_ERR_5_REG_ADDRESS 0x3D // FG故障码
+#define FG_ERR_6_REG_ADDRESS 0x3E // FG故障码
+#define FG_ERR_7_REG_ADDRESS 0x3F // FG故障码
+#define FG_ERR_8_REG_ADDRESS 0x40 // FG故障码
+#define FG_ERR_9_REG_ADDRESS 0x41 // FG故障码
+#define FG_ERR_10_REG_ADDRESS 0x42 // FG故障码
+#define FG_ERR_11_REG_ADDRESS 0x43 // FG故障码
+#define FG_ERR_12_REG_ADDRESS 0x44 // FG故障码
+#define FG_ERR_13_REG_ADDRESS 0x45 // FG故障码
+#define FG_ERR_14_REG_ADDRESS 0x46 // FG故障码
+
+#define INNER_2_POWER_REG_ADDRESS 0x78 // 内机二电源状态
+#define INNER_2_MODE_REG_ADDRESS 0x79 // 内机二工作模式
+#define INNER_2_SET_TEMP_REG_ADDRESS 0x7A // 内机二设定温度
+#define INNER_2_FAN_LEVEL_REG_ADDRESS 0x7B // 内机二风速档位
+
+#define INNER_3_POWER_REG_ADDRESS 0x7C // 内机三电源状态
+#define INNER_3_MODE_REG_ADDRESS 0x7D // 内机三工作模式
+#define INNER_3_SET_TEMP_REG_ADDRESS 0x7E // 内机三设定温度
+#define INNER_3_FAN_LEVEL_REG_ADDRESS 0x7F // 内机三风速档位
+
+// 定义协议 CID 表
+enum {
+    // 开关机
+    CID_POWER = 0,
+    // 工作模式
+    CID_MODE,
+    // 新风阀模式
+    CID_FAN_MODE,
+    // 送风风速
+    CID_FAN_SPEED,
+    // 风阀一开度
+    CID_FAN_1_DEGREE,
+    // 风阀二开度
+    CID_FAN_2_DEGREE,
+    // 风阀三开度
+    CID_FAN_3_DEGREE,
+    // 风阀四开度
+    CID_FAN_4_DEGREE,
+    // 风阀五开度
+    CID_FAN_5_DEGREE,
+    // 设定温度
+    CID_SET_TEMP,
+    // 电压一档
+    CID_VOLTAGE_1,
+    // 电压二档
+    CID_VOLTAGE_2,
+    // 电压三档
+    CID_VOLTAGE_3,
+    // 电压四档
+    CID_VOLTAGE_4,
+    // 电压五档
+    CID_VOLTAGE_5,
+    // 线控器处环境温度
+    CID_ENV_TEMP,
+    // 线控器处环境湿度
+    CID_ENV_HUMIDITY,
+    // PM2.5浓度
+    CID_PM25,
+    // CO2浓度
+    CID_CO2,
+    // 多联机内机地址
+    CID_INNER_ADDR,
+    // 驱动板故障类
+    CID_FAULT_CODE,
+    // 故障12
+    CID_FAULT_12,
+    // 故障13
+    CID_FAULT_13,
+    // 故障15
+    CID_FAULT_15,
+    // 故障19
+    CID_FAULT_19,
+    // 故障20
+    CID_FAULT_20,
+    // 故障21
+    CID_FAULT_21,
+    // 故障22
+    CID_FAULT_22,
+    // 故障23
+    CID_FAULT_23,
+    // 故障24
+    CID_FAULT_24,
+    // 故障26
+    CID_FAULT_26,
+};
+
+
+
+
+void modbus_master_init();
+
+esp_err_t mm_set_param(uint16_t cid, uint8_t *value);
+/**
+ * 发送 modbus 请求
+ * @param request
+ * @param data_prt
+ * @return
+ */
+esp_err_t send_request(mb_param_request_t * request, void * data_prt);
+
+void modbus_master_destroy();
+
+#endif //WIRE_CONTROLLER_MODBUS_MASTER_H

+ 79 - 0
main/app/include/sht30.h

@@ -0,0 +1,79 @@
+/**
+ * @Author: 李建
+ * @Date: 2025/4/22 13:48
+ * Description: 温湿度传感器
+ * Copyright: Copyright (©) 2025 永续绿建. All rights reserved.
+ */
+#ifndef WIRE_CONTROLLER_SHT30_H
+#define WIRE_CONTROLLER_SHT30_H
+
+
+#define I2C_MASTER_SCL_IO           GPIO_NUM_38      /*!< GPIO number used for I2C master clock */
+#define I2C_MASTER_SDA_IO           GPIO_NUM_39      /*!< GPIO number used for I2C master data  */
+#define I2C_MASTER_NUM              0                          /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
+#define I2C_MASTER_FREQ_HZ          400000                     /*!< I2C master clock frequency */
+#define I2C_MASTER_TX_BUF_DISABLE   0                          /*!< I2C master doesn't need buffer */
+#define I2C_MASTER_RX_BUF_DISABLE   0                          /*!< I2C master doesn't need buffer */
+#define SHT3X_TICKS_TO_WAIT           (100 / portTICK_PERIOD_MS)    // I2C读写的超时等待时间
+
+#define SHT3X_I2C_BUS                 I2C_NUM_1 // SHT30所在的I2C总线
+
+#define SHT3X_SLAVE_ADDRESS           0x44    // SHT30在I2C总线上的从机器件地址
+
+
+// SHT30命令列表
+typedef enum
+{
+    /* 软件复位命令 */
+
+    SOFT_RESET_CMD = 0x30A2,
+    /*
+    单次测量模式
+    命名格式:Repeatability_CS_CMD
+    CS:Clock stretching
+    */
+    HIGH_ENABLED_CMD    = 0x2C06,
+    MEDIUM_ENABLED_CMD  = 0x2C0D,
+    LOW_ENABLED_CMD     = 0x2C10,
+    HIGH_DISABLED_CMD   = 0x2400,
+    MEDIUM_DISABLED_CMD = 0x240B,
+    LOW_DISABLED_CMD    = 0x2416,
+
+    /*
+    周期测量模式
+    命名格式:Repeatability_MPS_CMD
+    MPS:measurement per second
+    */
+    HIGH_0_5_CMD   = 0x2032,
+    MEDIUM_0_5_CMD = 0x2024,
+    LOW_0_5_CMD    = 0x202F,
+    HIGH_1_CMD     = 0x2130,
+    MEDIUM_1_CMD   = 0x2126,
+    LOW_1_CMD      = 0x212D,
+    HIGH_2_CMD     = 0x2236,
+    MEDIUM_2_CMD   = 0x2220,
+    LOW_2_CMD      = 0x222B,
+    HIGH_4_CMD     = 0x2334,
+    MEDIUM_4_CMD   = 0x2322,
+    LOW_4_CMD      = 0x2329,
+    HIGH_10_CMD    = 0x2737,
+    MEDIUM_10_CMD  = 0x2721,
+    LOW_10_CMD     = 0x272A,
+    /* 周期测量模式读取数据命令 */
+    READOUT_FOR_PERIODIC_MODE = 0xE000,
+} SHT30_CMD;
+
+/**
+ * 包含数据的结构体
+ */
+typedef struct {
+    float temperature;
+    float humidity;
+}sht30_data_t;
+
+extern sht30_data_t sht30Data;
+
+void sht30_init();
+
+
+#endif //WIRE_CONTROLLER_SHT30_H

+ 205 - 0
main/app/modbus_master.c

@@ -0,0 +1,205 @@
+/**
+ * @Author: 李建
+ * @Date: 2025/4/27 15:35
+ * Description: 实现485主站
+ * Copyright: Copyright (©) 2025 永续绿建. All rights reserved.
+ */
+#include <esp_err.h>
+#include "modbus_master.h"
+#include "mbcontroller.h"
+
+#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
+static const char *TAG = "MODBUS_MASTER";
+
+enum {
+    // 分风箱主板从站号
+    MB_DEVICE_FFX_ADDR = 1,
+    // 分控一从站号
+    MB_DEVICE_FK_1_ADDR = 2,
+    // 分控二从站号
+    MB_DEVICE_FK_2_ADDR = 3,
+    // 分控三从站号
+    MB_DEVICE_FK_3_ADDR = 4,
+    // 分控四从站号
+    MB_DEVICE_FK_4_ADDR = 5,
+};
+static void *master_handle = NULL;
+
+#define STR(fieldname) ((const char *)( fieldname ))
+
+
+const mb_parameter_descriptor_t device_parameters[] = {
+        // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
+        {CID_POWER,        STR("power"),        STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                POWER_REG_ADDRESS,        1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_MODE,         STR("mode"),         STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                WORK_MOD_REG_ADDRESS,     1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_FAN_MODE,     STR("fan_mode"),     STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                FAN_MODE_REG_ADDRESS,     1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_FAN_SPEED,    STR("fan_speed"),    STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                FAN_LEVEL_REG_ADDRESS,    1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_FAN_1_DEGREE, STR("fan_1_degree"), STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                FAN_VALVE_1_REG_ADDRESS,  1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_FAN_2_DEGREE, STR("fan_2_degree"), STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                FAN_VALVE_2_REG_ADDRESS,  1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_FAN_3_DEGREE, STR("fan_3_degree"), STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                FAN_VALVE_3_REG_ADDRESS,  1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_FAN_4_DEGREE, STR("fan_4_degree"), STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                FAN_VALVE_4_REG_ADDRESS,  1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_FAN_5_DEGREE, STR("fan_5_degree"), STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                FAN_VALVE_5_REG_ADDRESS,  1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_SET_TEMP,     STR("set_temp"),     STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                SET_TEMP_REG_ADDRESS,     1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_VOLTAGE_1,    STR("voltage_1"),    STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                VOLTAGE_1_REG_ADDRESS,    1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_VOLTAGE_2,    STR("voltage_2"),    STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                VOLTAGE_2_REG_ADDRESS,    1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_VOLTAGE_3,    STR("voltage_3"),    STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                VOLTAGE_3_REG_ADDRESS,    1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_VOLTAGE_4,    STR("voltage_4"),    STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                VOLTAGE_4_REG_ADDRESS,    1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_VOLTAGE_5,    STR("voltage_5"),    STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                VOLTAGE_5_REG_ADDRESS,    1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_ENV_TEMP,     STR("env_temp"),     STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                ENV_TEMP_REG_ADDRESS,     1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_ENV_HUMIDITY, STR("env_humidity"), STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                ENV_HUMIDITY_REG_ADDRESS, 1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+        {CID_PM25,         STR("pm25"),         STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                PM25_NUMBER_REG_ADDRESS,  1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ},
+        {CID_CO2,          STR("co2"),          STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                CO2_NUMBER_REG_ADDRESS,   1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ},
+        {CID_INNER_ADDR,          STR("addr"),          STR(""), MB_DEVICE_FFX_ADDR, MB_PARAM_HOLDING,
+                GMV_INTERNAL_NUMBER_REG_ADDRESS,   1, 0, PARAM_TYPE_U16, 2, OPTS(0, 0, 0), PAR_PERMS_READ_WRITE},
+
+};
+
+// Calculate number of parameters in the table
+const uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0]));
+
+
+IRAM_ATTR static esp_err_t master_init() {
+    // Initialize Modbus controller
+    mb_communication_info_t comm = {
+            .ser_opts.port = MB_PORT_NUM,
+            .ser_opts.mode = MB_RTU,
+            .ser_opts.baudrate = MB_DEV_SPEED,
+            .ser_opts.parity = MB_PARITY_NONE,
+            .ser_opts.response_tout_ms = 1000,
+            .ser_opts.data_bits = UART_DATA_8_BITS,
+            .ser_opts.stop_bits = UART_STOP_BITS_1
+    };
+    ESP_LOGI(TAG, "Modbus controller initialized...");
+    esp_err_t err = mbc_master_create_serial(&comm, &master_handle);
+    MB_RETURN_ON_FALSE((master_handle != NULL), ESP_ERR_INVALID_STATE, TAG,
+                       "mb controller initialization fail.");
+    MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
+                       "mb controller initialization fail, returns(0x%x).", (int) err);
+
+    // Set UART pin numbers
+    err = uart_set_pin(MB_PORT_NUM, MB_GPIO_TX, MB_GPIO_RX,
+                       UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
+    MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
+                       "mb serial set pin failure, uart_set_pin() returned (0x%x).", (int) err);
+
+    err = mbc_master_start(master_handle);
+    MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
+                       "mb controller start fail, returned (0x%x).", (int) err);
+
+    // Set driver mode to Half Duplex
+    err = uart_set_mode(MB_PORT_NUM, UART_MODE_UART);
+    MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
+                       "mb serial set mode failure, uart_set_mode() returned (0x%x).", (int) err);
+
+    vTaskDelay(5);
+    err = mbc_master_set_descriptor(master_handle, &device_parameters[0], num_device_parameters);
+    MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
+                       "mb controller set descriptor fail, returns(0x%x).", (int) err);
+    ESP_LOGI(TAG, "Modbus master stack initialized...");
+    return err;
+}
+
+//static void *master_get_param_data(const mb_parameter_descriptor_t *param_descriptor)
+//{
+//    assert(param_descriptor != NULL);
+//    void *instance_ptr = NULL;
+//    if (param_descriptor->param_offset != 0) {
+//        switch(param_descriptor->mb_param_type)
+//        {
+//            case MB_PARAM_HOLDING:
+//                instance_ptr = ((void *)&holding_reg_params + param_descriptor->param_offset - 1);
+//                break;
+//            default:
+//                instance_ptr = NULL;
+//                break;
+//        }
+//    } else {
+//        ESP_LOGE(TAG, "Wrong parameter offset for CID #%u", (unsigned)param_descriptor->cid);
+//        assert(instance_ptr != NULL);
+//    }
+//    return instance_ptr;
+//}
+void modbus_master_task(void *arg) {
+    vTaskDelay(5000 / portTICK_PERIOD_MS);
+    ESP_LOGI(TAG, "Modbus master task started...");
+    esp_err_t err = ESP_OK;
+    uint8_t temp_data[2] = {0}; // temporary buffer to hold maximum CID size
+    uint8_t type = 0;
+    const mb_parameter_descriptor_t *param_descriptor = NULL;
+    for (;;) {
+        mb_param_request_t req = {
+                .slave_addr = 0x05,
+                .reg_size = 10,
+                .reg_start = 0x23,
+                .command = 0x10
+        };
+        uint16_t data[20] = {0, 1 , 2, 3, 4, 5, 6, 7, 8, 9};
+        send_request(&req, &data[0]);
+        err = mbc_master_get_cid_info(master_handle, 55, &param_descriptor);
+        for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < 1; cid++) {
+            err = mbc_master_get_cid_info(master_handle, cid, &param_descriptor);
+            if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
+                err = mbc_master_get_parameter(master_handle, param_descriptor->cid, (uint8_t *) temp_data, &type);
+                if (err == ESP_OK) {
+                    ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %d read successful.",
+                             param_descriptor->cid,
+                             (char *) param_descriptor->param_key,
+                             (char *) param_descriptor->param_units,
+                             *(uint16_t *) temp_data);
+                } else {
+                    ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
+                             param_descriptor->cid,
+                             (char *) param_descriptor->param_key,
+                             (int) err,
+                             (char *) esp_err_to_name(err));
+                }
+            }
+            vTaskDelay(1000 / portTICK_PERIOD_MS);
+        }
+        vTaskDelay(5000 / portTICK_PERIOD_MS);
+    }
+}
+
+ void modbus_master_init() {
+    ESP_LOGI(TAG, "Modbus master init...");
+    ESP_ERROR_CHECK(master_init());
+    vTaskDelay(10);
+    xTaskCreatePinnedToCore(modbus_master_task, "modbus_master_task", 4 * 1024, NULL, 5, NULL, 1);
+}
+
+esp_err_t mm_set_param(uint16_t cid, uint8_t *value) {
+    uint8_t type = PARAM_TYPE_U16;
+    return mbc_master_set_parameter(master_handle, cid, value, &type);
+}
+esp_err_t mm_get_param(uint16_t cid, uint8_t *value) {
+    uint8_t type = PARAM_TYPE_U16;
+    return mbc_master_get_parameter(master_handle, cid, value, &type);
+}
+
+
+esp_err_t send_request(mb_param_request_t *request, void * data_prt) {
+    return mbc_master_send_request(master_handle, request, data_prt);
+}
+
+void modbus_master_destroy() {
+    mbc_master_delete(master_handle);
+}

+ 252 - 0
main/app/sht30.c

@@ -0,0 +1,252 @@
+#include <sys/cdefs.h>
+/**
+ * @Author: 李建
+ * @Date: 2025/4/22 13:48
+ * Description: 温湿度传感器
+ * Copyright: Copyright (©) 2025 永续绿建. All rights reserved.
+ */
+#include <esp_log.h>
+#include <esp_wifi.h>
+#include "include/sht30.h"
+#include "driver/i2c.h"
+#include "main.h"
+#include "lvgl_port.h"
+//#include "ac_controller.h"
+//#include "wifi.h"
+#include "lcd_st7701.h"
+
+static const char *TAG = "SHT30";
+#define DRIFT_RATE 0.1524  // 根据历史数据计算的漂移率 (°C/min)
+#define ACK_CHECK_EN   0x1     /*!< I2C master will check ack from slave*/
+#define ACK_CHECK_DIS  0x0     /*!< I2C master will not check ack from slave */
+#define ACK_VAL    0x0         /*!< I2C ack value */
+#define NACK_VAL   0x1         /*!< I2C nack value */
+int current_seconds;
+sht30_data_t sht30Data = {0};
+
+// 温度修正函数
+static double correct_temperature(double current_temp) {
+    static const int start_seconds = 0;
+    // 计算经过时间(秒)
+    double elapsed_seconds = (double) (current_seconds - start_seconds);
+    // 计算修正量(漂移率 × 经过分钟数)
+    double correction = DRIFT_RATE * (elapsed_seconds / 60.0);
+    // 应用修正
+    return current_temp - correction;
+}
+
+static esp_err_t i2c_master_init(void) {
+    int i2c_master_port = I2C_MASTER_NUM;
+
+    i2c_config_t conf = {
+            .mode = I2C_MODE_MASTER,
+            .sda_io_num = I2C_MASTER_SDA_IO,
+            .scl_io_num = I2C_MASTER_SCL_IO,
+            .sda_pullup_en = GPIO_PULLUP_ENABLE,
+            .scl_pullup_en = GPIO_PULLUP_ENABLE,
+            .master.clk_speed = I2C_MASTER_FREQ_HZ,
+    };
+
+    i2c_param_config(i2c_master_port, &conf);
+
+    return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
+}
+
+esp_err_t
+i2c_master_write_slave_reg(i2c_port_t i2c_num, uint8_t slave_addr, uint8_t reg_addr, uint8_t *data_wr, size_t size,
+                           TickType_t ticks_to_wait) {
+    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+    i2c_master_start(cmd);
+    i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, reg_addr, ACK_CHECK_EN);
+    i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN);
+    i2c_master_stop(cmd);
+    esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, ticks_to_wait);
+    i2c_cmd_link_delete(cmd);
+    return ret;
+}
+
+esp_err_t i2c_master_read_slave_reg_16bit(i2c_port_t i2c_num, uint8_t slave_addr, uint16_t reg_addr, uint8_t *data_rd,
+                                          size_t size, TickType_t ticks_to_wait) {
+    if (size == 0) {
+        return ESP_OK;
+    }
+    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+    i2c_master_start(cmd);
+    i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, reg_addr >> 8, ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, reg_addr, ACK_CHECK_EN);
+    i2c_master_start(cmd);
+    i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_READ, ACK_CHECK_EN);
+    if (size > 1) {
+        i2c_master_read(cmd, data_rd, size - 1, ACK_VAL);
+    }
+    i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL);
+    i2c_master_stop(cmd);
+    esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, ticks_to_wait);
+    i2c_cmd_link_delete(cmd);
+    return ret;
+}
+
+/**
+ * @brief    向SHT3x发送一条指令(16bit)
+ *
+ * @param    cmd —— SHT3x指令(在SHT3x_MODE中枚举定义)
+ *
+ * @retval    成功返回HAL_OK(ESP_OK)
+*/
+static uint8_t SHT3X_Send_Cmd(SHT30_CMD cmd) {
+    uint8_t cmd_buffer[2];
+    cmd_buffer[0] = cmd >> 8;
+    cmd_buffer[1] = cmd;
+    return i2c_master_write_slave_reg(SHT3X_I2C_BUS, SHT3X_SLAVE_ADDRESS, cmd_buffer[0], cmd_buffer + 1, 1,
+                                      SHT3X_TICKS_TO_WAIT);
+}
+
+void sht3x_reset(void) {
+    SHT3X_Send_Cmd(SOFT_RESET_CMD);
+    vTaskDelay(20 / portTICK_PERIOD_MS);
+}
+
+esp_err_t sht3x_init(void) {
+    return SHT3X_Send_Cmd(MEDIUM_2_CMD);
+}
+
+esp_err_t sht3x_read_th_raw_dat(uint8_t *dat) {
+    return i2c_master_read_slave_reg_16bit(SHT3X_I2C_BUS, SHT3X_SLAVE_ADDRESS, READOUT_FOR_PERIODIC_MODE, dat, 6,
+                                           SHT3X_TICKS_TO_WAIT);
+}
+
+#define CRC8_POLYNOMIAL 0x31
+
+static uint8_t SHT3X_CheckCrc8(uint8_t *const message, uint8_t initial_value) {
+    uint8_t remainder;        //余数
+    uint8_t i = 0, j = 0;  //循环变量
+
+    /* 初始化 */
+    remainder = initial_value;
+
+    for (j = 0; j < 2; j++) {
+        remainder ^= message[j];
+
+        /* 从最高位开始依次计算  */
+        for (i = 0; i < 8; i++) {
+            if (remainder & 0x80) {
+                remainder = (remainder << 1) ^ CRC8_POLYNOMIAL;
+            } else {
+                remainder = (remainder << 1);
+            }
+        }
+    }
+
+    /* 返回计算的CRC码 */
+    return remainder;
+}
+
+/**
+ * @brief    将SHT30接收的6个字节数据进行CRC校验,并转换为温度值和湿度值
+ *
+ * @param    dat  —— 存储接收数据的地址(6个字节数组)
+ *
+ * @retval    校验成功  —— 返回0
+ *            校验失败  —— 返回1,并设置温度值和湿度值为0
+*/
+uint8_t sht3x_dat2float(uint8_t *const dat, float *temperature, float *humidity) {
+    uint16_t recv_temperature = 0;
+    uint16_t recv_humidity = 0;
+
+    /* 校验温度数据和湿度数据是否接收正确 */
+    if (SHT3X_CheckCrc8(dat, 0xFF) != dat[2] || SHT3X_CheckCrc8(&dat[3], 0xFF) != dat[5])
+        return 1;
+
+    /* 转换温度数据 */
+    recv_temperature = ((uint16_t) dat[0] << 8) | dat[1];
+    *temperature = -45 + 175 * ((float) recv_temperature / 65535) ;
+
+    /* 转换湿度数据 */
+    recv_humidity = ((uint16_t) dat[3] << 8) | dat[4];
+    *humidity = 100 * ((float) recv_humidity / 65535);
+    return 0;
+}
+
+LV_IMAGE_DECLARE(_wifi_mid_RGB565A8_32x32);
+LV_IMAGE_DECLARE(_wifi_weak_RGB565A8_32x32);
+
+// 刷新wifi图标
+//void ui_wifi_lab_refresh() {
+//    int rssi = 0;
+//    if (get_wifi_status()) {
+//        esp_err_t ret = esp_wifi_sta_get_rssi(&rssi);
+//        ESP_LOGE(TAG, "RSSI:%d", rssi);
+//        if (ret == ESP_OK) {
+//            lvgl_port_lock(-1);
+//            if (rssi >= -50) {
+//                lv_image_set_src(guider_ui.ACPage_img_wifi, &_wifi_stron_RGB565A8_32x32);
+//            } else if ((rssi < -50) && (rssi >= -70)) {
+//                lv_image_set_src(guider_ui.ACPage_img_wifi, &_wifi_mid_RGB565A8_32x32);
+//            } else if (rssi < -70) {
+//                lv_image_set_src(guider_ui.ACPage_img_wifi, &_wifi_weak_RGB565A8_32x32);
+//            }
+//            lvgl_port_unlock();
+//        }
+//
+//    } else {
+//
+//    }
+//}
+
+_Noreturn void i2c_sht3x_task(void *arg) {
+    // 配置I2C0-主机模式,400K,指定 SCL-5,SDA-4
+    //ESP_ERROR_CHECK(i2c_master_init());
+
+    uint8_t recv_dat[6] = {0};
+
+    ESP_LOGI(TAG, "esp32 sht3x project starting ……");
+
+    sht3x_reset(); // 复位SHT3X
+    if (sht3x_init() == ESP_OK) // 初始化SHT3X(周期测量模式)
+        ESP_LOGI(TAG, "sht3x init ok.\n");
+    else
+        ESP_LOGE(TAG, "sht3x init fail.\n");
+    vTaskDelay(1000 / portTICK_PERIOD_MS); //延时1s 等待SHT3X传感器内部采样完成
+    char temp_buf[32];
+    char hum_buf[32];
+    for (;;) {
+        if (sht3x_read_th_raw_dat(recv_dat) == ESP_OK) // 从SHT3X读取一次数据(周期测量模式下)
+        {
+
+            // 将SHT3X接收的6个字节数据进行CRC校验,并转换为温度值和湿度值
+            if (sht3x_dat2float(recv_dat, &sht30Data.temperature, &sht30Data.humidity) == 0) {
+                sht30Data.temperature = correct_temperature(sht30Data.temperature);
+                snprintf(temp_buf, sizeof(temp_buf), "%.1f °C", sht30Data.temperature);
+                snprintf(hum_buf, sizeof(hum_buf), "%.1f %%", sht30Data.humidity);
+                if (guider_ui.screen) {
+                    lvgl_port_lock(-1);
+//                    lv_label_set_text_static(guider_ui.ACPage_lab_env_temp_2, temp_buf);
+//                    lv_label_set_text_static(guider_ui.ACPage_lab_env_hum_2, hum_buf);
+//                    lv_checkbox_set_text(ui->WaterValvePage_cb_1, "555");
+                    lv_label_set_text_static(guider_ui.screen_lab_env_temp, temp_buf);
+                    lv_label_set_text_static(guider_ui.screen_lab_env_hum, hum_buf);
+                    lvgl_port_unlock();
+                }
+            } else {
+                ESP_LOGE(TAG, "crc check fail.\n");
+            }
+        } else {
+            ESP_LOGE(TAG, "read data from sht3x fail.\n");
+        }
+//        if (lcd_st7701_get_blacklight() == false) // 如果当前背光引脚为低电平
+//        {
+//            current_seconds -= 3;
+//            if (current_seconds < 0) current_seconds = 0;
+//        } else
+//            current_seconds += 3;
+//        ui_wifi_lab_refresh();
+        vTaskDelay(5000 / portTICK_PERIOD_MS);
+    }
+
+}
+
+void sht30_init() {
+    xTaskCreate(i2c_sht3x_task, "i2c_sht3x_task", 6 * 1024, NULL, 3, NULL);
+}

+ 3 - 2
main/main.c

@@ -8,6 +8,7 @@
 #include "gui_guider.h"
 #include "beep.h"
 #include "sht30.h"
+#include "modbus_master.h"
 
 lv_ui guider_ui;
 void app_main()
@@ -33,12 +34,12 @@ void app_main()
 //        if (*s_on_off == 1) {
 //            ESP_ERROR_CHECK(lcd_st7701_backlight_off());    // 关闭背光
 //        } else {
-            ESP_ERROR_CHECK(lcd_st7701_backlight_on());
+//            ESP_ERROR_CHECK(lcd_st7701_backlight_on());
 //        }
 //    } else {
 //        ESP_ERROR_CHECK(lcd_st7701_backlight_on());
 //    }
-    setup_ui(&guider_ui);       // 初始化UI
+//    setup_ui(&guider_ui);       // 初始化UI
     //app_wifi_init(on_wifi_connected);
     sht30_init(); // 初始化温湿度传感器