ARM Linux 嵌入式温度监测:从设备树采集到 Qt 可视化与自启动

ARM Linux 嵌入式温度监测:从设备树采集到 Qt 可视化与自启动 简介围绕ARM Linux平台的嵌入式温度监测系统设计这份PDF文献面向电子信息、嵌入式方向的学生与工程技术人员。内容以ARM9微处理器S3C2410A为核心运行嵌入式Linux串联温度采集、GPRS无线通信与远程监控中心重点剖析单总线DS18B20经GPIO采集温度的实现。硬件部分涵盖S3C2410外围电路、RS232串口、RJ45网络接口及存储配置并给出主控制器端口驱动能力不足时的驱动电路改进方案软件部分讨论Linux下应用程序、数据处理与通信流程以及单总线协议在嵌入式Linux中的落地方法。资源为1个PDF文件压缩包约268KB适合作为课程设计、毕业设计及中规模温度监测项目的方案参考便于快速理解系统结构、硬件选型与软件分层。已有463人学习对搭建ARM Linux测温原型、撰写论文或补充驱动细节具有借鉴价值。1. 从一颗 ARM 芯片到一条温度曲线嵌入式温度监测系统到底在做什么机柜里的边缘计算盒子连续跑了四十分钟进风口温度从 28 度爬到 51 度值班的人第二天早上翻日志才发现。基于 ARM Linux 的嵌入式温度监测系统要解决的就是这类问题一颗 ARM 处理器、一颗数字温度传感器、一套 Linux 用户态采集程序把温度这个模拟物理量变成可查询、可告警、可回溯的数字曲线。它不追求实验室里千分之一度的精度追求的是长时间不断电、断网能缓存、重启能自恢复。这件事听起来简单真做起来会同时踩到硬件接口、内核设备树、交叉编译、用户态调度、数据持久化和上位机可视化六层。适合两类人读一类是做工业控制柜、储能机柜、边缘计算盒子的工程师需要一份能直接抄的落地路径另一类是刚学嵌入式 Linux想找一个同时练到设备树、字符设备、交叉编译和 Qt 上位机的小项目练手的人。下面按硬件到内核、内核到用户态、用户态到可视化、最后到校准与长跑的顺序拆开讲。2. ARM Linux 温度采集的硬件选型与内核对接触点选型决定后面所有代码的形态。用 1-Wire 的 DS18B20 还是 I2C 的 LM75、TMP102、SHT30直接决定了内核里走的是 w1-gpio 子系统还是 i2c-dev也决定了用户态是读/sys/bus/w1/devices/还是读/sys/bus/i2c/devices/。板子这边Cortex-A7 级别的 i.MX6ULL、STM32MP157 足够跑采集加轻量可视化Cortex-A53/A57 的 RK3568、AXU15EG 这类开发板更适合再叠 Qt 界面。2.1 ARM 开发板与温度传感器的选型对照实际项目里最常纠结的是接口形式和布线成本。DS18B20 单总线能挂多颗、线可以拉几米适合一个机柜分几个点位I2C 传感器便宜、精度稳定但总线电容限制在 400pF 左右超过就得加缓冲。下面这张表是我自己选型时会对着填的。维度DS18B20 (1-Wire)LM75/TMP102 (I2C)SHT30 (I2C)接口占用1 个 GPIOSDASCLSDASCL典型精度±0.5°C±2°C / ±0.5°C±0.3°C多点能力单总线可挂多颗靠地址区分一般 8 个以内靠地址区分内核支持w1-gpio w1-thermlm75 / tmp102 驱动常需自写或走 i2c-dev适合场景分散点位、长线板载、密集测温高精度、带湿度如果只是验证链路能不能通我一般先上 DS18B20因为它对时序容忍度高w1-therm驱动是内核自带的不用写一行驱动代码就能在 sysfs 里看到温度。等到要量产、要精度再换 I2C 方案。2.2 从设备树节点到 sysfsI2C 传感器怎么接进内核以 TMP102 接在 i2c1 上、地址 0x48 为例设备树里要干的事就三件把 i2c1 的 status 打开、把 pinmux 配成 I2C 功能、把传感器节点挂上去。i2c1 { clock-frequency 100000; /* 标准模式 100kHz长线可降到 50kHz */ pinctrl-names default; pinctrl-0 pinctrl_i2c1; status okay; tmp102: temperature-sensor48 { compatible ti,tmp102; reg 0x48; /* 7 位地址写成 8 位会 probe 失败 */ /* 可选把 ALERT 脚接 GPIO用于硬件过温中断 */ }; };改完make dtbs重新烧写开机后dmesg | grep tmp102应该能看到驱动 probe 成功的日志。逻辑说明compatible是驱动匹配的唯一钥匙写错一个字符内核就当没有这个设备reg是 I2C 从机地址很多新手把读写地址0x90当成reg填进去结果 probe 一直失败。参数上clock-frequency在总线超过 30cm 或挂了 4 个以上从机时从 400kHz 降到 100kHz 甚至 50kHz能明显减少 ACK 丢失。验证通路是否真的通了不用写程序先看 sysfs# 确认设备被内核识别 ls /sys/bus/i2c/devices/1-0048/ # 读一次原始温度单位是毫摄氏度需要除以 1000 cat /sys/bus/i2c/devices/1-0048/hwmon/hwmon0/temp1_input # 每 2 秒采一次观察是否有跳变 watch -n 2 cat /sys/bus/i2c/devices/1-0048/hwmon/hwmon0/temp1_input看到 30500 这样的数字说明从硬件到内核这一层已经打通了。如果hwmon目录不存在先查dmesg里有没有 I2C 超时或者 NACK再确认总线上的上拉电阻是不是没焊。2.3 交叉编译工具链与采集程序在板端的部署验证ARM Linux 项目几乎躲不开交叉编译。宿主机上装好工具链之后先确认版本和 sysroot 能对上目标板的 libc。# 以 arm-linux-gnueabihf 为例先验证工具链可用 arm-linux-gnueabihf-gcc -v # 编译一个最简单的温度读取程序静态链接避免 libc 版本不匹配 arm-linux-gnueabihf-gcc -static -O2 -o temp_read temp_read.c # 用 file 确认产物架构是 ARM 而不是 x86 file temp_read # scp 到板子上执行 scp temp_read root192.168.1.50:/usr/local/bin/关键在-static。动态链接时要保证板子上的 glibc 版本不低于交叉工具链的版本否则会报GLIBC_2.xx not found。产品阶段为了体积和升级方便通常会换成动态链接加一个自带 sysroot 的 rootfs但调试期静态链接能省掉大量环境排查时间。3. 温度采集程序从 sysfs 读取到数据落盘的完整实现内核层通了之后用户态要解决三件事怎么稳定地读、怎么把原始值变成可用温度、怎么把数据存下来不丢。顺序不能反先有稳定的读再谈换算和存储。3.1 读取温度传感器的最小 C 程序读 sysfs 本质就是open加read但温度传感器有个坑一次 read 可能只返回部分内容必须循环读到 EOF否则会读到残缺的字符串。#include stdio.h #include stdlib.h #include fcntl.h #include unistd.h #include string.h #define SENSOR_PATH /sys/bus/i2c/devices/1-0048/hwmon/hwmon0/temp1_input /* 读 sysfs 温度返回摄氏度浮点值失败返回 -1000 */ double read_temperature(const char *path) { char buf[32] {0}; int fd open(path, O_RDONLY); if (fd 0) { perror(open sensor); return -1000.0; } ssize_t total 0, n 0; /* 循环读防止单次 read 只返回一部分 */ while (total (ssize_t)sizeof(buf) - 1 (n read(fd, buf total, sizeof(buf) - 1 - total)) 0) { total n; } close(fd); if (total 0) return -1000.0; long milli strtol(buf, NULL, 10); /* hwmon 单位是毫摄氏度 */ return milli / 1000.0; } int main(void) { double t read_temperature(SENSOR_PATH); if (t -100.0) { fprintf(stderr, read failed\n); return 1; } printf(%.3f\n, t); return 0; }逻辑说明strtol比atof更适合 sysfs因为 sysfs 返回的是纯整数毫摄氏度省掉浮点解析的边界问题。返回值-1000.0是错误哨兵正常温度不可能低于它调用方用t -100.0判断即可。参数上buf开 32 字节足够hwmon 一般返回 6 到 8 个字符。3.2 温度换算、滑动平均与过温阈值判断单次采样会有噪声尤其是 I2C 走长线的时候。直接拿单点做告警容易误报工程上一般做滑动平均。下面的实现用一个固定窗口的环形缓冲避免动态内存。#define WIN 8 static double win[WIN]; static int idx 0, cnt 0; /* 把新样本塞进窗口返回窗口内的平均值 */ double moving_average(double sample) { win[idx] sample; idx (idx 1) % WIN; if (cnt WIN) cnt; double sum 0; for (int i 0; i cnt; i) sum win[i]; return sum / cnt; } /* 告警等级0 正常1 预警2 严重 */ int judge(double avg) { if (avg 75.0) return 2; /* 严重立即上报并触发风扇全速 */ if (avg 60.0) return 1; /* 预警记录并提示 */ return 0; }逻辑说明窗口取 8 是经验值采样周期 1 秒时相当于 8 秒平滑既能压掉单点毛刺又不会把真实的快速升温拖得太钝。阈值 60 和 75 只是示例实际要按器件手册的结温上限反推留 15 到 20 度余量。如果做多点采集每个点位要各维护一套窗口状态不能共用。3.3 数据落盘SQLite 还是 CSV 滚动文件存储方案要看数据量和查询需求。每 1 秒一条、跑 30 天大约 260 万条记录用 SQLite 完全扛得住如果只是最近 24 小时回看滚动 CSV 更省事。下面这张表是两种方案在嵌入式环境下的实际差别。维度SQLiteCSV 滚动文件写入开销有事务需 WAL 模式追加写开销最小断电安全WAL 模式下基本不丢可能丢最后一行查询能力支持按时间、阈值筛选只能顺序扫体积占用单文件可定期 vacuum按天切分需自己清理适合规模百万级、需回溯十万级以内、只看近期用 SQLite 时表和索引建好写入用 WAL 模式避免频繁 fsync 把 SD 卡写坏。PRAGMA journal_mode WAL; -- 断电安全写入更快 CREATE TABLE IF NOT EXISTS temp_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, ts INTEGER NOT NULL, -- Unix 时间戳秒 sensor TEXT NOT NULL, -- 传感器标识多点时区分 temp_c REAL NOT NULL, level INTEGER NOT NULL DEFAULT 0 ); CREATE INDEX IF NOT EXISTS idx_ts ON temp_log(ts);索引idx_ts是为了按时间范围查询没有它30 天的数据查一次要全表扫。写入时批量 100 条提交一次事务比一条一提交快一个数量级。另外要设一个保留策略比如只保留最近 90 天用定时任务删旧数据并执行PRAGMA incremental_vacuum。4. 可视化与自启动把数据送到 Qt5 界面和浏览器数据存下来只是第一步现场的人要看曲线调度的人要收告警。ARM 板资源有限可视化要按场景选方案带屏幕的用 Qt纯远程的用轻量 HTTP 接口。4.1 Qt5.5.10 在 ARM 板上的交叉编译要点Qt5.5.10 是个被大量嵌入式项目沿用的版本稳定但对交叉编译环境挑剔。核心是先把宿主机上的 Qt 编成目标板能跑的版本再编应用。配置阶段几个必须显式指定的参数# 配置 Qt5.5.10关键在于 -platform 和 -xplatform 的分工 ./configure \ -prefix /opt/qt5.5.10-arm \ -opensource -confirm-license \ -release -shared \ -platform linux-g \ # 宿主机编译工具 -xplatform linux-arm-gnueabi-g \ # 目标板编译工具 -qt-freetype -qt-libpng -qt-zlib \ -no-opengl -no-xcb \ # 无 X11 时用 linuxfb -qt-libjpeg -no-pch \ -nomake examples -nomake tests make -j8 make install逻辑说明-no-xcb配-platform linuxfb是嵌入式无桌面环境的常态直接往 framebuffer 写。-no-opengl看板子有没有 GPU有 Mali 或 Vivante 就打开 EGLFS帧率差别很大。参数上-prefix指向的目录要原样拷贝到板子上应用运行时用-platform linuxfb启动或者设QT_QPA_PLATFORMlinuxfb。界面本身建议只做三件事显示当前温度、画最近 1 小时曲线、在阈值越界时变色。Qt Charts 在 5.5 时代还比较重曲线可以用QPainter自己画折线数据点控制在 360 个以内一帧刷新不超过 5ms。4.2 用轻量 HTTP 接口把温度曲线推给浏览器没有屏幕的板子起一个几十 KB 的 HTTP 服务暴露 JSON 就够了。用 Python 起一个最小服务只需要几十行借助http.server即可。# temp_http.py暴露 /latest 和 /history 两个接口 from http.server import BaseHTTPRequestHandler, HTTPServer import sqlite3, json, time DB /var/lib/tempmon/temp.db class Handler(BaseHTTPRequestHandler): def do_GET(self): conn sqlite3.connect(DB) cur conn.cursor() if self.path /latest: cur.execute(SELECT ts, temp_c, level FROM temp_log ORDER BY ts DESC LIMIT 1) row cur.fetchone() body json.dumps({ts: row[0], temp: row[1], level: row[2]}) elif self.path /history: since int(time.time()) - 3600 # 最近 1 小时 cur.execute(SELECT ts, temp_c FROM temp_log WHERE ts ? ORDER BY ts, (since,)) body json.dumps(cur.fetchall()) else: self.send_error(404) return conn.close() data body.encode() self.send_response(200) self.send_header(Content-Type, application/json) self.send_header(Content-Length, str(len(data))) self.end_headers() self.wfile.write(data) HTTPServer((0.0.0.0, 8080), Handler).serve_forever()逻辑说明/latest给告警脚本轮询/history给前端画曲线。数据全部来自 SQLite采集程序和 HTTP 服务共享同一个库文件靠 WAL 模式解决并发读写。参数上since用相对时间而不是绝对秒数前端不用关心时区。注意conn要记得关否则长时间跑会耗尽文件描述符。4.3 systemd 自启动与看门狗守护采集、HTTP、Qt 三类进程都要开机自起还要在崩溃后自动拉起。systemd 的 unit 是标准做法。# /etc/systemd/system/tempmon.service [Unit] DescriptionTemperature Monitor Collector Afternetwork.target [Service] Typesimple ExecStart/usr/local/bin/temp_collect Restartalways RestartSec3 WatchdogSec30 # 30 秒喂一次狗超时重启 EnvironmentLD_LIBRARY_PATH/opt/qt5.5.10-arm/lib [Install] WantedBymulti-user.target逻辑说明Restartalways加RestartSec3是防崩溃的底线WatchdogSec30要求进程每 30 秒调用一次sd_notify(0, WATCHDOG1)如果因为死锁卡住systemd 会主动杀掉重启。采集程序里加喂狗只需要在采样循环末尾调用一次几行代码的事。参数上WatchdogSec要大于单次采样周期的 5 倍以上否则正常抖动也会被误判。5. 温度精度校准与长期运行的可观测性技巧传感器出厂有偏差PCB 走线有自热机箱内部还有热源干扰。想拿到可信的温度就得做两点校准。常用做法是拿一颗经过计量的参考温度计把传感器和参考探头放在恒温水浴或恒温箱里取低温和高温两个点。具体操作是在 25 度和 70 度两个稳定点各采 200 个样本求均值记为raw_low、raw_high参考值为ref_low、ref_high然后按下面的公式做线性校正。# 两点线性校准把原始读数映射到参考温度 k (ref_high - ref_low) / (raw_high - raw_low) b ref_low - k * raw_low def calibrate(raw): return k * raw b逻辑说明k修正增益误差b修正零点偏移两个系数算完写进配置文件采集时实时套用。注意校准要在系统热平衡后进行采样前至少通电 30 分钟否则测到的是还没稳定的温漂。参数上如果两点覆盖范围太窄k会被噪声放大低温点最好压到 10 度以下、高温点拉到 70 度以上。长期运行最怕的是无声失败进程还在、数据没进来、告警不响。我一般埋三条可观测性线索。第一条是每小时的计数校验采集程序每小时往日志里写一条实际写入条数正常应该在 3600 上下浮动 5% 以内。第二条是心跳时间戳HTTP 接口的/latest返回的ts如果超过 30 秒没更新外部监控就报。第三条是原始值兜底采集失败时写入数值哨兵-999并置level2让曲线出现明显断层而不是数据静默中断。排查现场问题时下面这几条命令会反复用到# 看采集进程运行多久、重启过几次 systemctl status tempmon # 看最近的驱动层报错重点是 I2C NACK 和超时 dmesg -T | tail -50 # 检查存储空间数据库写满会静默失败 df -h /var/lib/tempmon # 直接查库确认最新数据时间 sqlite3 /var/lib/tempmon/temp.db SELECT datetime(max(ts),unixepoch,localtime) FROM temp_log如果max(ts)和当前时间差超过两个采样周期问题一定出在采集侧不在显示侧。这时先看dmesg里是不是 I2C 总线被风扇或继电器干扰再确认/sys下的传感器文件是否还在。把这三条线索和一套两点校准做完系统就算从能跑变成能长期赖在现场了。本文还有配套的精品资源点击获取