项目:工业 HVAC 控制器

工业级控制器:HMI + 实时控制 + 多协议通信。

一、目标

二、硬件

模块 功能
主控 i.MX 8M / RK3568
屏幕 7" 触摸屏(LVGL)
通信 RS485 (Modbus RTU)
温湿度 I2C 传感器
继电器输出 控制风机、阀门

三、软件架构

┌─────────────────────────────────────┐
│   HMI 应用(LVGL)                  │
├─────────────────────────────────────┤
│   控制逻辑(PID + 状态机)          │
├─────────────────────────────────────┤
│   通信层(Modbus/MQTT)              │
├─────────────────────────────────────┤
│   驱动(I2C/GPIO/RS485)            │
└─────────────────────────────────────┘

四、状态机

typedef enum {
    STATE_OFF,
    STATE_COOLING,
    STATE_HEATING,
    STATE_FAN_ONLY,
    STATE_ALARM,
    STATE_MAINTENANCE,
} hvac_state_t;

static hvac_state_t current_state = STATE_OFF;

void state_machine_update(void)
{
    float t = read_temperature();
    float setpoint = config.setpoint;

    switch (current_state) {
    case STATE_OFF:
        if (t > setpoint + 1.0) current_state = STATE_COOLING;
        if (t < setpoint - 1.0) current_state = STATE_HEATING;
        break;
    case STATE_COOLING:
        if (t < setpoint - 0.5) current_state = STATE_FAN_ONLY;
        if (sensor_fault()) current_state = STATE_ALARM;
        break;
    case STATE_HEATING:
        if (t > setpoint + 0.5) current_state = STATE_FAN_ONLY;
        if (sensor_fault()) current_state = STATE_ALARM;
        break;
    case STATE_ALARM:
        // 等人工确认
        break;
    }
}

五、PID 控制

typedef struct {
    float kp, ki, kd;
    float integral;
    float prev_error;
    float output_min, output_max;
} pid_t;

void pid_init(pid_t *pid, float kp, float ki, float kd,
              float min, float max)
{
    pid->kp = kp; pid->ki = ki; pid->kd = kd;
    pid->integral = 0;
    pid->prev_error = 0;
    pid->output_min = min;
    pid->output_max = max;
}

float pid_update(pid_t *pid, float setpoint, float measured, float dt)
{
    float error = setpoint - measured;
    pid->integral += error * dt;
    float derivative = (error - pid->prev_error) / dt;
    pid->prev_error = error;

    float output = pid->kp * error +
                   pid->ki * pid->integral +
                   pid->kd * derivative;

    if (output > pid->output_max) output = pid->output_max;
    if (output < pid->output_min) output = pid->output_min;
    return output;
}

六、Modbus Slave(作为被控设备)

// 保持寄存器(被云端读写)
uint16_t holding_regs[100] = { 0 };
// 输入寄存器(只读)
uint16_t input_regs[100] = { 0 };

int modbus_slave()
{
    modbus_t *ctx = modbus_new_rtu("/dev/ttymxc0", 9600, 'N', 8, 1);
    modbus_set_slave(ctx, 1);

    // 启动 Modbus TCP server
    modbus_t *tcp = modbus_new_tcp("0.0.0.0", 502);
    modbus_set_slave(tcp, 1);
    modbus_connect(tcp);

    while (running) {
        uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
        int rc = modbus_receive(tcp, query);
        if (rc > 0) {
            // 写入云端 / 从本地寄存器
            modbus_reply(tcp, query, rc, modbus_mapping);
        }
    }
}

七、HMI 显示(LVGL)

void ui_create_main_screen(void)
{
    lv_obj_t *scr = lv_obj_create(NULL);

    // 温度显示
    lv_obj_t *temp_label = lv_label_create(scr);
    lv_label_set_text(temp_label, "23.5 °C");
    lv_obj_set_style_text_font(temp_label, &lv_font_montserrat_48, 0);
    lv_obj_align(temp_label, LV_ALIGN_CENTER, 0, -50);

    // 模式按钮
    lv_obj_t *btn = lv_button_create(scr);
    lv_obj_align(btn, LV_ALIGN_CENTER, 0, 50);
    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);

    // 图表
    lv_obj_t *chart = lv_chart_create(scr);
    lv_chart_set_type(chart, LV_CHART_TYPE_LINE);
    lv_chart_series_t *s = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
    lv_obj_set_size(chart, 400, 200);
    lv_obj_align(chart, LV_ALIGN_BOTTOM_MID, 0, -20);
}

void ui_update_temperature(float t)
{
    lv_label_set_text_fmt(temp_label, "%.1f °C", t);
    // 滚动更新 chart
}

八、报警与日志

typedef enum {
    ALARM_NONE,
    ALARM_HIGH_TEMP,
    ALARM_LOW_TEMP,
    ALARM_SENSOR_FAIL,
    ALARM_COMM_FAIL,
} alarm_t;

void alarm_update(void)
{
    static alarm_t prev = ALARM_NONE;
    alarm_t cur = ALARM_NONE;

    float t = read_temperature();
    if (t > 35.0) cur = ALARM_HIGH_TEMP;
    else if (t < 5.0) cur = ALARM_LOW_TEMP;
    else if (sensor_fault()) cur = ALARM_SENSOR_FAIL;

    if (cur != ALARM_NONE && cur != prev) {
        log_alarm(cur);
        send_mqtt_alarm(cur);
        show_ui_alarm(cur);
    }
    prev = cur;
}

九、远程监控(MQTT)

// 上报周期
void publish_telemetry(void)
{
    cJSON *root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "id", config.device_id);
    cJSON_AddNumberToObject(root, "temp", read_temperature());
    cJSON_AddNumberToObject(root, "humidity", read_humidity());
    cJSON_AddStringToObject(root, "state", state_to_string(current_state));
    cJSON_AddNumberToObject(root, "alarm", current_alarm);
    char *json = cJSON_PrintUnformatted(root);
    mqtt_publish("hvac/telemetry", json, strlen(json));
    cJSON_Delete(root);
    free(json);
}

十、安全

十一、实时性

// 用 SCHED_FIFO
struct sched_param sp = { .sched_priority = 80 };
pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp);
mlockall(MCL_CURRENT|MCL_FUTURE);