当前位置:首页 > 随笔 > 正文内容

基于openEuler构建嵌入式智能温控系统实战

访客 随笔 2026年6月11日 1

环境准备与验证

系统基础环境检查

首先确认openEuler环境的基本信息:

# 查看系统版本和内核
cat /etc/os-release
uname -r
echo "当前用户:$(whoami)"
echo "工作目录:$(pwd)"

安装开发工具链

# 安装必要的编译和调试工具
sudo dnf update -y
sudo dnf install -y gcc gcc-c++ make cmake gdb
sudo dnf install -y python3 python3-pip

# 验证工具链版本
gcc --version
make --version
cmake --version

实战项目:智能温控系统

系统架构

智能温控系统架构:
├── 传感数据采集
│   ├── 温度采集(模拟)
│   ├── 湿度采集(模拟)
│   └── 数据滤波
├── 控制算法
│   ├── PID控制器
│   ├── 阈值管理
│   └── 状态机
├── 执行器控制
│   ├── 加热器
│   ├── 冷却器
│   └── 风扇
└── 交互界面
    ├── 实时显示
    ├── 参数配置
    └── 日志

第一步:创建项目目录结构

# 建立项目目录
mkdir -p ~/smart_thermostat/{src,include,config,test,logs}
cd ~/smart_thermostat

# 创建说明文件
cat > README.md << 'EOF'
# 智能温控系统 - openEuler嵌入式实战

## 项目描述
基于openEuler的嵌入式智能温控系统模拟,包含传感器采集、PID控制和执行器管理。

## 特性
- 多传感器模拟采集
- PID温度控制算法
- 实时监控
- 日志记录
- 可配置阈值

## 编译运行
make && ./thermostat
EOF

第二步:硬件抽象层实现

创建硬件头文件:

// include/hardware.h
#ifndef HARDWARE_H
#define HARDWARE_H

#include <stdint.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <math.h>

typedef enum {
    DEV_OFF = 0,
    DEV_ON = 1
} dev_state_t;

typedef struct {
    float temp;
    float hum;
    float target;
    uint32_t ts;
} sensor_data_t;

typedef struct {
    dev_state_t heater;
    dev_state_t cooler;
    dev_state_t fan;
    float power;
} actuator_state_t;

typedef struct {
    sensor_data_t sensors;
    actuator_state_t act;
    uint32_t runtime;
    uint8_t mode;
    uint8_t error;
} sys_state_t;

int init_hardware(void);
int read_temp(float *val);
int read_hum(float *val);

int ctrl_heater(dev_state_t st);
int ctrl_cooler(dev_state_t st);
int ctrl_fan(dev_state_t st);
int set_power(float level);

uint32_t get_time(void);
void delay_ms(uint32_t ms);
void show_status(const sys_state_t *st);

#endif

实现硬件抽象层:

// src/hardware.c
#include "hardware.h"

static uint32_t rnd_seed = 0;

static uint32_t rnd_next(void) {
    if (rnd_seed == 0) {
        rnd_seed = (uint32_t)time(NULL);
    }
    rnd_seed = (rnd_seed * 1103515245 + 12345) & 0x7FFFFFFF;
    return rnd_seed;
}

static float drift_temp = 22.0;

int init_hardware(void) {
    puts("=== 硬件初始化 ===");
    puts("温度传感器: 就绪");
    puts("湿度传感器: 就绪");
    puts("执行器: 就绪");
    puts("===================");
    
    rnd_seed = (uint32_t)time(NULL);
    drift_temp = 22.0 + (rnd_next() % 100) / 100.0;
    return 0;
}

int read_temp(float *val) {
    if (!val) return -1;
    
    float noise = (rnd_next() % 200 - 100) / 1000.0;
    drift_temp += noise;
    
    if (drift_temp < 15.0) drift_temp = 15.0;
    if (drift_temp > 35.0) drift_temp = 35.0;
    
    *val = drift_temp;
    return 0;
}

int read_hum(float *val) {
    if (!val) return -1;
    
    float base = 60.0;
    float var = (rnd_next() % 300 - 150) / 100.0;
    *val = base + var;
    
    if (*val < 45.0) *val = 45.0;
    if (*val > 75.0) *val = 75.0;
    
    return 0;
}

static actuator_state_t g_act = {
    .heater = DEV_OFF,
    .cooler = DEV_OFF,
    .fan = DEV_OFF,
    .power = 0.0
};

int ctrl_heater(dev_state_t st) {
    g_act.heater = st;
    if (st == DEV_ON) {
        drift_temp += 0.05 * g_act.power;
    }
    printf("[执行器] 加热器: %s\n", st == DEV_ON ? "开" : "关");
    return 0;
}

int ctrl_cooler(dev_state_t st) {
    g_act.cooler = st;
    if (st == DEV_ON) {
        drift_temp -= 0.05 * g_act.power;
    }
    printf("[执行器] 冷却器: %s\n", st == DEV_ON ? "开" : "关");
    return 0;
}

int ctrl_fan(dev_state_t st) {
    g_act.fan = st;
    printf("[执行器] 风扇: %s\n", st == DEV_ON ? "开" : "关");
    return 0;
}

int set_power(float level) {
    if (level < 0.0) level = 0.0;
    if (level > 1.0) level = 1.0;
    g_act.power = level;
    printf("[执行器] 功率: %.1f%%\n", level * 100.0);
    return 0;
}

uint32_t get_time(void) {
    return (uint32_t)time(NULL);
}

void delay_ms(uint32_t ms) {
    usleep(ms * 1000);
}

void show_status(const sys_state_t *st) {
    if (!st) return;
    
    printf("\n");
    puts("┌──────────────────────────────────────┐");
    puts("│         温控系统状态                  │");
    puts("├──────────────────────────────────────┤");
    printf("│ 温度: %5.1f°C   目标: %5.1f°C   │\n",
           st->sensors.temp, st->sensors.target);
    printf("│ 湿度: %5.1f%%    运行: %5u秒    │\n",
           st->sensors.hum, st->runtime);
    printf("│ 加热: %-4s  冷却: %-4s         │\n",
           st->act.heater ? "开" : "关",
           st->act.cooler ? "开" : "关");
    printf("│ 风扇: %-6s 功率: %4.1f%%        │\n",
           st->act.fan ? "开" : "关",
           st->act.power * 100.0);
    printf("│ 模式: %-8s 状态: %-10s │\n",
           st->mode == 0 ? "自动" : "手动",
           st->error == 0 ? "正常" : "异常");
    puts("└──────────────────────────────────────┘");
}

第三步:PID控制算法实现

// src/pid_ctrl.c
#include "hardware.h"

typedef struct {
    float kp;
    float ki;
    float kd;
    
    float setpoint;
    float integral;
    float prev_err;
    
    float out_min;
    float out_max;
    
    uint32_t last_ts;
} pid_ctrl_t;

void pid_setup(pid_ctrl_t *pid, float kp, float ki, float kd, float sp) {
    pid->kp = kp;
    pid->ki = ki;
    pid->kd = kd;
    pid->setpoint = sp;
    pid->integral = 0.0;
    pid->prev_err = 0.0;
    pid->out_min = 0.0;
    pid->out_max = 1.0;
    pid->last_ts = get_time();
}

float pid_calc(pid_ctrl_t *pid, float input) {
    uint32_t now = get_time();
    float dt = (float)(now - pid->last_ts);
    
    if (dt <= 0) return 0.0;
    
    float err = pid->setpoint - input;
    
    float p_term = pid->kp * err;
    
    pid->integral += err * dt;
    float i_limit = 100.0;
    if (pid->integral > i_limit) pid->integral = i_limit;
    if (pid->integral < -i_limit) pid->integral = -i_limit;
    
    float i_term = pid->ki * pid->integral;
    
    float d_term = pid->kd * (err - pid->prev_err) / dt;
    
    float output = p_term + i_term + d_term;
    
    if (output > pid->out_max) output = pid->out_max;
    if (output < pid->out_min) output = pid->out_min;
    
    pid->prev_err = err;
    pid->last_ts = now;
    
    printf("[PID] 误差: %.2f, 输出: %.2f (P:%.2f I:%.2f D:%.2f)\n",
           err, output, p_term, i_term, d_term);
    
    return output;
}

第四步:主控系统实现

// src/thermostat.c
#include "hardware.h"

typedef struct {
    float target_temp;
    float tolerance;
    float max_heat_pwr;
    float max_cool_pwr;
    uint32_t interval_ms;
} sys_config_t;

static sys_state_t g_state = {0};
static pid_ctrl_t g_pid = {0};
static sys_config_t g_cfg = {
    .target_temp = 24.0,
    .tolerance = 0.5,
    .max_heat_pwr = 0.8,
    .max_cool_pwr = 0.6,
    .interval_ms = 2000
};

int sys_boot(void) {
    puts("初始化温控系统...");
    
    if (init_hardware() != 0) {
        fprintf(stderr, "硬件初始化失败\n");
        return -1;
    }
    
    pid_setup(&g_pid, 2.0, 0.1, 0.5, g_cfg.target_temp);
    g_pid.out_min = -g_cfg.max_cool_pwr;
    g_pid.out_max = g_cfg.max_heat_pwr;
    
    g_state.sensors.target = g_cfg.target_temp;
    g_state.mode = 0;
    g_state.error = 0;
    
    puts("初始化完成");
    return 0;
}

void temp_ctrl_loop(void) {
    float temp, hum;
    
    if (read_temp(&temp) != 0) {
        fprintf(stderr, "温度读取失败\n");
        return;
    }
    
    if (read_hum(&hum) != 0) {
        fprintf(stderr, "湿度读取失败\n");
        return;
    }
    
    g_state.sensors.temp = temp;
    g_state.sensors.hum = hum;
    g_state.runtime = get_time();
    
    float output = pid_calc(&g_pid, temp);
    
    if (output > 0) {
        ctrl_heater(DEV_ON);
        ctrl_cooler(DEV_OFF);
        set_power(output);
    } else if (output < 0) {
        ctrl_heater(DEV_OFF);
        ctrl_cooler(DEV_ON);
        set_power(-output);
    } else {
        ctrl_heater(DEV_OFF);
        ctrl_cooler(DEV_OFF);
        set_power(0.0);
    }
    
    float diff = temp - g_cfg.target_temp;
    if (fabs(diff) > 2.0) {
        ctrl_fan(DEV_ON);
    } else {
        ctrl_fan(DEV_OFF);
    }
    
    g_state.act.heater = (output > 0) ? DEV_ON : DEV_OFF;
    g_state.act.cooler = (output < 0) ? DEV_ON : DEV_OFF;
    g_state.act.power = fabs(output);
}

void safety_check(void) {
    if (g_state.sensors.temp > 40.0) {
        puts("[安全] 温度超限! 关闭加热器");
        ctrl_heater(DEV_OFF);
        ctrl_fan(DEV_ON);
        g_state.error = 1;
    }
    
    if (g_state.sensors.temp < 10.0) {
        puts("[安全] 温度过低!");
        g_state.error = 2;
    }
    
    if (g_state.runtime > 3600) {
        puts("[监控] 系统已运行1小时");
    }
}

int main(void) {
    puts("=== openEuler温控系统启动 ===");
    
    if (sys_boot() != 0) return -1;
    
    printf("目标温度: %.1f°C\n", g_cfg.target_temp);
    printf("控制间隔: %u ms\n", g_cfg.interval_ms);
    puts("按Ctrl+C停止\n");
    
    uint32_t cycles = 0;
    
    while (1) {
        printf("\n--- 控制周期 #%u ---\n", ++cycles);
        
        temp_ctrl_loop();
        safety_check();
        show_status(&g_state);
        
        if (cycles % 10 == 0) {
            printf("\n[统计] 周期: %u, 温度: %.1f°C, 目标: %.1f°C\n",
                   cycles, g_state.sensors.temp, g_cfg.target_temp);
        }
        
        delay_ms(g_cfg.interval_ms);
    }
    
    return 0;
}

第五步:构建配置

# Makefile
cat > Makefile << 'EOF'
CC = gcc
CFLAGS = -Wall -Wextra -std=c99 -O2 -I./include
LDFLAGS = -lm
TARGET = thermostat
SRC = src/hardware.c src/pid_ctrl.c src/thermostat.c
OBJ = $(SRC:.c=.o)

.PHONY: all clean debug run

all: $(TARGET)

$(TARGET): $(OBJ)
	$(CC) $(LDFLAGS) -o $@ $^
	@echo "=== 编译完成: $(TARGET) ==="

%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

debug: CFLAGS += -g -DDEBUG
debug: $(TARGET)

clean:
	rm -f $(OBJ) $(TARGET)
	@echo "=== 清理 ==="

run: $(TARGET)
	./$(TARGET)

install: $(TARGET)
	cp $(TARGET) /usr/local/bin/
	@echo "=== 安装 ==="

test: $(TARGET)
	@echo "=== 测试 (5个周期) ==="
	timeout 10s ./$(TARGET) || true

info:
	@echo "项目: 智能温控系统"
	@echo "目标: $(TARGET)"
	@echo "源码: $(SRC)"
	@echo "编译器: $(CC)"
	@echo "选项: $(CFLAGS)"
EOF

第六步:编译与运行

make clean
make

file thermostat
ls -lh thermostat

./thermostat

编译输出示例:

=== 编译完成: thermostat ===
thermostat: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, with debug_info, not stripped
-rwxr-xr-x 1 root root 28K 当前时间 thermostat

运行输出片段:

=== openEuler温控系统启动 ===
初始化温控系统...
=== 硬件初始化 ===
温度传感器: 就绪
湿度传感器: 就绪
执行器: 就绪
===================
初始化完成
目标温度: 24.0°C
控制间隔: 2000 ms

--- 控制周期 #1 ---
[PID] 误差: 1.85, 输出: 0.80 (P:3.70 I:0.00 D:0.00)
[执行器] 加热器: 开
[执行器] 冷却器: 关
[执行器] 功率: 80.0%
[执行器] 风扇: 开

┌──────────────────────────────────────┐
│         温控系统状态                  │
├──────────────────────────────────────┤
│ 温度:  22.2°C   目标:  24.0°C      │
│ 湿度:  61.5%    运行: 1730000000秒   │
│ 加热: 开     冷却: 关              │
│ 风扇: 开    功率: 80.0%            │
│ 模式: 自动    状态: 正常            │
└──────────────────────────────────────┘

--- 控制周期 #2 ---
[PID] 误差: 1.45, 输出: 0.80 (P:2.90 I:0.29 D:-0.39)
[执行器] 加热器: 开
[执行器] 冷却器: 关
[执行器] 功率: 80.0%
[执行器] 风扇: 开
...

如果对openEuler感兴趣,可访问 DistroWatch openEuler页面openEuler官网 获取更多信息。

相关文章

可以按小时收费的VPS

很多 VPS 提供商都支持 按小时计费(hourly billing),想短期试用 / 临时搭建节点、测试网络、短期项目等场景非常合适。下面是当前最主流且靠谱的按小时 VPS 选项,分别按不同需求场景整理: 1. Vultr(全球节点,包括日本) 按小时计费 可选机房:东京 / 大阪 / 洛杉矶 / 法兰克福 / 伦敦 … 支持 PayPal(部分情况),但更常用信用卡/PayPal+卡价格参考$...

在 iPhone 上下载国外App

地区/国家限制App Store 会根据 Apple ID 的国家或地区限制应用下载。如果你的 Apple ID 绑定的是中国大陆,就可能无法下载 OpenAI 官方的 ChatGPT 应用,因为它在大陆 App Store 不上架。解决办法:换成美国、加拿大、香港等地区的 Apple ID。或者在现有 Apple ID 上更改地区。注册一个国外 Apple ID(推荐)比如注册 美国区 Appl...

Node.js 中的异步编程:回调与 Promise

Node.js 是一个基于 JavaScript 构建的单线程、非阻塞运行环境,它通过异步编程机制来高效处理多个操作。在执行如文件读取、API 请求或数据库查询等任务时,Node.js 不会等待这些操作完成,而是使用回调函数和 Promise 来避免阻塞主线程。 回调方式实现异步 那么当异步操作完成后,Node.js 如何知道接下来要做什么呢?这就要用到 回调函数(callback)。 回调本质上...

Selenium自动化测试入门指南

Selenium自动化测试入门指南

什么是自动化测试? 自动化测试是指利用软件工具自动执行测试用例,模拟用户操作,如打开网页、点击链接、输入文本等,并验证结果是否符合预期。 其主要优点包括: 大幅减少人工成本 测试速度快 可以在非工作时间运行 支持持续集成和交付 然而,它也存在一些局限性,例如开发成本较高、不适合快速变化的项目、依赖稳定的UI界面等。 自动化测试的应用条件 适合引入自动化测试的情况包括: 手动测试耗时且需要大量...

MariaDB Galera集群故障快速恢复指南

OpenStack控制节点采用三节点MariaDB Galera集群架构。当数据库集群因故障重启时,有时会出现Galera集群无法正常启动的问题。虽然有多种方法可以恢复数据库服务,但如何实现快速启动同时确保数据完整性呢? 通过分析日志发现,MariaDB Galera集群节点宕机时会在日志中输出以下信息: [Note] WSREP: 新集群视图:全局状态: 874d8e7e-5980-11e8-8...

Android 中 EventBus 的通信机制与实现原理深度解析

EventBus 核心设计思想 EventBus 是一个基于观察者模式的事件总线框架,广泛应用于 Android 平台以实现组件解耦。它通过中心化的消息分发机制,使不同层级、不同线程的对象能够以"发布-订阅"方式通信,避免了传统接口回调或广播带来的强依赖问题。 核心角色说明 事件(Event):任意 Java 对象,作为数据载体,如网络状态变更通知、用户登录信息等。 发布者(Publi...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。