树莓派Pico + OLED:手把手教你打造一个看得见的智能家居监控终端

你有没有过这样的经历?买了温湿度传感器,接上树莓派,数据是能读出来——但只能在电脑串口里看一串数字。想看看家里孩子房间是不是太闷、老人卧室有没有开窗通风?还得打开手机连SSH,查日志……太麻烦了。

今天我们就来解决这个问题: 让数据“长眼睛” 。

用一块小小的OLED屏,配上不到40元的树莓派Pico,做一个实时显示家居环境状态的小终端。温度、湿度、光照强度,一眼看清。不依赖网络、不用操作系统、代码不到百行,新手也能一天搞定。

这不是炫技,而是真正实用的嵌入式小工具。它安静地挂在墙上,像一个会呼吸的信息角,默默告诉你家里的空气正在发生什么变化。


为什么选树莓派Pico?不只是便宜那么简单

很多人第一次听说Pico,是因为“它只要4美元”。没错,价格确实香,但它真正打动我的地方,在于 把专业级能力塞进了极简外壳里 。

它的核心是RP2040芯片,双核ARM Cortex-M0+,主频133MHz,264KB内存——这听起来可能不如手机处理器惊艳,但在微控制器世界里已经是“性能怪兽”了。相比之下,经典款Arduino Uno(ATmega328P)只有2KB内存和16MHz主频,差距不是一点半点。

更关键的是,Pico原生支持 MicroPython 。这意味着你可以像写脚本一样控制硬件:

import machine
import time

led = machine.Pin(25, machine.Pin.OUT)  # 板载LED

while True:
    led.toggle()
    time.sleep(0.5)

就这么几行,就能让Pico上的小灯开始呼吸闪烁。不需要编译、不需要烧录复杂固件,插上USB,拖进文件就行。这种交互式的开发体验,对初学者极其友好。

而且别忘了它的PIO(Programmable I/O)系统——这是Pico独有的黑科技。你可以用它模拟SPI、I²C甚至自定义协议,相当于给GPIO装了个“变形引擎”。虽然我们这次用不上,但它意味着未来扩展无线模块或特殊传感器时几乎不会被接口卡住。


OLED屏怎么选?SSD1306为何成为创客标配?

市面上屏幕五花八门,为什么我们偏偏挑中这块小小的单色OLED?

答案就两个字: 克制 。

做监控终端,你要的不是炫酷动画,而是一个清晰、省电、可靠的信息窗口。SSD1306驱动的0.96英寸OLED完美符合这个定位。

它分辨率为128×64,通过I²C仅需两根线(SCL/SDA)就能通信,典型工作电流仅0.04W。最关键的是—— 它是自发光的 。不像LCD需要背光,OLED每个像素自己发光,黑色就是彻底关闭,所以对比度极高,关灯后也看得清。

接线也很简单:
- Pico GP8 → OLED SDA
- Pico GP9 → OLED SCL
- VCC接3.3V,GND接地

注意:有些OLED默认地址是 0x3C ,有些是 0x3D 。如果初始化失败,先用 i2c.scan() 扫一下就知道了。

初始化代码如下:

from machine import Pin, I2C
import ssd1306

# 创建I2C对象
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=400_000)
print("Scanning I2C devices...", i2c.scan())  # 应该返回 [60] 即 0x3C

oled = ssd1306.SSD1306_I2C(128, 64, i2c)

# 显示欢迎语
oled.fill(0)  # 清屏(0=黑,1=白)
oled.text("Hello!", 0, 0)
oled.text("OLED OK", 0, 16)
oled.show()

这里有个细节: .show() 才是真正把缓存刷到屏幕上。在此之前所有 .text() 操作都是在内存中绘制,避免画面撕裂。这也是OLED驱动的标准做法。


数据从哪来?传感器接入实战

我们的目标是显示三项关键指标: 温度、湿度、光照 。成本控制在百元内,全部走通用接口。

温湿度:DHT11 vs DHT22

推荐直接上 DHT22 。虽然贵十几块,但精度高得多(±0.5°C vs ±2°C),响应更快,寿命更长。接线只需三根:VCC(3.3V)、GND、Data → Pico GP15。

MicroPython有内置的 dht 库,调用非常简洁:

import dht
import machine

sensor = dht.DHT22(machine.Pin(15))

try:
    sensor.measure()
    temp = sensor.temperature()  # 摄氏度
    humi = sensor.humidity()     # 百分比
    print(f"当前温度: {temp}°C, 湿度: {humi}%")
except OSError as e:
    print("读取失败,请检查接线或电源")

⚠️ 坑点提醒:DHT系列对时序要求严格,建议供电加一个0.1μF电容滤波。另外每两次读取间隔至少2秒,否则会报错。

光照强度:用BH1750还是光敏电阻?

BH1750是数字光照传感器,I²C接口,输出单位勒克斯(lux),理论更准。但我实测发现,在室内自然光下,普通光敏电阻+分压电路完全够用,成本还低一大截。

接法也很简单:
- 光敏电阻一端接3.3V
- 另一端接Pico GP26(即ADC0)
- 同时在这端并联一个10kΩ下拉电阻到GND

这样就能读出模拟电压值:

from machine import ADC

adc = ADC(26)  # GP26 支持ADC输入

value = adc.read_u16()        # 返回0~65535
percentage = value * 100 // 65535  # 转为百分比

虽然不是精确lux值,但趋势是对的:白天亮、晚上暗、窗帘拉开变亮。对于可视化需求来说,这就足够了。


把一切串起来:完整的主循环逻辑

现在所有部件都准备好了,接下来就是整合成一个流畅运行的系统。

整体流程很清晰:
1. 初始化I²C和OLED
2. 设置传感器引脚
3. 进入无限循环:
- 读取各传感器数据
- 清屏,绘制新内容
- 刷新显示
- 等待2秒再继续

完整代码如下:

import time
import dht
from machine import Pin, I2C, ADC
import ssd1306

# === 硬件初始化 ===
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=400_000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)

sensor = dht.DHT22(Pin(15))
light_adc = ADC(26)

# === 主循环 ===
while True:
    # 读温湿度
    try:
        sensor.measure()
        temp = sensor.temperature()
        humi = sensor.humidity()
    except:
        temp = humi = None

    # 读光照
    light_raw = light_adc.read_u16()
    light_pct = light_raw * 100 // 65535

    # 更新屏幕
    oled.fill(0)  # 清屏防残影
    oled.text("🏠 家居状态", 0, 0)

    if temp is not None:
        oled.text(f"🌡️ {temp}°C", 0, 20)
        oled.text(f"💧 {humi}%", 0, 32)
    else:
        oled.text("⚠️ 传感器异常", 0, 20)

    oled.text(f"💡 光照: {light_pct}%", 0, 44)
    oled.show()

    time.sleep(2)  # 每2秒更新一次

你会发现,整个程序结构非常干净。没有复杂的中断、任务调度,也没有RTOS。对于这种低频监测场景,轮询就够了。

而且你完全可以根据需要修改显示样式。比如夜间自动降低亮度,或者超过阈值时显示警告图标。


实际部署中的那些“小问题”

理想很丰满,现实总有磕绊。我在实际搭建过程中踩过几个坑,分享给你避雷:

❌ 屏幕不显示?先看地址和供电

最常见的问题是OLED没反应。第一步永远是:

print(i2c.scan())

如果返回空列表 [] ,说明根本没检测到设备。可能是:
- 接线反了(SDA/SCL搞混)
- 电源不足(尤其是用长杜邦线时压降明显)
- 地线没共通
- I²C地址不对(换 I2C(1) 试试)

❌ 数字跳变严重?加个去抖逻辑

传感器偶尔通信失败会导致数据显示突变。可以加入简单的软件滤波:

# 维护历史值列表
temps = []
humis = []

def safe_read():
    try:
        sensor.measure()
        t = sensor.temperature()
        h = sensor.humidity()
        temps.append(t)
        humis.append(h)
        # 只保留最近5次
        if len(temps) > 5: temps.pop(0)
        if len(humis) > 5: humis.pop(0)
        return sum(temps)/len(temps), sum(humis)/len(humis)
    except:
        return None, None

这样即使某次读取失败,也不会立刻影响显示。

❌ 长时间显示同一画面?小心“烧屏”

OLED虽然寿命长,但长时间显示固定图案仍可能导致轻微残影。解决方案很简单:
- 定期全屏刷新(我们已经做了 fill(0) )
- 避免静态布局太久(可考虑每隔几分钟轻微移动文字位置)


这个小东西还能怎么玩?

你以为这就完了?不,这才是起点。

这个终端最大的价值,是它 既是终点,也是起点 。

你可以轻松把它升级为:
- Wi-Fi上报节点 :加上ESP-01S模块,把数据发到Home Assistant;
- 本地报警器 :当湿度过高或光线过暗时,触发蜂鸣器提醒;
- 儿童房守护者 :配合继电器自动开关加湿器;
- 植物生长伴侣 :放在花盆边,提醒浇水和补光;
- 教学演示平台 :带学生理解ADC、I²C、传感器融合等概念。

更重要的是,它教会我们一种思维方式: 不要让数据沉睡在日志里,要让它可见、可感、可用 。


如果你也想拥有这样一个“看得见的家”,现在就可以动手。所需材料清单如下:

名称 型号 单价估算
主控 树莓派Pico ¥25
显示屏 0.96” SSD1306 OLED ¥15
温湿度 DHT22 ¥12
光照检测 光敏电阻 + 10kΩ电阻 ¥2
面包板 + 杜邦线 若干 ¥10
合计 —— 约¥65

不到一杯奶茶的钱,换来一个真正属于你的智能终端。

不妨今晚就试试?插上Pico,点亮那块小小的屏幕,看着第一行“Hello World”跳出来的时候,你会明白:物联网的本质,从来都不是连接云端,而是让技术回归生活本身。

更多推荐