【上海晶珩睿莓 1 单板计算机】物联网环境监测终端
本文介绍了睿莓 1 单板计算机结合 AHT20 和 BMP280 传感器模块实现实现环境温湿度和气压监测,并通过 MQTT 协议上传至 Home Assistant 智能家居平台,实现物联网环境监测的项目设计。
项目介绍
- 准备工作:硬件连接、软件包安装、Docker 部署、EMQX 和 HomeAssistant平台部署;
- 工程测试:加载官方 Demo 例程,实现传感器数据终端打印;
- MQTT:结合 MQTT 协议,实现传感器数据上传至 EMQX 服务器平台,并远程读取;
- Home Assistant:通过 MQTT 集成,实现传感器数据卡片自动获取,实现物联网环境气体监测。
准备工作
包括硬件连接、软件包安装、Home Assistant 平台搭建等。
硬件连接
将 AHT20 和 BMP280 传感器模块连接至端口 /dev/i2c-1 ;
| AHT20 & BMP280 | 睿莓1 | Note |
|---|---|---|
| SDA | SDA (3) | Serial Data |
| SCL | SCL (5) | Serial Clock |
| GND | GND (6) | Ground |
| VCC | 3.3V (1) | Power |
实物图

40-Pin 接口定义

IIC 软件包
终端执行指令
bash
sudo apt install python3-smbus2 python3-smbus python3-paho-mqtt
物联网平台
通过 Docker 搭建本地物联网平台。
Docker
电脑主机安装 Docker 软件;
bash
sudo apt-get update && sudo apt-get upgrade
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
docker version
拉取 EMQX 和 Home Assistant 最新镜像;
bash
sudo docker pull emqx/emqx:latest
sudo docker pull homeassistant/home-assistant:latest
启动 EMQX 和 HA 容器;
bash
docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 emqx/emqx:latest
docker run -d --restart always --name homeassistant -v /data/homeassistant/config:/config -e TZ=Asia/Shanghai -p 8123:8123 homeassistant/home-assistant:latest
MQTT 客户端
EMQX 创建 MQTT 客户端
-
进入 EMQX 主页,如
http://192.168.31.116:18083; -
依次打开
访问控制-客户端认证-创建-Password-Based-内置数据库- (默认配置)-创建;
创建用户
用户管理-新建用户- 自定义用户名和密码 - 保存.
HA 配置
- 浏览器输入网址
http://<IP>:8123,如192.168.1.107:8123 - 进入 HA 主界面(首次打开需进行注册),输入用户名、密码等信息;
- 配置完成后进入 HA
概览标签页;
工程测试
包括 AHT10和BMP280模块IIC通信测试、SEN66模块驱动测试等。
AHT10 & BMP280
终端执行指令 touch aht20_bmp280_print.py 新建程序文件并添加如下代码
python
from aht10 import AHT10
from bmp280 import BMP280
import time
while True:
sensor = AHT10(bus=0, address=0x38)
temp, hum = sensor.read()
bmp280 = BMP280(bus=0, address=0x77)
temperature, pressure = bmp280.get_temperature_and_pressure()
print("Temperature: {:.2f} °C Humidity: {:.2f} %RH Pressure: {:.3f} kPa".format(temp, hum, pressure/1000))
time.sleep(2)
AHT10 驱动
python
"""
AHT10温湿度传感器驱动
"""
import smbus2
import time
class AHT10:
def __init__(self, bus=1, address=0x38):
self.bus = smbus2.SMBus(bus)
self.addr = address
self._initialize()
time.sleep(0.5) # 延长初始化等待时间
def _initialize(self):
"""初始化AHT10,发送0xE10800命令"""
try:
self.bus.write_i2c_block_data(self.addr, 0xE1, [0x08, 0x00])
return True
except:
return False
def read(self):
"""读取一次温湿度测量"""
# 触发测量 0xAC3300
self.bus.write_i2c_block_data(self.addr, 0xAC, [0x33, 0x00])
time.sleep(0.08) # 等待测量完成
# 读取6字节数据
data = self.bus.read_i2c_block_data(self.addr, 0x00, 6)
# 解析数据
hum_raw = ((data[1] << 12) | (data[2] << 4) | (data[3] >> 4))
temp_raw = (((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5])
humidity = hum_raw * 100 / (1 << 20) # 2^20 = 1048576
temperature = temp_raw * 200 / (1 << 20) - 50
return round(temperature, 1), round(humidity, 1)
# 使用示例
def demo(interval=1):
"""连续打印温湿度,interval:采样间隔(秒)"""
sensor = AHT10(bus=0, address=0x38)
while True:
temp, hum = sensor.read()
print(f"Temperature: {temp}°C, Humidity: {hum}%RH")
time.sleep(interval)
BMP280 驱动
python
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 BMP280(object):
def __init__(self, bus=1, address=BMP280_I2C_ADDRESS):
self._address = address
self._bus = smbus.SMBus(bus) # 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;
# 修复:使用正常的测量模式而不是 0xFF(0xFF 会使传感器进入强制模式但可能不稳定)
# 0x27 表示:温度过采样x1,压力过采样x1,正常模式
ctrlmeas = 0x27
# 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)
# 等待传感器稳定
time.sleep(0.01)
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
def demo(interval=1):
"""连续打印温度压强,interval:采样间隔(秒)"""
bmp280 = BMP280(bus=4, address=0x77)
while True:
time.sleep(1)
temperature, pressure = bmp280.get_temperature_and_pressure()
print('Temperature = %.2f C Pressure = %.3f kPa' %
(temperature, pressure/1000))
效果
-
终端执行指令
python3 aht20_bmp280_print.py运行程序; -
连续打印温湿度和气压数据;

MQTT
通过 MQTT 协议实现传感器数据上传,并使用 EMQX 的 WebSocket 工具接收 JSON 报文。
流程图
异常
Ctrl-C
开始
加载环境
扫描IIC设备
初始化传感器
启动连续测量
连接MQTT
发布HA配置
主循环
读取3个参量
提取数据
封装JSON
发布
打印日志
停止测量
断开MQTT
退出
代码
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
温湿度 + 气压 → MQTT → Home Assistant
传感器:AHT10 + BMP280(I²C 总线 0)
"""
import json
import time
from aht10 import AHT10
from bmp280 import BMP280
import paho.mqtt.client as mqtt
# =============== 参数 ==============
MQTT_BROKER = "192.168.1.107" # EMQX IP
MQTT_PORT = 1883
MQTT_USER = "xxx"
MQTT_PASS = "xxx"
NODE_ID = "bosch" # 设备节点 ID
IIC_BUS = 0 # i2c bus 0
# ===================================
# HA 自动发现主题模板
DISCOVERY_PREFIX = "homeassistant"
# 传感器配置
SENSORS = {
"temp": {"name": "Temperature", "unit": "°C", "dev_cla": "temperature", "ic": "mdi:thermometer"},
"hum": {"name": "Humidity", "unit": "%", "dev_cla": "humidity", "ic": "mdi:water-percent"},
"pres": {"name": "Pressure", "unit": "hPa", "dev_cla": "pressure", "ic": "mdi:gauge"},
}
def publish_discovery(client):
"""向 HA 发送自动发现配置,只需一次即可"""
for key, cfg in SENSORS.items():
topic = f"{DISCOVERY_PREFIX}/sensor/{NODE_ID}/{key}/config"
payload = {
"name": cfg["name"],
"unit_of_measurement": cfg["unit"],
"device_class": cfg["dev_cla"],
"icon": cfg["ic"],
"state_topic": f"{DISCOVERY_PREFIX}/sensor/{NODE_ID}/{key}/state",
"unique_id": f"{NODE_ID}_{key}",
"device": {
"identifiers": [NODE_ID],
"name": "Sensors",
"model": "AHT20_BMP280",
"manufacturer": "Bosch",
},
}
client.publish(topic, json.dumps(payload), retain=True)
def main():
client = mqtt.Client()
client.username_pw_set(MQTT_USER, MQTT_PASS)
client.connect(MQTT_BROKER, MQTT_PORT, 60)
client.loop_start()
# 上电后先发送一次发现配置
publish_discovery(client)
# 传感器初始化
aht = AHT10(bus=IIC_BUS, address=0x38)
bmp = BMP280(bus=IIC_BUS, address=0x77)
while True:
try:
temp, hum = aht.read()
_, pressure = bmp.get_temperature_and_pressure() # 温度 AHT10 采集
pressure_hpa = pressure * 10 # kPa → hPa
print(f"Temp: {temp:.2f} °C Hum: {hum:.2f} % Pres: {pressure_hpa:.2f} hPa")
client.publish(f"{DISCOVERY_PREFIX}/sensor/{NODE_ID}/temp/state", f"{temp:.2f}")
client.publish(f"{DISCOVERY_PREFIX}/sensor/{NODE_ID}/hum/state", f"{hum:.2f}")
client.publish(f"{DISCOVERY_PREFIX}/sensor/{NODE_ID}/pres/state", f"{pressure_hpa:.2f}")
except Exception as e:
print("读取/发送失败:", e)
time.sleep(2) # 2 s 上报一次
if __name__ == "__main__":
main()
保存代码。
测试
-
终端执行
python aht20_bmp280_mqtt.py运行程序; -
打印 JSON 格式报文;

EMQX 测试
- 进入 EMQX 网页控制界面,打开侧边标签栏,选择
诊断工具-WebSocket 客户端; - 输入用户名和密码,连接 MQTT 服务器;
- 在订阅版块输入目标传感器主题,如
homeassistant/sensor/bosch/temp/state,点击订阅按钮; - 在已接收版块可看到传感器数据,两秒更新一次;

Home Assistant
通过 MQTT 协议上传 SEN66 传感器数据至 Home Assistant 平台,实现远程 IoT 数据实时监测。
参数配置
打开 Home Assistant 网页界面 http://192.168.31.118:8123 ,在 设置 - 设备与服务 标签页添加 MQTT 集成;
- 输入 EMQX 客户端信息,刷新页面,可看到新增的传感器;

- 点击 SEN66 传感器,进入详情页面,获取设备信息;

- 点击编辑按钮,配置所在区域等信息,并添加至仪表盘;
效果
进入 概览 页面,刷新网页,可获得实时传感器数据

- 点击目标传感器,可获得历史数据及演化曲线

总结
本文介绍了睿莓 1 单板计算机结合 AHT20 和 BMP280 传感器模块实现环境温湿度及气压监测,并通过 MQTT 协议上传至 Home Assistant 智能家居平台,实现物联网环境监测终端的项目设计,为相关产品在物联网领域的快速开发和应用设计提供了参考。