尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

长时程温度记录器DIY:基于Arduino与DS18B20的低功耗设计

长时程温度记录器DIY:基于Arduino与DS18B20的低功耗设计 做温度测量的人都知道测一个“点”容易测一条“线”很难。这里说的“线”是时间轴上的温度曲线——从某天上午10点开始每一分钟记录一次连续记录48小时、72小时甚至一个月。这种“Temperature Logger for Long Duration Sessions”类的需求比想象中要普遍得多发酵箱升温曲线、温室昼夜温差、冷链运输波动、服务器机柜热点变化、低温慢煮水温稳定性……这些问题拿温度计怼一下根本回答不了你需要的是一个能长时间稳定在线、自动记录、不丢数据的小盒子。这篇文章我打算完整交代一遍我做长时程温度记录器的全过程包括为什么没买现成记录仪、器件怎么选、固件怎么写、掉电保护怎么做、实测数据长什么样、以及哪些坑必须绕开。内容偏向可复现的实操目标读者是想自己攒一套低成本温度记录系统的人无论你是做发酵、做实验还是只是想监控家里鱼缸和暖气温度多少都能参考。1. 为什么长时程记录比瞬时测量难一个量级1.1 瞬时测量永远回答不了“这段时间发生了什么”很多人一开始会想测温度嘛拿个温度计/万用表加探头不就行了。但真正开始记录才发现瞬时测量和长时程记录是两个物种。你要的不是某一个时刻的读数而是整个时间窗口里温度怎么变化、有没有异常跳变、持续了多久。举个例子。我做酸奶发酵时发现发酵箱标称42°C但实际箱内温度是波动的加热棒启动时冲到44°C然后缓慢跌到40°C再重新加热。如果你只在“某个时间点”去测一次可能恰好测到43°C也可能测到41°C根本不知道这个循环周期是多少。只有把整晚的数据按分钟记录下来才看到波峰波谷的规律。这就是长时程记录的核心价值——它不是“测量仪器”而是“证据链”。1.2 长时程记录器的需求拆解在动手之前我先把自己的需求列了出来。没有这一步后面选型肯定翻车需求项具体要求我的判断记录时长单次会话至少48小时理想情况一周以上必须低功耗大容量存储采样间隔可配置默认1分钟1次间隔太短会显著拉高功耗时间戳每条记录必须带准确时间掉电不能丢RTC独立供电或带电池存储介质离线也能存几万条数据方便导出MicroSD卡是最省事的方案工作环境可能是潮湿、震动、长时间无人值守探头防水、断电恢复要可靠成本单套不超过100元主流传感器MCU组合可以满足这些需求写完之后方案其实就明朗了用单片机做离线记录器传感器负责测温RTC负责时间SD卡负责存储电池负责续航。不要过度设计。2. 方案选型我为什么没有用树莓派和商用记录仪2.1 自研记录器的三个可选主控与选型结论市面上能跑温度记录的板子很多但适合“长时间离线”的真不多。我重点考虑了三类树莓派Zero WLinux系统写Python脚本很方便甚至可以直接挂SQLite。但树莓派待机功耗在100mA以上5V供电约0.5W电池供电续航很难看而且TF卡在异常断电后容易文件系统损坏。做长期无人值守它不是个好选择。ESP32 / ESP32-C3集成Wi-Fi/蓝牙支持深度睡眠Deep Sleep功耗可以做到几十微安。但唤醒后Wi-Fi重连、时间同步会引入额外功耗和代码复杂度。如果你需要远程回传数据ESP32是首选。ATmega328PArduino Pro Mini 3.3V/8MHz极其成熟的8位MCU外设简单深度睡眠电流在5µA以下代码逻辑清晰。缺点是没有网络功能但这恰恰是优点——离线系统故障点更少。我最终选了ATmega328P。原因很直接这个项目的核心诉求是“低功耗可靠记录”不是“远程可见”。把Wi-Fi砍掉功耗和稳定性压力直接小一个量级。2.2 传感器选型DS18B20为什么够用温度传感器我首先排除了NTC热敏电阻——虽然便宜但非线性校准麻烦而且模拟信号长线传输容易受干扰。选来选去DS18B20是综合性价比最高的数字输出单总线协议一根数据线就能挂多个传感器读到的是数字量没有模拟校准问题。防水探头封装常见不锈钢外壳版本可以扔进水槽、埋在土壤、贴在金属表面。精度±0.5°C-10°C ~ 85°C对于发酵、环境监测、设备散热检查完全够用。工作电压3.0V~5.5V和3.3V主控直接兼容。DS18B20的可配置分辨率也值得了解它支持9到12位可调。12位分辨率下转换时间最长约750ms9位分辨率只要93.75ms精度略低。我的记录器默认用12位分辨率1分钟采样一次750ms的转换时间对功耗和速度都不是问题。2.3 RTC和存储时间戳比测量值更重要的场景长时程记录里时间戳的优先级其实比温度值更高。你想如果数据里只有温度没有时间48小时后你根本不知道哪条对应哪一刻。所以RTC实时时钟不是可选项是必须项。我用的是DS3231模块。它内置温补晶振TCXO在-40°C到85°C范围内年误差通常在2分钟以内。相比之下DS1307用的是普通32.768kHz晶振温度变化大时一天偏好几秒都很正常长时程记录积累下来的误差很难接受。DS3231模块自带CR2032电池座主电源断了RTC还能继续走时间这个特性对无人值守场景太重要了。存储部分我选了MicroSD卡模块配合FAT32文件系统而不是单片机EEPROM或外挂Flash。原因是容量和读取效率EEPROM按KB算Flash按MB算SD卡按GB算。用1分钟采样一次、CSV格式文本存储一天的数据量大约只有100KB左右一张2GB的老旧SD卡都能记录一年以上。更关键的是SD卡拔下来插电脑就能读不需要额外通信工具这和“离线记录器”的核心体验非常匹配。3. 硬件搭建传感器、RTC、SD卡与电源的连线细节3.1 完整接线表我的硬件组合为Arduino Pro Mini 3.3V/8MHz DS18B20防水探头 DS3231 RTC模块 MicroSD卡模块。连线如下模块引脚接主控引脚备注DS18B20VCC红线3.3V如果线缆很长建议在探头端并联0.1µF电容DS18B20GND黑线GNDDS18B20DQ黄线D2必须接一个4.7kΩ上拉电阻到3.3VDS3231VCC3.3V模块自带电池座DS3231GNDGNDDS3231SDAA4I2C数据线DS3231SCLA5I2C时钟线DS3231SQWD3用于唤醒单片机接外部中断1SD卡模块CSD10SPI片选SD卡模块MOSID11SD卡模块MISOD12SD卡模块SCKD133.2 电源与电平最容易出问题的地方这个系统里最容易被忽视的是电平匹配。Arduino Pro Mini如果用的是5V/16MHz版本SD卡模块和DS18B20虽然兼容5V但很多MicroSD卡模块的电平转换电路做得并不好长期用5V供电容易发热甚至烧卡。所以我特意选了3.3V/8MHz的Pro Mini版本所有外设电平统一3.3V问题少一半。电源我用了一节18650锂电池标称3.7V通过一个低压差LDO降到3.3V供电。这里要注意普通的AMS1117-3.3压差约1V输入电压低于4.3V时输出电压就会开始跌落18650在放电后期电压降到3.4V左右AMS1117基本就废了。我改用MCP1700-3.3压差只有0.4V左右可以用到电池电压降到3.5V甚至更低。如果你手里只有AMS1117也不是不能做但要把电池保护板的截止电压调低或者干脆用两节AA电池串联。提示SD卡写入瞬间电流可能到50~100mA如果LDO余量不足电压瞬间跌落会导致传感器读数跳变甚至SD卡写入失败。选LDO时至少留2倍余量。4. 固件实现定时唤醒、温度读取与SD写入的循环4.1 主循环思路整个固件的逻辑不复杂核心是一个“睡一会儿→醒来干点活→继续睡”的循环上电初始化RTC、SD卡、DS18B20配置DS3231的闹钟中断让它每N秒/分钟拉低一次SQW引脚单片机进入深度睡眠POWER_DOWN模式SQW引脚产生下降沿触发外部中断唤醒读取RTC当前时间、读取DS18B20温度、拼接CSV行写入SD卡重置下一次闹钟回到第3步。用DS3231的闹钟中断而非看门狗定时器WDT唤醒有一个关键优势时间基准更准确。AVR的WDT受温度和电压影响有较大漂移长时间累积误差明显。而DS3231的闹钟基于温补晶振1分钟的闹钟间隔误差极小还能顺便保证每条记录的时间戳和真实时间一致。4.2 低功耗策略与唤醒机制深度睡眠是ATmega328P功耗的关键。用avr/sleep.h把MCU切到SLEEP_MODE_PWR_DOWN关闭ADC、定时器、串口等外设睡眠电流可以降到5µA左右。唤醒后先恢复外设再干活。代码框架大致是这个样子#include avr/sleep.h #include avr/power.h #include Wire.h #include SD.h #include OneWire.h #include DallasTemperature.h #define RTCPIN A4 #define SD_CS 10 #define ONE_WIRE_BUS 2 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(oneWire); volatile bool wokeUp false; void wakeUpISR() { wokeUp true; } void enterDeepSleep() { wokeUp false; set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_enable(); sleep_cpu(); // 到这里CPU停住等待中断 sleep_disable(); power_all_enable(); } void setup() { Wire.begin(); Serial.begin(9600); sensors.begin(); SD.begin(SD_CS); // 配置DS3231的在D3引脚产生闹钟中断 pinMode(3, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(3), wakeUpISR, FALLING); // 第一次记录前先初始化时间戳 // 如果RTC时间异常可在这里强制设定一次基准时间 } void loop() { enterDeepSleep(); if (wokeUp) { logTemperature(); } }实际项目里logTemperature()内部会做三件事读RTC时间、请求DS18B20转换并读取、拼CSV行写入SD。这里特别说明一下DS18B20的读取姿势void logTemperature() { DateTime now rtc.now(); // 读RTC时间 sensors.requestTemperatures(); // 发起温度转换 float temp sensors.getTempCByIndex(0); // 读取第一个传感器 if (temp DEVICE_DISCONNECTED_C) { temp NAN; // 传感器掉线要记录异常而不是乱填0 } File dataFile SD.open(LOG.CSV, FILE_WRITE); if (dataFile) { dataFile.print(now.year()); dataFile.print(-); dataFile.print(now.month()); dataFile.print(-); dataFile.print(now.day()); dataFile.print( ); dataFile.print(now.hour()); dataFile.print(:); dataFile.print(now.minute()); dataFile.print(:); dataFile.print(now.second()); dataFile.print(,); dataFile.println(temp); dataFile.flush(); // 确保数据落到SD卡 dataFile.close(); } }单总线协议有一点要注意DS18B20转换温度需要时间12位分辨率至少要等750ms而且requestTemperatures()是非阻塞的如果你想在转换期间做其他事需要主动延时或查询状态。但在这个项目里750ms完全在可在唤醒窗口内完成无脑等待就行。4.3 写入可靠性设计长时程记录的另一个关键点是掉电保护。SD卡和电脑的U盘不一样数据在FAT文件系统里要经过缓冲区如果写入后不flush直接断电很可能丢数据甚至损坏文件系统。我的策略是每次写入后调用dataFile.flush()确保数据落入物理介质每次开机时生成一个递增的文件名比如LOG0001.CSV、LOG0002.CSV避免重复覆盖旧数据如果SD卡初始化失败不阻塞主流程LED闪烁提示同时保留内存中的最近记录等SD恢复后再补写。这里有个取舍频繁flush会增加SD卡写入次数但1分钟1次、每条约60字节的写入量对SD卡寿命来说九牛一毛不用过度担心。对于要求更高的场景比如无人值守一个月你可以改成每小时关闭一次文件、重新打开追加降低文件系统碎片代价是断电时最多丢失一小时数据。5. 长跑实测48小时连续记录的数据与功耗5.1 校准流程与精度测试做完硬件和固件第一件事不是直接丢到现场而是先校准。DS18B20的出厂精度是±0.5°C但如果你的探头要跨好几个小时去追踪0.2°C级别的变化这个误差就不能忽略。最简单的校准方法是冰水浴把探头放进碎冰和水的混合物中标准温度是0°C等读数稳定后记录差值把这个差值作为固件里的偏移常量。如果没有碎冰用高精度数字温度计和水银温度计做对比也可以只是操作麻烦一点。我在实测前做了三组校准参考源标准温度DS18B20读数偏差冰水浴0.0°C0.1°C0.1°C室温环境25.3°C参考温度计25.6°C0.3°C恒温加热台45.0°C参考温度计45.2°C0.2°C三组数据基本都在规格书标称的±0.5°C以内最终取平均偏移0.2°C在固件里修正。5.2 48小时实测数据的表现我拿这套记录器做了48小时发酵箱温度监测采样间隔1分钟。以下是开头20条数据和后面某段数据的节选时间温度°C备注2025-06-20 10:00:0028.31启动室温2025-06-20 10:01:0028.40发酵箱预热2025-06-20 10:02:0028.522025-06-20 10:03:0028.662025-06-20 10:04:0028.812025-06-20 10:05:0028.952025-06-20 10:10:0031.2010分钟升温2.9°C2025-06-20 10:20:0038.152025-06-20 10:30:0042.062025-06-20 10:35:0042.18接近设定温度2025-06-20 10:40:0041.92加热棒停止温度回落2025-06-20 10:45:0041.752025-06-20 10:50:0042.08加热棒重启开始新一轮循环从数据里能清楚看到发酵箱的“加热-停止-冷却-再加热”循环周期大约10分钟温度波动范围在±0.5°C以内。这种规律性用肉眼观察根本不可能发现只有连续记录才能看出来。48小时全程下来128KB左右的文件大小共2880条记录没出现一条乱码或时间戳错乱。中途我故意拔掉电源再插上记录器重启后能从断电前的文件名继续追加没有丢时间戳DS3231的电池起到了关键作用。5.3 功耗实测与续航估算功耗测试是这次项目里让我最意外的地方。用万用表串联在电池正极测电流分开测三种状态状态实测电流持续时间深度睡眠4.2µA59.8秒唤醒读RTCDS18B20转换写SD约35mA约200ms初始化阶段SD挂载等约25mA约1秒按1分钟采样周期计算平均电流平均电流 4.2µA × 59.8s / 60s 35mA × 0.2s / 60s ≈ 4.19µA 116.7µA ≈ 121µA一块18650电池如果标称2000mAh理论上2000mAh / 0.121mA ≈ 16528小时 ≈ 688天当然这只是理论值LDO自身静态电流、电池自放电、SD卡插入检测损耗都要算进去实际打折到50~60%也有一年左右的续航。这个结果已经远超我的预期足够支撑绝大多数“long duration sessions”了。6. 数据回读与常见问题处理6.1 CSV导出与Python绘图记录器测完的SD卡直接拔下来插电脑打开CSV文件就是纯文本。我习惯用Python和matplotlib快速出图因为很多发酵、环境监测场景下曲线比数字表格直观得多import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(LOG0001.CSV, names[time, temp]) df[time] pd.to_datetime(df[time]) df[temp] pd.to_numeric(df[temp], errorscoerce) plt.figure(figsize(12, 4)) plt.plot(df[time], df[temp], linewidth1) plt.xlabel(time) plt.ylabel(temperature (°C)) plt.title(48h fermentation temperature) plt.tight_layout() plt.savefig(fermentation_curve.png, dpi150)如果你不想写Python直接用Excel导入CSV选“分隔符号-逗号”再插入折线图效果完全一样。实测下来CSV文本格式最稳妥的地方就是兼容性——任何表格软件都能打开二十年后再翻出来也不会因为软件升级读不了。6.2 传感器漂移与误读的排查长时程记录跑久了最烦的是偶尔冒出一个离谱读数比如发酵箱明明42°C突然出现一条-55°C。这个问题我一开始也遇到过排查下来有三大常见原因DS18B20接触不良单总线对接触电阻很敏感接线端子松动会导致读数跳变。解决方法是焊接而非插拔端子或者用带螺丝的接线端子压紧。电源波动SD卡写入瞬间拉低电压DS18B20供电跌落会导致转换结果异常。加一个100µF电解电容在电源端问题基本消失。传感器自热DS18B20转换时会有约1mA级电流流过传感器连续快速采样比如每秒一次会让探头自身升温约0.1~0.2°C长时程记录按分钟采样则基本无影响。我还在固件里加了一道简单的滤波如果当前读数和上一条记录差值超过±5°C再连续读两次确认如果三次都是异常值才记录为异常。这样既能把偶发干扰滤掉又不会把真实的温度剧变漏掉。提示如果要对传感器做更严格的可靠性验证可以把它和参考温度计并排放在同一个环境里连续记录24小时对比两条曲线的偏差曲线。偏差稳定说明传感器和系统都正常偏差逐渐漂移则说明探头老化或受潮。最后再分享一个小技巧。DS18B20的单总线设计天然支持一总线挂多个探头我后来把系统扩展成了“1个记录器3个探头”分别放在发酵箱上、中、下三层。固件里只需要把requestTemperatures()改成循环读取getTempCByIndex(0/1/2)CSV追加一列就能同时看到垂直方向的温度梯度。很多“长时间会话”的真实问题其实不是单一测点的变化而是多点之间的差异——能多测几个点这套记录器的价值直接翻倍。
返回列表