传感器:I2C/SPI/1-Wire
让树莓派感知世界:温度、湿度、气压、光照。掌握三大总线,就能接上市面上 90% 的传感器模块。
让树莓派感知世界:温度、湿度、气压、光照。掌握三大总线,就能接上市面上 90% 的传感器模块。
/sys/bus/w1/devices/,读文件即得数据,极简单。默认用 GPIO4。i2cdetect -y 1,会显示一张地址表,找到设备(如 0x76)说明接线正确。扫不到通常是接线错、没启用 I2C、或没接上拉电阻。read_byte_data(addr, reg)、write_byte_data() 等方法,直接读写设备的寄存器。是手动驱动 I2C 传感器的基础库。很多传感器也有更高层的专用库(如 Adafruit CircuitPython 系列)。MCP3008 类,读值返回 0.0~1.0,极其方便。先直观理解三种总线怎么接、各占几根线、能挂几个设备:
树莓派默认关闭这些接口,需先在 raspi-config 里启用(第 2 章讲过):
# 图形菜单方式:3 Interface Options → 分别启用 I2C / SPI / 1-Wire sudo raspi-config # 或命令行一键启用(0 = 启用) sudo raspi-config nonint do_i2c 0 sudo raspi-config nonint do_spi 0 sudo raspi-config nonint do_onewire 0 # 安装 I2C 工具 sudo apt install -y i2c-tools python3-smbus2 # 重启使内核模块加载 sudo reboot # 重启后确认设备文件已出现 ls /dev/i2c-* # 应看到 /dev/i2c-1 ls /dev/spidev* # 应看到 /dev/spidev0.0 spidev0.1
BMP280 是常见的 I2C 温度+气压传感器。接线:VCC→3.3V,GND→GND,SDA→GPIO2(脚3),SCL→GPIO3(脚5)。先扫描确认:
# 扫描 I2C 总线 1 上的设备 i2cdetect -y 1 # 输出地址表,在 76 位置看到 "76" 表示 BMP280 已识别: # 0 1 2 ... 6 7 8 9 a b c d e f # 70: 76 --
用 Adafruit 专用库读取(比手撸寄存器省心):
# 在虚拟环境中安装专用库
python3 -m venv env && source env/bin/activate
pip install adafruit-circuitpython-bmp280
# bmp280_read.py — 读取温度与气压 import board import adafruit_bmp280 from time import sleep # board.I2C() 自动使用 GPIO2/3(默认 I2C-1) i2c = board.I2C() sensor = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address=0x76) # 设置海平面气压用于计算海拔(可选,按当地天气填) sensor.sea_level_pressure = 1013.25 while True: print(f"温度: {sensor.temperature:.1f} °C") print(f"气压: {sensor.pressure:.1f} hPa") print(f"海拔: {sensor.altitude:.1f} m") print("-" * 20) sleep(2)
如果想理解底层,用 smbus2 手动读寄存器:
# raw_i2c.py — 用 smbus2 直接读 I2C 寄存器(理解底层) from smbus2 import SMBus BMP280_ADDR = 0x76 REG_ID = 0xD0 # 芯片 ID 寄存器(查数据手册得知) with SMBus(1) as bus: # 打开 I2C 总线 1 # 读取芯片 ID,BMP280 应返回 0x58 chip_id = bus.read_byte_data(BMP280_ADDR, REG_ID) print(f"芯片 ID: 0x{chip_id:02X}") # 0x58 = 确认是 BMP280
DS18B20 是防水温度探头,1-Wire 协议。接线:VCC→3.3V,GND→GND,DATA→GPIO4(脚7),DATA 与 VCC 之间接 4.7kΩ 上拉电阻。启用 1-Wire 后,内核驱动自动把它变成文件:
# 列出检测到的 1-Wire 设备(28- 开头是 DS18B20) ls /sys/bus/w1/devices/ # 28-3c01d607... 00-800000000000 (前者是温度传感器) # 读取原始数据文件 cat /sys/bus/w1/devices/28-*/w1_slave # 输出: # a1 01 4b 46 7f ff 0c 10 5c : crc=5c YES # a1 01 4b 46 7f ff 0c 10 5c t=26062 ← t=26062 表示 26.062°C
# ds18b20.py — 读 DS18B20 温度(读文件即可,无需库) import glob from time import sleep # 自动找到设备文件路径 device_file = glob.glob("/sys/bus/w1/devices/28-*/w1_slave")[0] def read_temp(): with open(device_file) as f: lines = f.readlines() # 第一行末尾要有 YES(CRC 校验通过) if lines[0].strip()[-3:] != "YES": return None # 第二行 t= 后面是温度×1000 pos = lines[1].find("t=") temp_c = int(lines[1][pos+2:]) / 1000.0 return temp_c while True: print(f"温度: {read_temp():.2f} °C") sleep(1)
因为树莓派内核直接内置了 1-Wire 驱动,把硬件协议全部处理好,最终以普通文本文件暴露给用户。你只需 cat 或 open() 读文件即可,无需理解时序、无需任何库。这是 Linux "一切皆文件"哲学的绝佳体现。
树莓派无 ADC,读电位器、光敏电阻这类模拟传感器必须外接 MCP3008。接线(SPI):VDD/VREF→3.3V,AGND/DGND→GND,CLK→GPIO11,DOUT(MISO)→GPIO9,DIN(MOSI)→GPIO10,CS→GPIO8(CE0)。gpiozero 直接支持:
# mcp3008.py — 读电位器(接 MCP3008 通道 0) from gpiozero import MCP3008 from time import sleep # channel=0 表示读 MCP3008 的 CH0 引脚 pot = MCP3008(channel=0) while True: # value 返回 0.0 ~ 1.0(10位ADC归一化) raw = pot.value voltage = raw * 3.3 # 换算成实际电压 print(f"归一值: {raw:.3f} 电压: {voltage:.2f}V") sleep(0.5)
# light_sensor.py — 光敏电阻 + MCP3008 做光照检测 from gpiozero import MCP3008, LED from time import sleep ldr = MCP3008(channel=1) # 光敏电阻接 CH1 led = LED(17) while True: light = ldr.value print(f"光照: {light:.2f}") # 天黑(值低于阈值)自动开灯 if light < 0.3: led.on() else: led.off() sleep(0.5)
| 总线 | 线数 | 速度 | 多设备 | 典型器件 |
|---|---|---|---|---|
| I2C | 2(SDA/SCL) | 中(100k~400kHz) | 靠地址,多个 | BMP280、OLED、RTC |
| SPI | 4(+每设备片选) | 高(数 MHz) | 靠片选,多个 | MCP3008、屏幕、Flash |
| 1-Wire | 1(+电源地) | 低 | 靠64位ID,多个 | DS18B20 温度 |
大纲提到的 DHT11/DHT22 温湿度传感器用的是专有单总线协议(非标准 1-Wire),时序严格。由于树莓派跑 Linux 无法保证微秒级实时性,纯软件读取常失败。推荐用 adafruit-circuitpython-dht 库配合 pip install,或干脆改用 I2C 的 AHT20/SHT31 替代——更稳定。示例:import adafruit_dht; dht = adafruit_dht.DHT22(board.D4)。
三大总线让树莓派感知世界。I2C(2 线 SDA/SCL,靠地址区分多设备)接 BMP280、OLED 等,先 i2cdetect -y 1 扫描确认地址,再用 Adafruit 专用库或 smbus2 手动读寄存器。1-Wire(1 根数据线 + 4.7kΩ 上拉)接 DS18B20,内核驱动把它变成 /sys/bus/w1/devices/ 下的文件,直接读文件即得温度,最简单。SPI(4 线 + 片选,高速)接屏幕、Flash,以及关键的 MCP3008 ADC——因为树莓派没有内置 ADC,读电位器/光敏电阻等模拟传感器必须外接它,gpiozero 的 MCP3008(channel=0) 返回 0.0~1.0。所有总线都要先在 raspi-config 启用。DHT22 用专有协议在 Linux 上不稳,建议改用 I2C 的 AHT20/SHT31。下一章我们用 PWM 驱动舵机和电机,让树莓派动起来。