树莓派Pico与MicroPython温度传感器驱动实战:从DS18B20到TMP36
2026/8/23 22:32:09 网站建设 项目流程

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 核心组件清单

首先,你需要准备以下硬件:

  1. 树莓派Pico:主角,负责运行我们的MicroPython程序。
  2. 温度传感器
    • DS18B20(数字):一款精度较高(±0.5°C)、采用单总线协议的数字温度传感器。它只需要一根数据线(外加电源和地)即可通信,抗干扰能力强,适合长距离布线。
    • TMP36(模拟):一款输出电压与温度成正比的模拟传感器。价格便宜,使用简单,但精度和抗干扰能力相对数字传感器稍弱。
  3. 面包板与杜邦线:用于快速搭建电路,无需焊接。
  4. 电阻
    • 对于DS18B20,需要一个4.7kΩ的上拉电阻。
    • 对于TMP36,通常不需要额外电阻。
  5. USB数据线:用于给Pico供电和传输程序。

2.2 电路连接原理与实操

正确的电路连接是项目成功的一半。错误的连接轻则无法读取数据,重则可能损坏传感器或Pico。

对于DS18B20(数字传感器):DS18B20采用“单总线”协议。这意味着数据发送、接收、电源(在某些接线方式下)都通过同一根线完成。为了在空闲状态下将总线稳定在高电平,并且为传感器内部电容提供充电电流,必须在数据线(DQ)和电源(VCC)之间连接一个上拉电阻(通常4.7kΩ)。这是单总线设备的典型要求,缺少它,通信会非常不稳定甚至完全失败。

具体连接步骤:

  1. 将DS18B20的VDD引脚(通常为红色线)连接到Pico的**3V3(OUT)**引脚。这是3.3V电源。
  2. 将DS18B20的GND引脚(通常为黑色或蓝色线)连接到Pico的任意一个GND引脚。
  3. 将DS18B20的DQ引脚(数据线,通常为黄色线)连接到Pico的某个GPIO引脚,例如GP16
  4. 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。

具体连接步骤:

  1. 将TMP36的VCC引脚(平面对着自己,从左至右依次为VCC、OUT、GND)连接到Pico的3V3(OUT)
  2. 将TMP36的GND引脚连接到Pico的GND
  3. 将TMP36的OUT引脚(信号输出)连接到Pico的GP26(对应ADC0)。

注意:Pico的ADC参考电压是3.3V。这意味着它能把0-3.3V的输入电压转换成0-65535的数字值(16位精度)。TMP36在测量范围内输出电压不会超过3.3V,因此可以直接连接。如果你的传感器输出可能超过3.3V,必须使用分压电路进行降压,否则会损坏Pico的ADC!

3. MicroPython环境搭建与固件烧录

硬件连好了,接下来要让Pico“听懂”我们的Python指令。这一步就是给Pico安装MicroPython“操作系统”。

3.1 获取与烧录固件

  1. 下载固件:访问树莓派基金会官方仓库,找到最新稳定版的MicroPython固件文件(通常是一个.uf2文件)。这是为Pico硬件量身定制的解释器。
  2. 进入烧录模式:按住Pico板上的BOOTSEL按钮不放,然后将USB线插入电脑。此时,电脑会将Pico识别为一个名为RPI-RP2的U盘。
  3. 复制固件:将下载好的.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 使用onewireds18x20

MicroPython固件通常内置了onewireds18x20库。我们的代码结构如下:

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(当电源电压Vs=3.3V时,公式近似成立;更精确的公式是Vout = (Temp * 0.01) + 0.5,但需要补偿,为简化我们使用前者) 因此,温度计算公式为:Temp = (Vout - 0.75) / 0.01Temp = (Vout - 0.75) * 100

为什么是0.75V?因为TMP36在0°C时,输出典型值为0.75V。每升高1°C,输出电压增加10mV(0.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(f'ADC值:{adc_value:>5d}, 电压:{voltage:.3f} V, 温度:{temperature_c:.2f} °C') time.sleep(2)

代码关键点与优化:

  • adc_pin.read_u16():这是MicroPython提供的便捷方法,直接返回0-65535的数值。
  • 读数波动与软件滤波:模拟读数容易受到电源噪声、电磁干扰等影响,导致数值在小范围内跳动。直接使用单次读数会显得温度值不稳定。一个简单有效的改进是多次采样取平均
def read_temperature_samples(samples=100): 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> <head><title>Pico Temperature</title></head> <body> <h1>Current Temperature</h1> <p style="font-size: 48px;">{temp:.2f} &deg;C</p> <p><small>Last 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客户端库。

核心流程是:

  1. 连接Wi-Fi。
  2. 连接MQTT服务器。
  3. 在主循环中读取温度。
  4. 将温度数据封装成JSON格式。
  5. 发布到指定的MQTT主题(例如pico/sensor/temperature)。
  6. 云平台订阅该主题,即可进行数据存储、可视化或触发报警。

这种架构将数据采集(边缘设备)和数据处理/展示(云端)解耦,是现代物联网项目的标准做法。

7. 项目优化与常见问题排查

即使按照步骤操作,你也可能会遇到一些问题。这里总结几个我踩过的坑和对应的解决方案。

7.1 DS18B20读取失败或返回85°C

  • 症状scan()返回空列表,或者read_temp()始终返回85.0°C。
  • 排查步骤
    1. 检查硬件连接:这是最常见的问题。确保VCC、GND、DQ三根线没有接错或虚接。尤其检查4.7kΩ上拉电阻是否连接在DQ和3.3V之间。可以用万用表测量DQ引脚对地电压,在空闲时应该是3.3V左右的高电平。
    2. 检查电源电压:DS18B20的工作电压范围是3.0V至5.5V,但Pico的3.3V输出是足够的。如果使用长导线,线阻可能导致传感器端电压不足,可以尝试在传感器VCC和GND之间并联一个0.1uF的电容稳压。
    3. 检查GPIO引脚:确保代码中定义的引脚号(如Pin(16))与实际硬件连接一致。
    4. 85°C的特殊含义:DS18B20上电后的默认温度值就是85°C。如果一直读到85°C,通常意味着convert_temp()命令没有成功执行,或者你在转换完成前就执行了read_temp()确保在convert_temp()后等待足够长的时间(至少750ms)
    5. 尝试不同的GPIO引脚:有些GPIO引脚可能有特殊功能(如调试),换一个普通的GPIO试试。

7.2 TMP36读数不准或跳动大

  • 症状:计算出的温度值与实际环境温度偏差较大,或者数值不停跳动。
  • 排查与优化
    1. 软件滤波:如前所述,必须使用多次采样取平均。单次ADC读数噪声很大。
    2. 校准参考电压:用万用表实际测量Pico的3V3(OUT)引脚电压,替换代码中的VREF = 3.3。例如,实测为3.28V,就改成VREF = 3.28。这是提升精度的最有效方法之一。
    3. 检查公式与传感器型号:确认你使用的传感器是TMP36(0°C时输出0.75V)。还有类似型号如TMP35(0°C时输出0V)、LM35(0°C时输出0V),它们的转换公式不同。
    4. 电源去耦:在TMP36的VCC和GND引脚之间,靠近传感器的地方,焊接或插接一个0.1uF的陶瓷电容,可以有效滤除电源噪声。
    5. 避免自发热:如果Pico或传感器本身发热较大(如长时间运行复杂程序),会影响测量精度。尽量让传感器远离热源,并保持空气流通。

7.3 MicroPython程序崩溃或重启

  • 症状:程序运行一段时间后,Shell输出乱码或自动重启。
  • 可能原因与解决
    1. 内存泄漏:在循环中不断创建对象(如新的PinADC对象)而不释放,会导致内存耗尽。最佳实践是在循环外初始化所有硬件对象
    2. 看门狗超时:MicroPython有时会启用看门狗定时器。如果你的循环中有长时间的阻塞操作(如time.sleep(60)),可能导致看门狗复位。可以考虑使用time.sleep_ms()进行短时间睡眠,或者用machine.idle()来降低功耗。
    3. 电源问题:USB供电不稳定或电流不足。尝试更换USB线或连接到电脑主板后置USB口,避免使用劣质充电宝或扩展坞。
    4. 代码错误导致硬故障:例如,试图配置一个已经用于其他功能的引脚。仔细检查代码逻辑。

7.4 提升项目的健壮性

  • 异常处理:使用try...except块包裹可能出错的代码(如传感器读取、网络请求)。
    try: temp = ds_sensor.read_temp(rom) print(f'Temperature: {temp}C') except Exception as e: print(f'Failed to read sensor: {e}') # 可以在这里尝试重新初始化传感器
  • 将主程序保存为main.py:在Thonny中,将你的代码文件命名为main.py并上传到Pico的根目录。这样,Pico上电后就会自动运行这个程序,实现脱机运行。
  • 低功耗考虑:对于电池供电的项目,可以在每次读取间隔让Pico进入深度睡眠模式。使用machine.deepsleep(),并通过GPIO或RTC闹钟唤醒,可以极大延长电池寿命。

从连接一根线开始,到稳定地读取温度数据,再到将数据通过网络发送出去,这个过程涵盖了嵌入式开发从硬件到软件、从本地到网络的基本链路。我自己的第一个Pico项目就是一个放在花盆里的土壤湿度温度监测器,用的就是DS18B20。当时最大的收获不是代码怎么写,而是理解了上拉电阻为什么必不可少,以及软件滤波对模拟信号有多重要。这些经验远比单纯复制代码更有价值。希望你在动手实践的过程中,也能遇到并解决属于自己的那个“坑”,那才是学习嵌入式最有趣的部分。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询