树莓派Pico温度记录:MicroPython文件读写实战
发布时间:2026/9/11 1:50:03 作者:尧图编辑部 阅读量:1,286

最近我拿树莓派 Pico 做了一个温度数据记录的小项目本质上是把 MicroPython 文件读写这套东西从头到尾梳理了一遍。项目本身不复杂——读温度、打时间戳、写文件但你真正上手之后会发现文件读写里藏着大量细节打开模式选错了会丢历史数据忘了 flush 会掉电丢日志采样间隔太短还会加速 flash 磨损。这篇文章就把整个项目的设计思路、代码实现和踩坑过程完整记录下来既是给想入门 MicroPython 文件读写的朋友一份参考也是我自己的一个复盘。1. 一个“自动记事本”这个项目解决什么问题1.1 谁适合照着做做完能收获什么温度数据记录这个场景可能是学习 MicroPython 文件读写最好的入门练习没有之一。原因很简单它同时覆盖了文件读写的三个核心动作——打开文件、写入数据、追加记录而且数据格式是典型的“一行一条记录”可以顺手把 CSV 格式、时间戳、异常处理这些真实项目里离不开的东西全部练一遍。比起单纯在 REPL 里敲两行open()、write()这种“要记录一整天、晚上拔电导出数据”的完整流程才能真正让人理解文件系统的运作方式。这个项目适合三种人来参考刚拿到 Pico 想找个完整项目练手的新手已经会点 Python、但没接触过嵌入式文件系统特性的人以及想给环境温度做持续监测、但不想用云平台的玩家。做完之后你会得到一个能持续记录温度到本地文件的 Pico、一份结构化的 CSV 日志以及一套以后做任何数据记录项目都能复用的话术和代码框架。1.2 方案选型内置温度传感器还是外接 DS18B20树莓派 Pico 的 RP2040 芯片内部其实集成了一个温度传感器通过 ADC4 通道可以读取。所以做入门版温度记录不需要外接任何传感器一根 USB 线供电就能跑起来硬件成本就是一块 Pico 板子。但这里要说明白内置传感器测的是芯片内部温度不是环境温度。芯片在高负载下升温明显读数会比室温高几度到十几度用来做“学习文件读写”的载体完全够用用来做严谨的环境监测就不太合适了。如果你确实需要测环境温度最常见的方案是外接 DS18B20 数字温度传感器它走 OneWire 协议MicroPython 有现成的onewire和ds18x20模块接一根数据线到 GPIO 引脚就能用。但作为入门教程内置 ADC 的方案门槛最低后面我会单独讲怎么把代码改成 DS18B20。2. 开工前的准备Pico、固件和 Thonny2.1 硬件清单与接线这个入门版项目的硬件清单非常短树莓派 Pico 开发板一块USB 数据线一根注意要选能传数据的有些线只能充电插上后电脑没反应电脑一台Windows、macOS、Linux 都行如果你打算测环境温度再加一个 DS18B20 和一个 4.7kΩ 电阻。DS18B20 有三个引脚红色接 3V3OUT黑色接 GND黄色或白色数据线接任意 GPIO比如 GP28数据线和 3V3 之间并一个 4.7kΩ 上拉电阻。接线图网上很多这里不展开画了。内置传感器方案连一根线都不用接所以这部分整个跳过。2.2 给 Pico 烧录 MicroPython 固件新的 Pico 出厂默认是空的里面没有 MicroPython 固件需要自己烧。过程其实很简单但第一次做的人容易在 BOOTSEL 这一步卡住。正确顺序是这样的按住 Pico 板子上的 BOOTSEL 按钮不放保持按住把 USB 线插到电脑电脑上会出现一个名为RPI-RP2的可移动磁盘去 MicroPython 官网下载对应 Pico 的.uf2固件文件把下载好的.uf2文件直接拖进RPI-RP2磁盘拖完磁盘自动消失板子自动重启固件就烧好了这里有个容易出错的细节如果你先插 USB 再按 BOOTSEL是进不去烧录模式的。所以一定要“先按住按钮再插线”。如果你插上后没看到RPI-RP2拔掉重来按住按钮时间稍微长一点。2.3 配置 Thonny 并验证环境推荐用 Thonny 作为开发环境对 MicroPython 支持非常成熟集成了编辑器、REPL、文件浏览器对新手特别友好。安装并打开 Thonny 后需要配置解释器打开菜单栏的“运行” → “配置解释器”解释器选择“MicroPython (Raspberry Pi Pico)”端口会自动识别为连接的 Pico如果没识别到检查驱动或换一根数据线配置好后在 Thonny 的 Shell 窗口里应该能看到提示符。输入一行print(hello pico)能输出结果说明环境没问题了。这步验证很关键因为后面所有代码都要在 Thonny 里运行和调试如果数据线传输不稳定或固件版本太老会出现很多莫名其妙的报错先把环境跑通再往下走。3. 先搞懂 MicroPython 文件读写的几个关键点3.1 open() 的三种模式r、w、a 怎么选MicroPython 的文件操作和桌面 Python 基本一致核心入口就是open()函数。但嵌入式场景下模式选错的代价比桌面上大得多因为数据写在 flash 里恢复成本很高。三种最常用的模式是r只读模式文件不存在会直接报OSError: [Errno 2] ENOENTw写入模式会清空原有内容从头写文件不存在会自动创建a追加模式在文件末尾继续写不会动原有内容温度记录日志天然应该用a模式。我见过不少人图省事每次开机都用w模式重新创建文件结果记录几天后发现里面只有最后一次运行的数据之前全被覆盖了。这类数据丢失不会报任何错误程序看起来一切正常危害很大。如果你需要覆盖旧数据比如要重写配置文件才用w模式。日常追加记录一律a。另外还有一个容易忽略的点MicroPython 支持文本模式和二进制模式。写温度数据这种纯文本内容用默认的文本模式就行如果要写图片、录音等二进制数据需要在模式后面加b比如wb、ab。温度日志用不到。3.2 with 语句为什么是首选我见过很多初学 MicroPython 的人这样写文件f open(data.csv, a) f.write(...) f.close()这段代码单独看没问题但如果write()之前出错了close()就不会执行文件句柄一直占着不放。在嵌入式环境里文件句柄是有限的资源反复出错的程序运行一段时间后就会遇到“文件打不开但明明没满”的诡异问题。更推荐用with语句with open(data.csv, a) as f: f.write(...)with语句会在代码块执行完毕后自动关闭文件无论中间是否发生异常。这是 Python 里管理文件的标准姿势MicroPython 也完整支持入门阶段就用它能避开一大半文件相关的坑。说白了with就是帮你把“用完必须关”这件事自动处理掉你只管写数据不用记着清理。3.3 写入 flash 的坑flush 与数据落盘这是整个项目里最容易被忽略、也最要命的一个知识点。MicroPython 的write()执行后数据并不会立刻写入 Pico 的 flash 芯片而是先存在内存缓存里系统会在合适的时候统一写盘。这个机制和电脑上的硬盘缓存一模一样好处是减少 flash 擦写次数、延长寿命坏处是如果突然断电缓存里的数据就会丢。解决方式是在关键位置手动调用f.flush()把缓存里的数据强制刷到 flash。但要注意频繁 flush 会加重 flash 磨损所以要在“数据安全”和“flash 寿命”之间找平衡。我的做法是每条记录写完立即 flush。对于温度记录这种低频数据每 5 秒或 10 秒一条这个频率不算高安全性优先。如果你希望更彻底还可以在写完文件后调用os.sync()它会同步整个文件系统。不过日常场景下flush()已经足够。4. 温度数据记录程序设计与完整代码4.1 用 ADC4 读取 RP2040 内部温度RP2040 芯片集成温度传感器通过 ADC 通道 4 读取。MicroPython 里创建 ADC 对象时使用adc machine.ADC(4)读取时需要从read_u16()拿到 0 到 65535 的原始数值然后做两步换算第一步把原始值转换成电压。RP2040 的 ADC 参考电压是 3.3V所以voltage raw_value * 3.3 / 65535第二步根据数据手册给出的公式由电压换算温度temp_c 27 - (voltage - 0.706) / 0.001721这个公式的原理是芯片内部的温度传感器输出一个与温度线性相关的电压0.706V 对应 27 摄氏度每升高 1 摄氏度电压下降约 0.001721V。也就是传感器电压越低温度越高。这里我建议不要只读一次而是连续读 20 次取平均因为 ADC 读数本身有噪声单次读数可能出现 ±1℃ 的抖动。平均之后读数会稳定很多。4.2 用 RTC 给每条记录打上时间戳记录温度没有时间戳数据基本没用。但 Pico 这个板子有点特殊它没有电池供电的 RTC断电后时间会归零每次上电都回到默认时间。所以代码里要在记录开始前检查并设置一次时间。MicroPython 的machine.RTC()支持设置时间设置格式是一个 8 元元组rtc.datetime((2025, 1, 1, 0, 12, 0, 0, 0))依次是年、月、日、星期、时、分、秒、微秒。星期影响不大随手填个 0 就行。设置完后用time.localtime()拿到的就是一个可读的本地时间元组格式是(年, 月, 日, 时, 分, 秒, 星期, 年内第几天)我按这个顺序把年月日时分秒格式化进日志。更省事的判断方法是如果time.localtime()返回的年份明显不对比如小于 2024就自动设置一次。下面的完整代码就是这么处理的。4.3 完整代码与逐段解析下面是我整理后的完整代码直接在 Thonny 里新建文件运行即可import machine import time import os LOG_FILE /temperature_log.csv SAMPLE_INTERVAL_S 10 READINGS_PER_SAMPLE 20 adc machine.ADC(4) rtc machine.RTC()导入模块、定义常量的部分没什么好说的。LOG_FILE我用了绝对路径/temperature_log.csv这样不管当前工作目录在哪都不会写错地方。如果你想分目录管理可以配合os.mkdir()创建子目录。def setup_rtc(): now time.localtime() if now[0] 2024: rtc.datetime((2025, 1, 1, 0, 12, 0, 0, 0)) print(RTC 时间已初始化为 2025-01-01 12:00:00) else: print(RTC 时间正常当前时间, now[:6])这个函数检查当前 RTC 年份如果还没设置过就写一个初始时间进去。注意这只是权宜之计每次重新上电都要执行一次因为掉电后 RTC 不保存设置。def read_internal_temp(): total 0 for _ in range(READINGS_PER_SAMPLE): total adc.read_u16() avg_raw total / READINGS_PER_SAMPLE voltage avg_raw * 3.3 / 65535 temp_c 27 - (voltage - 0.706) / 0.001721 return avg_raw, voltage, temp_c读取传感器并做转换返回原始值、电压、温度三个量方便后面一起记录到 CSV 里。def ensure_file(): if LOG_FILE not in os.listdir(/): with open(LOG_FILE, w) as f: f.write(timestamp,adc_raw,voltage,temp_c\n)首次运行时创建 CSV 文件并写入表头。如果文件已经存在就跳过避免覆盖历史数据。这里用os.listdir(/)检查根目录下是否有同名文件是一个很实用的技巧。def append_record(line): with open(LOG_FILE, a) as f: f.write(line) f.flush()追加写入一条记录写完立即 flush。这就是前面说的“数据安全优先”策略。setup_rtc() ensure_file() print(开始记录温度采样间隔 {} 秒日志文件{}.format(SAMPLE_INTERVAL_S, LOG_FILE)) while True: try: raw, voltage, temp_c read_internal_temp() year, month, day, hour, minute, second, _, _ time.localtime() timestamp {:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.format( year, month, day, hour, minute, second ) line {},{:.0f},{:.3f},{:.1f}\n.format(timestamp, raw, voltage, temp_c) append_record(line) print(line.strip()) except Exception as e: print(记录失败请检查原因, e) time.sleep(SAMPLE_INTERVAL_S)主循环里用try/except包住了整个记录流程这样即使某次写入失败程序也不会崩溃退出而是打印错误后继续下一轮。日志在 while 循环中稳定追加。time.localtime()返回的 8 元组里最后两个是星期和年内第几天用_丢弃只取前六个字段。4.4 运行效果如何确认文件写对了在 Thonny 里运行这段程序Shell 窗口会周期性地打印出类似下面的内容开始记录温度采样间隔 10 秒日志文件/temperature_log.csv 2025-01-01 12:00:05,29810,1.501,24.8 2025-01-01 12:00:15,29825,1.502,24.6 2025-01-01 12:00:25,29840,1.504,24.4接下来你可以去 Thonny 左侧的“设备文件”面板查看文件列表应该能看到temperature_log.csv点进去就能看到和 Shell 输出对应的内容。如果想在电脑上查看在文件名上右键选择“下载到…”即可。这里有个新手常踩的坑Thonny 左侧文件面板默认显示的是电脑本地文件Pico 上的文件在“设备文件”或“MicroPython 设备”里别找错地方。5. 让日志真正可用文件管理与数据分析5.1 防止日志无限增长文件大小检查与轮转一直跑下去日志文件会越来越大。虽然 Pico 的文件系统有 2MB温度记录一条才几十字节理论上能存很久但严谨一点应该在写入前检查文件大小超过某个阈值就自动轮转。轮转的常见做法是把当前文件重命名为带时间戳的旧文件然后新建一个空文件继续写。MAX_LOG_SIZE 100 * 1024 # 100KB def rotate_log_if_needed(): try: size os.stat(LOG_FILE)[6] if size MAX_LOG_SIZE: new_name /temperature_backup_{}.csv.format(time.time_ns()) os.rename(LOG_FILE, new_name) with open(LOG_FILE, w) as f: f.write(timestamp,adc_raw,voltage,temp_c\n) print(日志已轮转旧文件, new_name) except OSError: pass这个函数放在主循环开头调用就行。用time.time_ns()生成文件名后缀基本不会重名。这句代码解决的是“记录几个月后磁盘被撑满”的隐患。5.2 在电脑上用 pandas 读取 CSV记录下来的 CSV 文件可以直接用文本编辑器打开但要分析趋势最好用 pandas 读到电脑里处理。从 Pico 下载temperature_log.csv到电脑后在电脑的 Python 环境里运行import pandas as pd df pd.read_csv(temperature_log.csv) df[timestamp] pd.to_datetime(df[timestamp]) print(df.head()) print(df[temp_c].describe())这样就能得到温度记录的统计摘要均值、标准差、最高最低温度。再进一步画个折线图df.plot(xtimestamp, ytemp_c)温度变化趋势一目了然。这个衔接点是我当时做这个项目最爽的部分Pico 负责采集存储电脑负责分析和出图两边用的都是 Python生态完全打通从硬件到数据分析一条链。6. 踩坑实录常见问题与排查技巧6.1 打开/写入文件报错怎么排查做文件读写基本绕不开几个固定报错这里整理成一份速查表报错信息常见原因解决方法OSError: [Errno 2] ENOENT文件或目录不存在检查路径用w或a模式会自动创建文件OSError: [Errno 28] ENOSPCflash 空间已满删除旧日志文件或做日志轮转OSError: [Errno 5] EIOflash 写入异常或文件系统损坏用os.listdir()检查文件系统必要时重新烧录固件ValueError: invalid syntax打开模式写错了检查模式字符串是否为r、w、a及组合文件打不开但没报错文件句柄被占用之前有没 close 的文件统一改用with语句管理文件最开始我用r模式去读不存在的文件每次都报ENOENT后来才意识到对写入场景应该用a或w模式文件不存在会自动创建。这个经验让我养成了“先想清楚要读还是要写”的习惯。6.2 温度读数忽高忽低怎么办如果发现连续两次读到的温度差好几度通常是 ADC 噪声导致的。解决办法就是前面代码里做的多次采样取平均我把READINGS_PER_SAMPLE设成 20实际测试下来抖动基本能控制在 ±0.3℃ 以内。还有一个容易被忽略的因素如果你用的是 Pico 板载供电USB 电源本身的纹波会影响 ADC 参考电压导致读数漂移。这时候可以在读取前加一个小延时或者改用外部 3.3V 基准不过入门阶段不用太较真。另外内置温度传感器对芯片负载很敏感。如果循环里做了大量计算或频繁写 flash芯片发热会让读数偏高。我测试时发现高负载下读数能比待机高 5℃ 以上所以解读数据时心里要有数。6.3 断电后数据丢了问题出在哪这是最典型的嵌入式文件系统问题。很多人写完就拔电再插上发现刚才记录的数据不见了然后怀疑代码有问题。其实大概率是write()的数据还在内存缓存里没落盘。解决思路在前面讲过每条记录写完立即flush()。如果依然丢数据可以再检查一下是不是文件句柄没有正常关闭with语句能保证这一点。还有个细节拔电前最好留一点时间让最后一条 flush 完成。如果你在写入日志的瞬间强行切断电源即使调用了 flush也可能赶不上。比较稳妥的做法是在主循环里控制写入时机比如在循环末尾执行time.sleep(0.1)给文件系统一点喘息空间。6.4 记录久了内存不足怎么办MicroPython 运行在内存有限的 MCU 上Pico 的 RAM 只有 264KB所以你不能像写桌面程序那样随意创建大列表。温度记录程序本身内存占用很低但如果你的代码里用了大量字符串拼接、或往列表里攒了上万条记录才一次性写入内存就会爆掉。正确姿势是一次只处理一条记录写完立即丢弃。上面的示例代码里所有变量都在每次循环结束时被回收内存占用是恒定的跑多久都不会涨。如果你想在记录前做滤波或缓存把缓冲区大小限制在几十条以内定期批量写入即可不要无限增长。做完这个项目我最大的体会是MicroPython 里的文件读写并不是“把 Python 语法搬到单片机上”这么简单。flash 的写入寿命、掉电丢失、文件句柄管理、RTC 时间初始化这些在桌面 Python 里完全不用操心的问题到了嵌入式环境全都变成了绕不开的坑。但恰恰是这些坑才让这个入门项目变得特别有学习价值——它逼着你去理解底层在做什么而不是停留在 API 调用层面。如果你照着这篇文章把代码跑通了建议下一步试试外接 DS18B20 测环境温度再把日志轮转加上记录个跨夜的数据然后导到电脑上画一张温度变化曲线。这一套流程走完你基本就掌握了 MicroPython 文件读写的绝大多数实战要点后面再做传感器的数据记录项目思路会非常顺畅。