【树莓派Zero 2W】物联网智能家居终端
本文介绍了树莓派 Zero 2W 驱动 BMP280 传感器获取环境温度压强数据,并通过 MQTT 协议上传至 Home Assistant 智能家居平台的物联网项目设计。
项目介绍
树莓派 Zero 2W 驱动 BMP280 获取数据,通过 MQTT 协议上传至 HA 平台。
准备工作:包括系统安装、软件包部署、硬件连接等;
BMP280:通过 smbus 库驱动 BMP280 传感器模块,终端打印环境温度压强数据;
MQTT:结合板载 WiFi 模块,实现 MQTT 报文发送、EMQX 订阅测试;
Home Assistant:将 BMP280 数据通过 MQTT 发送至 HA 并自动识别添加传感器设备,实现物联网终端。
准备工作
连接 WiFi 实现无线网络通信;
使用 Micro-USB 数据线实现设备供电;


系统安装
Micro-SD 卡需烧录 RaspberryPi 官方操作系统;
软件包部署
安装 IIC 通信所需的 smbus 库,以及排针定义环境 RPI.GPIO 库;
sudo apt-get update sudo apt-get install python3-smbus sudo apt-get install python3-RPi.GPIO
使能板端 IIC 配置
终端执行指令 raspi-config 打开配置界面;
依次选择 Interface - IIC - Enable - Yes 即可。
硬件连接
BMP280 模块接线方式如下
BMP280 模块树莓派 Zero 2W
| VCC | 3.3V |
| GND | GND |
| SDA | 40Pin-3 (SDA) |
| SCL | 40Pin-5 (SCL) |
终端执行 i2cdetect -y 1 指令,检索 BMP280 传感器设备,识别对应的 IIC 设备地址 0x77;


BMP280
BMP280 是一种专门用于移动用途的绝对气压传感器,可同时获取环境的温度和大气压强数据。


这里介绍了 IIC 协议驱动 BMP280 传感器并终端打印温度和大气压强的相关流程,包括关键代码、效果演示等。
代码
终端执行指令 touch bmp280.py 新建程序文件,并添加如下代码
import time
import smbus
# BMP280 iic address.
BMP280_I2C_ADDRESS = 0x77 # SDO = 0
# Registers value
BMP280_ID_Value = 0x58 # BMP280 ID
BMP280_RESET_VALUE = 0xB6
# BMP280 Registers definition
BMP280_TEMP_XLSB_REG = 0xFC # Temperature XLSB Register
BMP280_TEMP_LSB_REG = 0xFB # Temperature LSB Register
BMP280_TEMP_MSB_REG = 0xFA # Temperature LSB Register
BMP280_PRESS_XLSB_REG = 0xF9 # Pressure XLSB Register
BMP280_PRESS_LSB_REG = 0xF8 # Pressure LSB Register
BMP280_PRESS_MSB_REG = 0xF7 # Pressure MSB Register
BMP280_CONFIG_REG = 0xF5 # Configuration Register
BMP280_CTRL_MEAS_REG = 0xF4 # Ctrl Measure Register
BMP280_STATUS_REG = 0xF3 # Status Register
BMP280_RESET_REG = 0xE0 # Softreset Register
BMP280_ID_REG = 0xD0 # Chip ID Register
# calibration parameters
BMP280_DIG_T1_LSB_REG = 0x88
BMP280_DIG_T1_MSB_REG = 0x89
BMP280_DIG_T2_LSB_REG = 0x8A
BMP280_DIG_T2_MSB_REG = 0x8B
BMP280_DIG_T3_LSB_REG = 0x8C
BMP280_DIG_T3_MSB_REG = 0x8D
BMP280_DIG_P1_LSB_REG = 0x8E
BMP280_DIG_P1_MSB_REG = 0x8F
BMP280_DIG_P2_LSB_REG = 0x90
BMP280_DIG_P2_MSB_REG = 0x91
BMP280_DIG_P3_LSB_REG = 0x92
BMP280_DIG_P3_MSB_REG = 0x93
BMP280_DIG_P4_LSB_REG = 0x94
BMP280_DIG_P4_MSB_REG = 0x95
BMP280_DIG_P5_LSB_REG = 0x96
BMP280_DIG_P5_MSB_REG = 0x97
BMP280_DIG_P6_LSB_REG = 0x98
BMP280_DIG_P6_MSB_REG = 0x99
BMP280_DIG_P7_LSB_REG = 0x9A
BMP280_DIG_P7_MSB_REG = 0x9B
BMP280_DIG_P8_LSB_REG = 0x9C
BMP280_DIG_P8_MSB_REG = 0x9D
BMP280_DIG_P9_LSB_REG = 0x9E
BMP280_DIG_P9_MSB_REG = 0x9F
class BMP180(object):
def __init__(self, address=BMP280_I2C_ADDRESS):
self._address = address
self._bus = smbus.SMBus(1) # 1: iic编号为1(根据自己的硬件接口选择对应的编号)
# Load calibration values.
if self._read_byte(BMP280_ID_REG) == BMP280_ID_Value: # read bmp280 id
self._load_calibration() # load calibration data
# BMP280_T_MODE_1 << 5 | BMP280_P_MODE_1 << 2 | BMP280_SLEEP_MODE;
ctrlmeas = 0xFF
# BMP280_T_SB1 << 5 | BMP280_FILTER_MODE_1 << 2;
config = 0x14
self._write_byte(BMP280_CTRL_MEAS_REG, ctrlmeas) # write bmp280 config
# sets the data acquisition options
self._write_byte(BMP280_CONFIG_REG, config)
else:
print("Read BMP280 id error!\r\n")
def _read_byte(self, cmd):
return self._bus.read_byte_data(self._address, cmd)
def _read_u16(self, cmd):
LSB = self._bus.read_byte_data(self._address, cmd)
MSB = self._bus.read_byte_data(self._address, cmd+1)
return (MSB << 8) + LSB
def _read_s16(self, cmd):
result = self._read_u16(cmd)
if result > 32767:
result -= 65536
return result
def _write_byte(self, cmd, val):
self._bus.write_byte_data(self._address, cmd, val)
def _load_calibration(self): # load calibration data
"load calibration"
""" read the temperature calibration parameters """
self.dig_T1 = self._read_u16(BMP280_DIG_T1_LSB_REG)
self.dig_T2 = self._read_s16(BMP280_DIG_T2_LSB_REG)
self.dig_T3 = self._read_s16(BMP280_DIG_T3_LSB_REG)
""" read the pressure calibration parameters """
self.dig_P1 = self._read_u16(BMP280_DIG_P1_LSB_REG)
self.dig_P2 = self._read_s16(BMP280_DIG_P2_LSB_REG)
self.dig_P3 = self._read_s16(BMP280_DIG_P3_LSB_REG)
self.dig_P4 = self._read_s16(BMP280_DIG_P4_LSB_REG)
self.dig_P5 = self._read_s16(BMP280_DIG_P5_LSB_REG)
self.dig_P6 = self._read_s16(BMP280_DIG_P6_LSB_REG)
self.dig_P7 = self._read_s16(BMP280_DIG_P7_LSB_REG)
self.dig_P8 = self._read_s16(BMP280_DIG_P8_LSB_REG)
self.dig_P9 = self._read_s16(BMP280_DIG_P9_LSB_REG)
# print(self.dig_T1)
# print(self.dig_T2)
# print(self.dig_T3)
# print(self.dig_P1)
# print(self.dig_P2)
# print(self.dig_P3)
# print(self.dig_P4)
# print(self.dig_P5)
# print(self.dig_P6)
# print(self.dig_P7)
# print(self.dig_P8)
# print(self.dig_P9)
def compensate_temperature(self, adc_T):
"""Returns temperature in DegC, double precision. Output value of "1.23"equals 51.23 DegC."""
var1 = ((adc_T) / 16384.0 - (self.dig_T1) / 1024.0) * (self.dig_T2)
var2 = (((adc_T) / 131072.0 - (self.dig_T1) / 8192.0) *
((adc_T) / 131072.0 - (self.dig_T1) / 8192.0)) * (self.dig_T3)
self.t_fine = var1 + var2
temperature = (var1 + var2) / 5120.0
return temperature
def compensate_pressure(self, adc_P):
"""Returns pressure in Pa as double. Output value of "6386.2"equals 96386.2 Pa = 963.862 hPa."""
var1 = (self.t_fine / 2.0) - 64000.0
var2 = var1 * var1 * (self.dig_P6) / 32768.0
var2 = var2 + var1 * (self.dig_P5) * 2.0
var2 = (var2 / 4.0) + ((self.dig_P4) * 65536.0)
var1 = ((self.dig_P3) * var1 * var1 / 524288.0 +
(self.dig_P2) * var1) / 524288.0
var1 = (1.0 + var1 / 32768.0) * (self.dig_P1)
if var1 == 0.0:
return 0 # avoid exception caused by division by zero
pressure = 1048576.0 - adc_P
pressure = (pressure - (var2 / 4096.0)) * 6250.0 / var1
var1 = (self.dig_P9) * pressure * pressure / 2147483648.0
var2 = pressure * (self.dig_P8) / 32768.0
pressure = pressure + (var1 + var2 + (self.dig_P7)) / 16.0
return pressure
def get_temperature_and_pressure(self):
"""Returns pressure in Pa as double. Output value of "6386.2"equals 96386.2 Pa = 963.862 hPa."""
xlsb = self._read_byte(BMP280_TEMP_XLSB_REG)
lsb = self._read_byte(BMP280_TEMP_LSB_REG)
msb = self._read_byte(BMP280_TEMP_MSB_REG)
adc_T = (msb << 12) | (lsb << 4) | (
xlsb >> 4) # temperature registers data
temperature = self.compensate_temperature(
adc_T) # temperature compensate
xlsb = self._read_byte(BMP280_PRESS_XLSB_REG)
lsb = self._read_byte(BMP280_PRESS_LSB_REG)
msb = self._read_byte(BMP280_PRESS_MSB_REG)
adc_P = (msb << 12) | (lsb << 4) | (
xlsb >> 4) # pressure registers data
pressure = self.compensate_pressure(
adc_P) # pressure compensate
return temperature, pressure
if __name__ == '__main__':
import time
print("BMP280 Test Program ...")
bmp280 = BMP180()
while True:
time.sleep(1)
temperature, pressure = bmp280.get_temperature_and_pressure()
print('Temperature = %.2f C Pressure = %.3f kPa' %
(temperature, pressure/1000))保存代码。
效果
终端执行指令 python bmp280.py 运行程序,连续打印温度和压强数据


MQTT
MQTT,即消息队列遥测传输协议 (Message Queuing Telemetry Transport),是一种基于发布/订阅(publish/subscribe)模式的轻量级通讯协议,构建于TCP/IP协议上。
MQTT 以有限的带宽为连接远程设备提供实时可靠的消息服务。MQTT 作为一种即时通讯协议,在物联网、小型设备、移动应用等方面有广泛的应用。
这里介绍了将 BMP280 获取的数据通过 MQTT 协议上传至 EMQX 服务器并远程订阅测试的相关流程,包括关键代码和效果演示。
代码
终端执行指令 touch bmp280_mqtt.py 新建程序文件,并添加如下代码
#!/usr/bin/env python3
import time
import json
import bmp280 # 导入上面的驱动
import paho.mqtt.client as mqtt
# ----------------- 参数 -----------------
MQTT_BROKER = "192.168.1.107" # MQTT 服务器
MQTT_PORT = 1883
MQTT_TOPIC = "rpi/sensor/bmp280"
CLIENT_ID = "rpi_bmp280"
USERNAME = "xxx" # 认证信息
PASSWORD = "xxx"
PUBLISH_INTERVAL = 2 # 秒
# ------------------------------------------------
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("MQTT 已连接")
else:
print("MQTT 连接失败,rc =", rc)
def on_disconnect(client, userdata, rc):
print("MQTT 断开,rc =", rc)
while True:
try:
client.reconnect()
print("重连成功")
break
except Exception as e:
print("重连失败:", e)
time.sleep(5)
client = mqtt.Client(client_id=CLIENT_ID, clean_session=True)
client.username_pw_set(USERNAME, PASSWORD)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.connect(MQTT_BROKER, MQTT_PORT, keepalive=60)
client.loop_start() # 后台线程自动保活
# 初始化传感器
sensor = bmp280.BMP280()
try:
while True:
t, p = sensor.get_temperature_and_pressure()
payload = json.dumps({
"temperature": round(t, 2),
"pressure" : round(p / 1000, 3),
"ts" : int(time.time())
})
print("→", payload)
client.publish(MQTT_TOPIC, payload, qos=1)
time.sleep(PUBLISH_INTERVAL)
except KeyboardInterrupt:
print("用户中断,程序退出")
finally:
client.loop_stop()
client.disconnect()保存代码。
效果
终端执行指令 python bmp280_mqtt.py 运行程序,打印 MQTT 报文信息

消息订阅
浏览器访问 http://192.168.31.107:18083 进入 EMQX 控制终端;
左侧工具栏 - 诊断工具 - WebSocket 客户端
填写 ip 地址、端口、用户名、密码,点击 连接 按钮;
在 订阅 面板中填写主题 rpi/sensor/bmp280 ;

下方 已接收 消息框立刻收到 BMP280 温度和压强的 JSON 报文;

Home Assistant
Home Assistant 是一个开源的智能家居平台,旨在通过集成各种智能设备和服务,提供一个统一的、可自定义的家庭自动化解决方案。
下面介绍 BMP280 温度压强数据通过 MQTT 协议上传至 Home Assistant 平台的相关流程,包括流程图、代码、效果演示等。
流程图

代码
终端执行指令 touch bmp280_mqtt_ha.py 新建程序文件,并添加如下代码
#!/usr/bin/env python3
import time
import json
import bmp280
import paho.mqtt.client as mqtt
import uuid
# ---------- 参数 ----------
BROKER, PORT = "192.168.1.107", 1883
USERNAME = "xxx" # 认证
PASSWORD = "xxx"
NODE_ID = "rpi_bmp280" # 节点唯一标识
SEND_EVERY = 2 # 秒
# ------------------------------
# 生成全局唯一 ID,避免重复
uid = uuid.uuid4().hex[:8]
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("MQTT 已连接")
# 发布自动发现配置
publish_discovery(client)
else:
print("MQTT 连接失败,rc =", rc)
def publish_discovery(client):
"""向 HA 注册两个传感器实体"""
base_topic = f"homeassistant/sensor/{NODE_ID}"
# 温度
temp_cfg = {
"name": "BMP280 Temperature",
"uniq_id": f"{NODE_ID}_temp_{uid}",
"state_topic": f"{base_topic}/temp/state",
"unit_of_meas": "°C",
"device_class": "temperature",
"device": {
"identifiers": [NODE_ID],
"name": "Raspberry Pi BMP280",
"model": "BMP280",
"manufacturer": "Bosch"
}
}
# 气压
pres_cfg = {
"name": "BMP280 Pressure",
"uniq_id": f"{NODE_ID}_pres_{uid}",
"state_topic": f"{base_topic}/pres/state",
"unit_of_meas": "hPa",
"device_class": "pressure",
"device": {
"identifiers": [NODE_ID],
"name": "Raspberry Pi BMP280",
"model": "BMP280",
"manufacturer": "Bosch"
}
}
client.publish(f"{base_topic}/temp/config", json.dumps(temp_cfg), qos=1, retain=True)
client.publish(f"{base_topic}/pres/config", json.dumps(pres_cfg), qos=1, retain=True)
print("→ 自动发现配置已推送")
client = mqtt.Client()
if USERNAME:
client.username_pw_set(USERNAME, PASSWORD)
client.on_connect = on_connect
client.connect(BROKER, PORT, keepalive=60)
client.loop_start()
sensor = bmp280.BMP280()
try:
while True:
t, p = sensor.get_temperature_and_pressure()
# 发布状态
client.publish(f"homeassistant/sensor/{NODE_ID}/temp/state", f"{t:.2f}", qos=1)
client.publish(f"homeassistant/sensor/{NODE_ID}/pres/state", f"{p/100:.2f}", qos=1)
print(f"→ 温度 {t:.2f} °C 气压 {p/100:.2f} hPa")
time.sleep(SEND_EVERY)
except KeyboardInterrupt:
print("用户中断,程序退出")
finally:
client.loop_stop()
client.disconnect()保存代码。
效果
终端执行指令 python bmp280_mqtt_ha.py 运行程序,打印通过 MQTT 上传至 HA 的报文信息
打开 HA 主页,进入 设置 - 设备与服务 - MQTT,点击 Raspberry Pi BMP280 设备选项

点击 添加到仪表盘 链接,选择目标区域,点击添加按钮;

进入 概览 标签页,可看到已添加的 BMP280 传感器卡片;

点击卡片,获取历史数据

总结
本文介绍了树莓派 Zero 2W 驱动 BMP280 传感器获取环境温度压强数据,并通过 MQTT 协议上传至 Home Assistant 智能家居平台的物联网项目设计,为相关产品在物联网 IoT 领域的快速开发和应用设计提供了参考。
我要赚赏金
