拓冰建站拓冰建站
首页 / 资讯中心 / 正文

树莓派Pico与MicroPython温度传感器驱动实战:从DS18B20到TMP36

1. 项目缘起为什么用Pico和MicroPython做温度读取如果你手头正好有一块树莓派Pico想用它做点简单又有趣的电子项目那么读取温度传感器绝对是个不错的起点。这个项目听起来简单但背后涉及了嵌入式开发中几个非常核心的环节硬件接口、传感器通信协议、数据采集与处理以及如何将物理世界的信号转换成我们能理解的数字信息。我之所以选择用MicroPython来实现而不是更底层的C/C原因很简单快。MicroPython为像Pico这样的微控制器提供了一个极简的Python运行环境。这意味着如果你已经会写几行Python代码那么几乎不需要学习新的语法就能直接控制GPIO、读取传感器、处理数据。这大大降低了嵌入式开发的门槛让你能把精力集中在项目逻辑本身而不是纠结于寄存器配置、内存管理和复杂的编译工具链。对于快速原型验证、教育学习或者DIY一个家用温度监控器来说MicroPython的“快速上手”特性是无与伦比的。市面上温度传感器很多比如经典的DS18B20单总线、DHT11/DHT22单总线带湿度、以及像LM35、TMP36这样的模拟传感器。为了覆盖更广的应用场景我会以数字传感器DS18B20和模拟传感器TMP36为例分别讲解在MicroPython下的驱动方法。这样无论你手头是哪类传感器都能找到对应的解决方案。2. 硬件准备与核心电路解析工欲善其事必先利其器。在写代码之前我们需要把硬件连接好。这里会涉及一些基础的电子学知识但别担心我会解释清楚每一个连接背后的“为什么”。2.1 核心组件清单首先你需要准备以下硬件树莓派Pico主角负责运行我们的MicroPython程序。温度传感器DS18B20数字一款精度较高±0.5°C、采用单总线协议的数字温度传感器。它只需要一根数据线外加电源和地即可通信抗干扰能力强适合长距离布线。TMP36模拟一款输出电压与温度成正比的模拟传感器。价格便宜使用简单但精度和抗干扰能力相对数字传感器稍弱。面包板与杜邦线用于快速搭建电路无需焊接。电阻对于DS18B20需要一个4.7kΩ的上拉电阻。对于TMP36通常不需要额外电阻。USB数据线用于给Pico供电和传输程序。2.2 电路连接原理与实操正确的电路连接是项目成功的一半。错误的连接轻则无法读取数据重则可能损坏传感器或Pico。对于DS18B20数字传感器DS18B20采用“单总线”协议。这意味着数据发送、接收、电源在某些接线方式下都通过同一根线完成。为了在空闲状态下将总线稳定在高电平并且为传感器内部电容提供充电电流必须在数据线DQ和电源VCC之间连接一个上拉电阻通常4.7kΩ。这是单总线设备的典型要求缺少它通信会非常不稳定甚至完全失败。具体连接步骤将DS18B20的VDD引脚通常为红色线连接到Pico的**3V3(OUT)**引脚。这是3.3V电源。将DS18B20的GND引脚通常为黑色或蓝色线连接到Pico的任意一个GND引脚。将DS18B20的DQ引脚数据线通常为黄色线连接到Pico的某个GPIO引脚例如GP16。在DQ引脚和3V3电源之间连接一个4.7kΩ的电阻。这就是上拉电阻。注意务必确认你的DS18B20模块引脚定义。有些模块已经集成了上拉电阻这时外接电阻反而可能导致问题。如果模块上有三个引脚VCC, GND, DQ且没有标明已集成电阻通常需要外接。如果模块只有两个引脚GND和DQ则它可能采用“寄生供电”模式从数据线偷电这种情况下对时序要求更严格初学者建议使用三线接法即上述接法更稳定。对于TMP36模拟传感器TMP36的输出是模拟电压所以我们需要使用Pico的模拟输入引脚ADC引脚。Pico有3个ADC引脚GP26, GP27, GP28但GP28通常被内部ADC用于测量VSYS所以我们常用GP26和GP27。具体连接步骤将TMP36的VCC引脚平面对着自己从左至右依次为VCC、OUT、GND连接到Pico的3V3(OUT)。将TMP36的GND引脚连接到Pico的GND。将TMP36的OUT引脚信号输出连接到Pico的GP26对应ADC0。注意Pico的ADC参考电压是3.3V。这意味着它能把0-3.3V的输入电压转换成0-65535的数字值16位精度。TMP36在测量范围内输出电压不会超过3.3V因此可以直接连接。如果你的传感器输出可能超过3.3V必须使用分压电路进行降压否则会损坏Pico的ADC3. MicroPython环境搭建与固件烧录硬件连好了接下来要让Pico“听懂”我们的Python指令。这一步就是给Pico安装MicroPython“操作系统”。3.1 获取与烧录固件下载固件访问树莓派基金会官方仓库找到最新稳定版的MicroPython固件文件通常是一个.uf2文件。这是为Pico硬件量身定制的解释器。进入烧录模式按住Pico板上的BOOTSEL按钮不放然后将USB线插入电脑。此时电脑会将Pico识别为一个名为RPI-RP2的U盘。复制固件将下载好的.uf2文件直接拖拽或复制到这个RPI-RP2U盘中。复制完成后Pico会自动重启U盘盘符会消失。至此MicroPython固件烧录完成。3.2 选择并连接开发工具Pico现在是一个运行着MicroPython的微型电脑了。我们需要一个“终端”来给它发送命令。有两个主流选择Thonny强烈推荐给初学者。它是一个集成了代码编辑、MicroPython shell和文件管理的IDE。安装后在右下角选择解释器为“MicroPython (Raspberry Pi Pico)”并选择正确的串口点击“Stop/Restart”按钮就能在下方Shell中与Pico交互了。你可以直接输入print(‘Hello Pico!’)并回车如果看到回复说明连接成功。命令行工具如rshell,ampy适合更喜欢命令行操作的用户。它们可以通过串口向Pico发送文件或执行命令。我个人的习惯是快速测试和交互用Thonny的Shell编写正式项目代码则用任何你喜欢的代码编辑器如VS Code写好再用Thonny或ampy上传到Pico。Thonny的文件管理器功能可以很方便地上传、下载和删除Pico上的文件。4. 驱动DS18B20单总线协议深度解析现在进入核心环节写代码读取传感器。我们先从数字传感器DS18B20开始这会涉及到对单总线协议的微观操作。4.1 单总线协议一种“一问一答”的通信方式你可以把单总线想象成一条共享的电话线主机Pico和多个从机DS18B20都挂在这条线上。所有通信都由主机发起和控制。协议非常严格包括初始化序列主机发送一个复位脉冲从机回应一个存在脉冲宣告“我在线”。ROM命令如果总线上有多个传感器主机需要通过读取每个传感器唯一的64位ROM地址来选中其中一个。功能命令告诉被选中的传感器要做什么比如开始温度转换、读取暂存器等。在MicroPython中我们需要通过精确的时序来控制GPIO引脚的高低电平变化来模拟这些信号。幸运的是通常有现成的库帮我们处理这些底层细节。4.2 使用onewire和ds18x20库MicroPython固件通常内置了onewire和ds18x20库。我们的代码结构如下import machine import onewire import ds18x20 import time # 1. 定义数据引脚 dat_pin machine.Pin(16) # 对应之前硬件连接的GP16 # 2. 创建单总线对象 ds_bus onewire.OneWire(dat_pin) # 3. 创建DS18X20传感器对象 ds_sensor ds18x20.DS18X20(ds_bus) # 4. 扫描总线上的传感器 roms ds_sensor.scan() print(找到的传感器ROM地址, roms) # 5. 主循环 while True: # 5.1 发起温度转换命令需要时间DS18B20最高精度约750ms ds_sensor.convert_temp() time.sleep_ms(750) # 必须等待转换完成时间取决于分辨率设置 # 5.2 读取温度 for rom in roms: temp_c ds_sensor.read_temp(rom) print(f传感器 {rom.hex()[:8]}... 温度{temp_c:.2f} °C) time.sleep(5) # 每5秒读取一次代码关键点解析ds_sensor.scan()这个方法会返回一个列表包含总线上所有DS18B20传感器的64位ROM地址。这是识别和访问多个传感器的关键。convert_temp()这是一个“广播”命令总线上所有的DS18B20都会同时开始温度转换。转换需要时间在12位分辨率下默认最大转换时间为750毫秒。这里是一个常见的坑如果你在convert_temp()后没有等待足够时间就去read_temp()读到的将是上一次的转换结果或者一个错误的值。所以time.sleep_ms(750)是必须的。read_temp(rom)传入具体的ROM地址读取该传感器的温度值。返回值是摄氏度浮点数。4.3 多传感器管理与地址识别当你连接多个DS18B20时scan()会返回多个ROM地址。你可以将这些地址保存在列表或字典中并为它们赋予逻辑名称如“室内”、“室外”。sensor_dict { b(\xaa\x8b\x15\x05\x00\x00\xd9: living_room, b(\xff\x64\x1d\x05\x00\x00\x0c: balcony } for rom, name in sensor_dict.items(): temp ds_sensor.read_temp(rom) print(f{name}: {temp:.2f}°C)实操心得如何获取传感器的ROM地址第一次运行上述扫描代码时它会打印出类似b(\xaa\x8b\x15\x05\x00\x00\xd9的字节串。把这个地址记录下来它就是该传感器的“身份证”。注意每个传感器的地址是唯一的焊接或更换传感器后地址会变。5. 读取TMP36模拟信号与ADC的奥秘模拟传感器TMP36的读取逻辑完全不同。它输出的是一个连续的电压值我们需要用Pico的ADC模数转换器将这个电压“翻译”成数字再根据传感器数据手册中的公式计算出温度。5.1 ADC基础与参考电压Pico内部有一个12位的ADC在MicroPython中通过machine.ADC访问时默认被缩放为16位即0-65535。它的参考电压Vref是内部的3.3V。这是一个非常重要的参数因为ADC读数adc_value和实际电压voltage的关系是voltage (adc_value / 65535) * 3.3这个3.3V是理想值。实际上Pico内部3.3V稳压器的输出会有微小偏差且ADC本身也有偏移和增益误差。对于要求不高的温度测量直接用3.3V计算没问题。如果追求更高精度可以测量Pico的3V3(OUT)引脚的实际电压作为Vref或者使用Pico的ADC_VREF引脚连接一个更精准的外部基准电压源。5.2 TMP36电压-温度转换公式根据TMP36的数据手册其输出电压Vout单位伏特与温度Temp单位摄氏度的关系是Vout (Temp * 0.01) 0.75当电源电压Vs3.3V时公式近似成立更精确的公式是Vout (Temp * 0.01) 0.5但需要补偿为简化我们使用前者 因此温度计算公式为Temp (Vout - 0.75) / 0.01即Temp (Vout - 0.75) * 100为什么是0.75V因为TMP36在0°C时输出典型值为0.75V。每升高1°C输出电压增加10mV0.01V。5.3 完整代码与滤波处理import machine import time # 1. 初始化ADC引脚 adc_pin machine.ADC(26) # 对应GP26 ADC0 # 2. 定义参考电压实测或理想值 VREF 3.3 # 3. 主循环 while True: # 3.1 读取ADC原始值 adc_value adc_pin.read_u16() # 读取16位无符号整数范围0-65535 # 3.2 计算电压 voltage (adc_value / 65535) * VREF # 3.3 计算温度使用简化公式 temperature_c (voltage - 0.75) * 100 # 3.4 打印结果 print(fADC值{adc_value:5d}, 电压{voltage:.3f} V, 温度{temperature_c:.2f} °C) time.sleep(2)代码关键点与优化adc_pin.read_u16()这是MicroPython提供的便捷方法直接返回0-65535的数值。读数波动与软件滤波模拟读数容易受到电源噪声、电磁干扰等影响导致数值在小范围内跳动。直接使用单次读数会显得温度值不稳定。一个简单有效的改进是多次采样取平均。def read_temperature_samples(samples100): total 0 for _ in range(samples): total adc_pin.read_u16() time.sleep_us(100) # 微小延迟避免采样过快 avg_adc total / samples voltage (avg_adc / 65535) * VREF return (voltage - 0.75) * 100 # 在主循环中调用 temp read_temperature_samples(50) print(f平均温度{temp:.2f} °C)通过取50或100个样本的平均值可以显著平滑输出得到更稳定的温度读数。这是在实际项目中提升数据质量的必备技巧。6. 数据记录与远程监控进阶玩法仅仅在Shell里打印温度值还不够酷。我们可以让项目变得更实用比如把数据存下来或者通过网络发送到手机或电脑上。6.1 将数据记录到Pico本地文件Pico的MicroPython固件支持文件系统虽然很小约1.4MB。我们可以将温度数据以CSV格式写入文件。import uos def log_to_file(filename, sensor_name, temperature, timestamp): # 检查文件是否存在如果不存在则写入表头 try: with open(filename, r) as f: pass # 文件存在 except OSError: with open(filename, w) as f: f.write(timestamp,sensor_name,temperature_c\n) # 追加数据 with open(filename, a) as f: f.write(f{timestamp},{sensor_name},{temperature:.2f}\n) # 在主循环中获取时间戳并记录 # MicroPython的time.time()返回的是自2000年1月1日以来的秒数我们可以用这个 current_time time.time() log_to_file(temp_log.csv, DS18B20_01, temp_c, current_time)注意频繁写入文件会磨损Pico的Flash存储。对于长期、高频的日志记录建议考虑外接SD卡模块或者将数据先缓存在内存中定期批量写入。6.2 构建一个简单的Web服务器如果你的Pico连接了Wi-Fi模块如Pico W你可以让它变成一个微型Web服务器通过浏览器实时查看温度。这里以Pico W为例展示核心思路import network import socket import time # ... (Wi-Fi连接代码省略) def web_page(temp): html f !DOCTYPE html html headtitlePico Temperature/title/head body h1Current Temperature/h1 p stylefont-size: 48px;{temp:.2f} deg;C/p psmallLast updated: {time.ticks_ms()}/small/p /body /html return html addr socket.getaddrinfo(0.0.0.0, 80)[0][-1] s socket.socket() s.bind(addr) s.listen(1) print(Listening on, addr) while True: cl, addr s.accept() print(Client connected from, addr) request cl.recv(1024) # 读取传感器温度假设有一个read_temp()函数 current_temp read_temp() response web_page(current_temp) cl.send(HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n) cl.send(response) cl.close() # 每次请求后等待一下避免过热 time.sleep(0.1)这段代码创建了一个阻塞式的单线程Web服务器。当你在浏览器中输入Pico的IP地址时它会返回一个包含当前温度值的简单网页。这对于局域网内的快速监控非常方便。6.3 将数据发送到物联网平台更高级的玩法是将数据发送到云平台如ThingsBoard、Blynk、或者自建的MQTT服务器如Mosquitto。这需要你的Pico连接网络并使用umqtt.simple等MQTT客户端库。核心流程是连接Wi-Fi。连接MQTT服务器。在主循环中读取温度。将温度数据封装成JSON格式。发布到指定的MQTT主题例如pico/sensor/temperature。云平台订阅该主题即可进行数据存储、可视化或触发报警。这种架构将数据采集边缘设备和数据处理/展示云端解耦是现代物联网项目的标准做法。7. 项目优化与常见问题排查即使按照步骤操作你也可能会遇到一些问题。这里总结几个我踩过的坑和对应的解决方案。7.1 DS18B20读取失败或返回85°C症状scan()返回空列表或者read_temp()始终返回85.0°C。排查步骤检查硬件连接这是最常见的问题。确保VCC、GND、DQ三根线没有接错或虚接。尤其检查4.7kΩ上拉电阻是否连接在DQ和3.3V之间。可以用万用表测量DQ引脚对地电压在空闲时应该是3.3V左右的高电平。检查电源电压DS18B20的工作电压范围是3.0V至5.5V但Pico的3.3V输出是足够的。如果使用长导线线阻可能导致传感器端电压不足可以尝试在传感器VCC和GND之间并联一个0.1uF的电容稳压。检查GPIO引脚确保代码中定义的引脚号如Pin(16)与实际硬件连接一致。85°C的特殊含义DS18B20上电后的默认温度值就是85°C。如果一直读到85°C通常意味着convert_temp()命令没有成功执行或者你在转换完成前就执行了read_temp()。确保在convert_temp()后等待足够长的时间至少750ms。尝试不同的GPIO引脚有些GPIO引脚可能有特殊功能如调试换一个普通的GPIO试试。7.2 TMP36读数不准或跳动大症状计算出的温度值与实际环境温度偏差较大或者数值不停跳动。排查与优化软件滤波如前所述必须使用多次采样取平均。单次ADC读数噪声很大。校准参考电压用万用表实际测量Pico的3V3(OUT)引脚电压替换代码中的VREF 3.3。例如实测为3.28V就改成VREF 3.28。这是提升精度的最有效方法之一。检查公式与传感器型号确认你使用的传感器是TMP360°C时输出0.75V。还有类似型号如TMP350°C时输出0V、LM350°C时输出0V它们的转换公式不同。电源去耦在TMP36的VCC和GND引脚之间靠近传感器的地方焊接或插接一个0.1uF的陶瓷电容可以有效滤除电源噪声。避免自发热如果Pico或传感器本身发热较大如长时间运行复杂程序会影响测量精度。尽量让传感器远离热源并保持空气流通。7.3 MicroPython程序崩溃或重启症状程序运行一段时间后Shell输出乱码或自动重启。可能原因与解决内存泄漏在循环中不断创建对象如新的Pin、ADC对象而不释放会导致内存耗尽。最佳实践是在循环外初始化所有硬件对象。看门狗超时MicroPython有时会启用看门狗定时器。如果你的循环中有长时间的阻塞操作如time.sleep(60)可能导致看门狗复位。可以考虑使用time.sleep_ms()进行短时间睡眠或者用machine.idle()来降低功耗。电源问题USB供电不稳定或电流不足。尝试更换USB线或连接到电脑主板后置USB口避免使用劣质充电宝或扩展坞。代码错误导致硬故障例如试图配置一个已经用于其他功能的引脚。仔细检查代码逻辑。7.4 提升项目的健壮性异常处理使用try...except块包裹可能出错的代码如传感器读取、网络请求。try: temp ds_sensor.read_temp(rom) print(fTemperature: {temp}C) except Exception as e: print(fFailed to read sensor: {e}) # 可以在这里尝试重新初始化传感器将主程序保存为main.py在Thonny中将你的代码文件命名为main.py并上传到Pico的根目录。这样Pico上电后就会自动运行这个程序实现脱机运行。低功耗考虑对于电池供电的项目可以在每次读取间隔让Pico进入深度睡眠模式。使用machine.deepsleep()并通过GPIO或RTC闹钟唤醒可以极大延长电池寿命。从连接一根线开始到稳定地读取温度数据再到将数据通过网络发送出去这个过程涵盖了嵌入式开发从硬件到软件、从本地到网络的基本链路。我自己的第一个Pico项目就是一个放在花盆里的土壤湿度温度监测器用的就是DS18B20。当时最大的收获不是代码怎么写而是理解了上拉电阻为什么必不可少以及软件滤波对模拟信号有多重要。这些经验远比单纯复制代码更有价值。希望你在动手实践的过程中也能遇到并解决属于自己的那个“坑”那才是学习嵌入式最有趣的部分。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门