1. 项目概述
这个项目基于STM32L151RCT6微控制器,整合了BC20通信模块、DHT11温湿度传感器和GPS定位模块,实现了环境数据采集并通过ADC转换后上传至最新版OneNET物联网平台。整套系统适用于户外环境监测、资产追踪等场景,特别适合需要低功耗运行的远程监测应用。
STM32L151RCT6作为Cortex-M3内核的低功耗MCU,与BC20 NB-IoT模块的搭配堪称完美组合。DHT11虽然精度一般但成本低廉,GPS模块提供位置信息,ADC则扩展了模拟量采集能力。将这些数据统一上传到OneNET平台后,用户可以通过可视化界面实时监控设备状态和环境参数。
2. 硬件选型与连接
2.1 核心器件介绍
STM32L151RCT6:
- 32位ARM Cortex-M3内核,主频32MHz
- 256KB Flash + 32KB RAM
- 超低功耗特性:运行模式低至214μA/MHz
- 丰富外设:USART、I2C、SPI、ADC等
BC20 NB-IoT模块:
- 支持NB-IoT和GNSS定位
- 低功耗设计,PSM模式下电流仅1μA
- 内置TCP/IP协议栈
- 支持MQTT、HTTP等物联网协议
DHT11温湿度传感器:
- 温度测量范围:0-50℃ ±2℃
- 湿度测量范围:20-90%RH ±5%
- 单总线数字接口
- 采样周期≥1秒
2.2 硬件连接方案
code复制STM32L151RCT6 硬件连接:
PA2/USART2_TX -> BC20_RX
PA3/USART2_RX -> BC20_TX
PB6/I2C1_SCL -> GPS_SCL
PB7/I2C1_SDA -> GPS_SDA
PC0/ADC1_IN10 -> 模拟量输入
PC1 -> DHT11数据线
注意:BC20模块需要单独供电(3.7V锂电池),并通过电平转换电路与STM32连接。GPS模块建议使用有源天线以提高定位精度。
3. 软件设计与实现
3.1 开发环境搭建
-
安装Keil MDK-ARM开发环境
-
添加STM32L1系列设备支持包
-
配置STM32CubeMX生成基础工程:
- 时钟树配置:HSI 16MHz作为主时钟源
- 外设初始化:
- USART2 115200bps用于BC20通信
- I2C1 100kHz用于GPS模块
- ADC1 12位分辨率,通道10
- GPIO PC1推挽输出用于DHT11
-
添加必要的库文件:
- DHT11驱动库
- BC20 AT指令封装库
- OneNET MQTT协议库
3.2 数据采集实现
DHT11温湿度采集代码片段:
c复制#define DHT11_PIN GPIO_PIN_1
#define DHT11_PORT GPIOC
void DHT11_Start(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置为输出模式
GPIO_InitStruct.Pin = DHT11_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
// 发送开始信号
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET);
HAL_Delay(18);
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET);
// 切换为输入模式
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
}
uint8_t DHT11_ReadByte(void) {
uint8_t data = 0;
for(int i=0; i<8; i++) {
while(!HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)); // 等待低电平
uint32_t start = HAL_GetTick();
while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)); // 等待高电平
if((HAL_GetTick() - start) > 40) {
data |= (1 << (7-i));
}
}
return data;
}
GPS数据解析关键点:
- 通过I2C读取GPS模块原始NMEA数据
- 解析GPRMC语句获取经纬度、速度等信息
- 坐标转换:将度分格式转换为十进制格式
c复制void GPS_Parse(char* gpsData) {
if(strstr(gpsData, "$GPRMC")) {
char* p = strtok(gpsData, ",");
int field = 0;
while(p != NULL) {
switch(field) {
case 3: // 纬度
gps.latitude = atof(p) / 100.0;
break;
case 4: // 纬度半球
gps.lat_dir = *p;
break;
case 5: // 经度
gps.longitude = atof(p) / 100.0;
break;
// 其他字段解析...
}
p = strtok(NULL, ",");
field++;
}
}
}
3.3 BC20通信实现
BC20初始化流程:
- 发送AT指令检查模块状态
- 配置NB-IoT网络参数
- 激活PDP上下文
- 连接MQTT服务器
c复制void BC20_Init(void) {
BC20_SendCommand("AT", 1000);
BC20_SendCommand("AT+QCFG=\"nwscanseq\",01,1", 1000);
BC20_SendCommand("AT+QCFG=\"nwscanmode\",3,1", 1000);
BC20_SendCommand("AT+QICSGP=1,1,\"CMNB\",\"\",\"\",1", 1000);
BC20_SendCommand("AT+QIACT=1", 30000);
// MQTT连接
char mqttCmd[128];
sprintf(mqttCmd, "AT+QMTOPEN=1,\"%s\",%d", ONENET_SERVER, ONENET_PORT);
BC20_SendCommand(mqttCmd, 5000);
sprintf(mqttCmd, "AT+QMTCONN=1,\"%s\"", DEVICE_ID);
BC20_SendCommand(mqttCmd, 5000);
}
4. OneNET平台对接
4.1 设备接入配置
-
在OneNET平台创建产品:
- 选择NB-IoT接入方式
- 添加数据流:temperature, humidity, location, adc_value
- 记录产品ID和API Key
-
设备端MQTT主题配置:
- 发布主题:$sys/{pid}/{devname}/dp/post/json
- 订阅主题:$sys/{pid}/{devname}/dp/post/json/+
-
数据格式示例:
json复制{
"id": 123,
"dp": {
"temperature": [{"v": 25.5}],
"humidity": [{"v": 65.2}],
"location": [{"v": {"lon": 116.397, "lat": 39.908}}],
"adc_value": [{"v": 1.23}]
}
}
4.2 数据上传实现
c复制void OneNET_UploadData(float temp, float humi, GPS_Data gps, float adc) {
char json[256];
sprintf(json,
"{\"id\":%d,\"dp\":{"
"\"temperature\":[{\"v\":%.1f}],"
"\"humidity\":[{\"v\":%.1f}],"
"\"location\":[{\"v\":{\"lon\":%.6f,\"lat\":%.6f}}],"
"\"adc_value\":[{\"v\":%.2f}]"
"}}",
msg_id++, temp, humi, gps.longitude, gps.latitude, adc);
char mqttCmd[300];
sprintf(mqttCmd, "AT+QMTPUB=1,0,0,0,\"$sys/%s/%s/dp/post/json\"",
ONENET_PID, DEVICE_ID);
BC20_SendCommand(mqttCmd, 1000);
BC20_SendData(json, strlen(json));
}
5. 低功耗优化策略
5.1 STM32低功耗配置
-
时钟配置优化:
- 使用MSI内部时钟源替代HSI
- 降低主频至2MHz(运行模式)
- 关闭未使用的外设时钟
-
电源模式选择:
- 采集间隔>10s:使用STOP模式
- 采集间隔>1min:使用STANDBY模式
- 通过RTC或外部中断唤醒
-
代码优化:
c复制void Enter_LowPowerMode(void) {
// 关闭外设
HAL_ADC_DeInit(&hadc1);
HAL_UART_DeInit(&huart2);
// 配置唤醒源
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
// 进入STOP模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后重新初始化
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
}
5.2 BC20功耗管理
-
PSM模式配置:
- 发送AT+QPSM=1启用PSM
- 设置AT+QPSMEXT="00000110","00000010"(Active Time=30s, Periodic TAU=2h)
-
eDRX模式配置(可选):
- AT+CEDRXS=1,5,"0101"(eDRX周期=20.48s)
-
发送策略优化:
- 数据缓存,达到阈值或超时后批量发送
- 采用QoS0级别消息减少确认等待
6. 常见问题与解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| DHT11读取失败 | 时序不准确/接线错误 | 检查接线,调整延时时间,增加重试机制 |
| BC20无法注册网络 | SIM卡问题/信号弱 | 检查SIM卡状态,AT+CSQ查看信号强度(>10) |
| GPS定位时间长 | 冷启动/天线问题 | 确保室外环境,使用有源天线,启用AGPS |
| OneNET数据不上传 | MQTT连接断开 | 增加心跳包(AT+QMTCONN),检查设备鉴权信息 |
| 系统功耗偏高 | 未进入低功耗模式 | 检查所有外设电源状态,确认PSM模式生效 |
实际调试中发现,BC20模块在弱信号环境下可能频繁掉线。建议在代码中加入网络状态监测和自动重连机制:
c复制void BC20_CheckConnection(void) {
if(HAL_GetTick() - lastCheckTime > CHECK_INTERVAL) {
char resp[64];
if(BC20_SendCommandWithResponse("AT+QMTCONN?", "+QMTCONN: 1,0", resp, 1000) != 0) {
BC20_Init(); // 重新初始化连接
}
lastCheckTime = HAL_GetTick();
}
}
7. 项目优化方向
-
传感器升级:
- 替换DHT11为SHT30提高精度
- 增加大气压力传感器BMP280
-
边缘计算:
- 在STM32端实现简单阈值判断
- 数据异常时立即上报
-
固件远程升级(FOTA):
- 通过OneNET平台推送更新包
- BC20支持HTTP分片下载
-
电源管理:
- 增加太阳能充电电路
- 实现电池电量监测和低电量预警
-
数据安全:
- 启用MQTT TLS加密
- 增加数据签名验证
这套系统在实际部署中表现稳定,平均功耗可控制在200μA以下(1小时上报一次)。对于需要长期户外监测的场景,建议搭配10000mAh锂电池,可支持超过1年的持续工作。GPS模块的定位精度在城市环境中约5-10米,完全满足大多数物联网应用的需求。