大家好!在前 25 篇中,我们从 STM32 基础外设、通信协议、RTOS 开发到物联网健壮性设计,逐步掌握了完整的技术栈。但 “零散技术” 需要通过 “完整项目” 串联,才能真正落地为可使用的产品。这一篇我们将以 “智能环境监测站” 为目标,整合所有核心技术,从 “硬件选型与电路设计”“软件架构搭建”“云平台可视化部署” 三个维度,实现一个 “能采集、能传输、能存储、能远程监控、能故障自恢复” 的工业级设备,让你完成从 “技术学习者” 到 “项目开发者” 的跨越。
一、项目定位与核心功能:什么是智能环境监测站?
智能环境监测站是一款面向 “室内 / 户外环境监控” 的物联网设备,核心价值是 “实时采集关键环境参数,远程可视化监控,异常自动报警”,典型应用场景包括:
工业车间:监测温湿度、粉尘浓度,避免设备因高湿高温损坏;农业大棚:监测温湿度、光照强度,联动控制通风 / 灌溉设备;户外站点:监测温湿度、大气压力,数据长期存储用于环境分析。
本项目的核心功能需覆盖 “数据采集→处理→传输→存储→监控→报警” 全链路,具体如下:
多参数采集:温湿度(SHT30)、光照强度(BH1750)、大气压力(BMP280),采样间隔可远程配置(1~60 分钟);双模式传输:NB-IoT(广域,传至阿里云)+ 蓝牙 BLE(本地,手机近场调试),兼顾远程监控与现场维护;分级存储:
实时数据:优先上传阿里云,断网时缓存至 SPI Flash(W25Q128);历史数据:本地存储 3 个月数据,支持蓝牙本地导出;配置参数:存储至 STM32 内部 Flash,断电不丢失;
远程监控:阿里云平台实现 “数据仪表盘可视化、历史曲线查询、设备在线状态监控”;异常报警:温湿度 / 光照超阈值、设备低电量、传感器故障时,自动向云端 / 手机推送报警信息;低功耗续航:采用 18650 锂电池供电,平均功耗≤1mA,满电续航≥6 个月(户外无需频繁换电)。
二、硬件设计:从选型到电路落地
硬件是项目的 “骨架”,需兼顾 “功能完整性、稳定性、低功耗、成本控制”,核心分为 “核心控制模块”“传感器模块”“通信模块”“电源模块”“存储模块” 五大部分。
1. 硬件选型清单(性价比优先)
| 模块类型 | 选型型号 | 核心参数 | 作用说明 |
|---|---|---|---|
| 核心控制器 | STM32F103C8T6 | 72MHz 主频,64KB Flash,20KB RAM | 统筹所有模块,执行采集、传输、存储、控制逻辑 |
| 温湿度传感器 | SHT30 | 精度 ±2% RH,±0.3℃,I2C 接口 | 采集环境温湿度 |
| 光照传感器 | BH1750 | 精度 ±20%,量程 1~65535 lx,I2C 接口 | 采集环境光照强度 |
| 压力传感器 | BMP280 | 精度 ±1hPa,I2C/SPI 接口 | 采集大气压力,辅助判断天气变化 |
| 广域通信模块 | 移远 BC26(NB-IoT) | 支持中国三大运营商 NB 网络,UART 接口 | 远程传输数据至阿里云,接收云端控制指令 |
| 本地通信模块 | HC-08(蓝牙 BLE) | 从模式,UART 接口,待机功耗 10μA | 本地调试(修改配置、导出历史数据),无需拆设备 |
| 外部存储 | W25Q128(SPI Flash) | 128MB 容量,擦写寿命 10 万次 | 缓存断网数据、存储历史数据 |
| 电源模块 | 18650 锂电池(3.7V/2000mAh)+ AXP192(电源管理) | 支持充电、过压 / 过流保护、低功耗休眠 | 为所有模块供电,实现低功耗控制(如关闭闲置传感器电源) |
| 指示模块 | LED 指示灯(3 个)+ 蜂鸣器 | – | LED:电源(红)、在线(绿)、报警(黄);蜂鸣器:本地异常报警 |
2. 核心电路设计(关键部分)
电路设计需遵循 “抗干扰、低功耗、易维护” 原则,核心电路图与设计要点如下:
(1)电源管理电路(AXP192)
功能:将 18650 锂电池(3.7~4.2V)稳压至 3.3V,为 STM32、传感器、通信模块供电;低功耗设计:
传感器供电端通过 STM32 GPIO 控制(如 PA4 控制 SHT30 电源),采样时上电,采样后断电,降低待机功耗;AXP192 支持 “休眠模式”,STM32 进入停止模式时,触发 AXP192 关闭非必要电源轨(如蓝牙模块);
保护设计:内置过压(>4.3V)、过流(>500mA)、过温(>85℃)保护,避免电池损坏。
(2)传感器接口电路(I2C 总线复用)
复用设计:SHT30、BH1750、BMP280 均为 I2C 接口,通过 “不同地址 + GPIO 片选” 实现复用(避免地址冲突):
SHT30 地址:0x44,片选引脚 PB0(高电平使能);BH1750 地址:0x23,片选引脚 PB1(高电平使能);BMP280 地址:0x76,片选引脚 PB2(高电平使能);
抗干扰设计:I2C 总线(SCL/PB6、SDA/PB7)串联 100Ω 限流电阻,并联 0.1μF 电容,减少传输干扰。
(3)通信模块电路(NB-IoT+BLE)
NB-IoT(BC26):
供电:独立 3.3V 电源(AXP192 单独供电轨),避免与其他模块抢电流;控制:PWR_KEY 引脚(PB3)用于模块开关机,RESET 引脚(PB4)用于异常复位;
蓝牙 BLE(HC-08):
供电:GPIO 控制(PA3),仅调试时上电,正常运行时断电,降低功耗;
串口复用:BC26 与 HC-08 共用 USART1(PA9/TX、PA10/RX),通过 GPIO 片选(PA2 控制 HC-08 使能,PA1 控制 BC26 使能),同一时间仅一个模块工作。
3. PCB 布局要点(抗干扰与低功耗)
分区布局:按 “数字区(STM32、SPI Flash)、模拟区(传感器)、射频区(NB/BLE 天线)” 分区,避免模拟信号被干扰;天线设计:BC26 的天线座远离电源电路(≥5mm),避免电源噪声影响射频信号;电源布线:3.3V 主电源走线宽度≥1mm,AXP192 到 BC26 的电源线宽度≥0.8mm,减少压降;接地处理:所有模块的 GND 通过 “单点接地” 连接到电源地,避免地环流干扰。
三、软件架构:分层设计与核心模块实现
软件是项目的 “大脑”,需采用 “分层架构”(驱动层→中间件层→应用层),降低耦合度,便于后期维护与功能扩展,架构图与核心模块实现如下:
1. 软件分层架构
| 层级 | 核心模块 | 功能说明 | 依赖技术 |
|---|---|---|---|
| 驱动层(Driver) | GPIO、UART、I2C、SPI、ADC、RTC、DMA | 底层硬件驱动,提供标准化接口(如),屏蔽硬件差异 |
STM32 HAL 库 |
| 中间件层(Middleware) | 传感器驱动(SHT30/BH1750/BMP280)、通信驱动(BC26/HC-08)、存储驱动(W25Q128 / 内部 Flash)、FreeRTOS、FatFS、cJSON | 封装核心功能,提供应用层调用接口(如) |
第 5~25 篇所有核心技术 |
| 应用层(Application) | 数据采集任务、数据传输任务、数据存储任务、远程控制任务、故障检测任务、蓝牙调试任务 | 实现业务逻辑,任务间通过队列 / 信号量通信,确保并发与同步 | FreeRTOS 任务调度、同步机制 |
| 云交互层(Cloud) | 阿里云 MQTT 通信、指令解析、数据上报、报警推送 | 处理与阿里云的双向交互,包括数据封装、指令响应、报警上报 | MQTT 协议、JSON 解析、健壮性设计 |
2. 核心任务设计(FreeRTOS 任务调度)
应用层基于 FreeRTOS 实现多任务并发,需合理分配优先级(高优先级处理紧急任务),核心任务清单如下:
| 任务名称 | 优先级 | 栈大小 | 周期 / 触发方式 | 核心功能 |
|---|---|---|---|---|
| 故障检测任务 | 5 | 512 | 10 秒周期 | 检测传感器异常、电池低电量、通信模块故障,触发报警(蜂鸣器 + 云端上报) |
| 数据采集任务 | 4 | 512 | 配置的采样间隔(1~60 分钟) | 按间隔唤醒传感器,采集温湿度、光照、压力,数据存入 “采集队列” |
| 数据传输任务 | 3 | 1024 | 事件触发(采集完成 / 网络恢复) | 从采集队列取数据,优先通过 NB-IoT 上传阿里云;断网时缓存至 SPI Flash |
| 远程控制任务 | 3 | 512 | 中断触发(NB-IoT/BLE 收到指令) | 解析云端 / BLE 下发的指令(如修改采样间隔、导出历史数据),执行并反馈结果 |
| 数据存储任务 | 2 | 512 | 事件触发(数据缓存 / 导出请求) | 管理 SPI Flash 存储:缓存数据循环覆盖、响应蓝牙导出请求、定期清理过期数据 |
| 蓝牙调试任务 | 1 | 512 | 手动触发(蓝牙连接) | 响应手机 BLE 调试指令(读取配置、导出历史数据、手动校准传感器) |
| 空闲任务(FreeRTOS) | 0 | 256 | 无(空闲时运行) | 执行低功耗操作(如进入停止模式)、内存碎片整理 |
3. 关键功能实现(代码片段)
(1)多传感器数据采集(应用层)
#include "sensor.h"
#include "freertos.h"
#include "task.h"
#include "queue.h"
// 数据采集队列(存储采集到的环境参数)
QueueHandle_t xDataQueue;
// 传感器采集任务
void vTaskSensorCollect(void *argument)
{
Sensor_ParamsTypeDef xSensorData; // 存储所有传感器参数
uint32_t ulSampleInterval = 30 * 60 * 1000; // 初始采样间隔30分钟(毫秒)
TickType_t xLastWakeTime = xTaskGetTickCount();
// 1. 初始化队列(队列长度10,每个元素为Sensor_ParamsTypeDef)
xDataQueue = xQueueCreate(10, sizeof(Sensor_ParamsTypeDef));
while (1)
{
// 2. 读取当前采样间隔(可被远程控制任务修改)
ulSampleInterval = g_SampleInterval * 60 * 1000;
// 3. 依次唤醒并采集传感器数据
// (1)温湿度(SHT30)
Sensor_SHT30_PowerOn(); // GPIO控制上电
HAL_Delay(100); // 等待传感器就绪
if (Sensor_SHT30_Read(&xSensorData.fTemp, &xSensorData.fHumi) != 0)
{
xSensorData.ucFault |= 0x01; // 标记SHT30故障
}
Sensor_SHT30_PowerOff(); // 断电节能
// (2)光照强度(BH1750)
Sensor_BH1750_PowerOn();
HAL_Delay(50);
if (Sensor_BH1750_Read(&xSensorData.fLight) != 0)
{
xSensorData.ucFault |= 0x02; // 标记BH1750故障
}
Sensor_BH1750_PowerOff();
// (3)大气压力(BMP280)
Sensor_BMP280_PowerOn();
HAL_Delay(50);
if (Sensor_BMP280_Read(&xSensorData.fPressure) != 0)
{
xSensorData.ucFault |= 0x04; // 标记BMP280故障
}
Sensor_BMP280_PowerOff();
// 4. 添加时间戳(RTC获取)
xSensorData.ulTimestamp = RTC_GetTimestamp();
// 5. 发送数据到采集队列,触发传输任务
if (xQueueSend(xDataQueue, &xSensorData, 100) != pdPASS)
{
// 队列满,直接缓存到SPI Flash
Storage_SaveSensorData(&xSensorData);
}
// 6. 故障检测:若有传感器故障,触发报警任务
if (xSensorData.ucFault != 0)
{
xTaskNotifyGive(xHandleFaultTask); // 通知故障任务
}
// 7. 按采样间隔休眠
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(ulSampleInterval));
}
}
(2)阿里云数据上报与指令接收(云交互层)
#include "aliyun_mqtt.h"
#include "cJSON.h"
// 阿里云设备三元组(替换为实际设备信息)
#define PRODUCT_KEY "a1XXXXXXXXX"
#define DEVICE_NAME "EnvMonitor_001"
#define DEVICE_SECRET "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
// 数据上报:封装传感器参数为阿里云物模型格式
void Aliyun_UploadSensorData(Sensor_ParamsTypeDef *pxData)
{
cJSON *pRoot = cJSON_CreateObject();
cJSON *pParams = cJSON_CreateObject();
char pcPayload[256] = {0};
// 1. 封装物模型参数(与阿里云物模型字段一致)
cJSON_AddNumberToObject(pParams, "Temperature", pxData->fTemp);
cJSON_AddNumberToObject(pParams, "Humidity", pxData->fHumi);
cJSON_AddNumberToObject(pParams, "LightIntensity", pxData->fLight);
cJSON_AddNumberToObject(pParams, "AtmPressure", pxData->fPressure);
cJSON_AddNumberToObject(pParams, "BatteryVoltage", pxData->fBatVolt);
cJSON_AddItemToObject(pRoot, "params", pParams);
// 2. 转换为JSON字符串
cJSON_PrintPreallocated(pRoot, pcPayload, sizeof(pcPayload), 0);
cJSON_Delete(pRoot);
// 3. 发送到阿里云数据上报主题
Aliyun_MQTT_Publish("/sys/"PRODUCT_KEY"/"DEVICE_NAME"/thing/event/property/post",
pcPayload, strlen(pcPayload), 0);
printf("阿里云上报数据:%s
", pcPayload);
}
// 指令接收:解析阿里云下发的控制指令
void Aliyun_ParseControlCmd(char *pcCmd)
{
cJSON *pRoot = cJSON_Parse(pcCmd);
cJSON *pParams = NULL;
cJSON *pItem = NULL;
if (pRoot == NULL) return;
// 1. 提取params节点
pParams = cJSON_GetObjectItem(pRoot, "params");
if (pParams == NULL) goto end_parse;
// 2. 解析“采样间隔”指令
pItem = cJSON_GetObjectItem(pParams, "SampleInterval");
if (pItem != NULL)
{
uint32_t ulNewInterval = pItem->valueint;
if (ulNewInterval >= 1 && ulNewInterval <= 60) // 限制1~60分钟
{
g_SampleInterval = ulNewInterval;
Storage_SaveConfig(); // 保存到内部Flash
Aliyun_ReportCmdResult(200, "SampleInterval updated", "OK"); // 反馈结果
printf("采样间隔修改为%d分钟
", ulNewInterval);
}
else
{
Aliyun_ReportCmdResult(500, "SampleInterval out of range", "Failed");
}
}
// 3. 解析“手动采集”指令
pItem = cJSON_GetObjectItem(pParams, "ManualCollect");
if (pItem != NULL && strcmp(pItem->valuestring, "ON") == 0)
{
xTaskNotifyGive(xHandleSensorTask); // 通知采集任务立即采集
Aliyun_ReportCmdResult(200, "Manual collect triggered", "OK");
}
end_parse:
cJSON_Delete(pRoot);
}
四、云平台部署:阿里云可视化与报警配置
硬件与软件就绪后,需通过 “云平台” 实现 “数据可视化” 与 “报警推送”,让用户能直观监控设备状态,核心步骤如下:
1. 阿里云物模型创建
登录阿里云 IoT 平台,创建 “产品”:
产品名称:“智能环境监测站”,产品类型:“环境监测”,通信方式:“NB-IoT”,数据格式:“JSON”;
定义 “物模型属性”(与设备上报字段一致):
| 属性名称 | 标识符 | 数据类型 | 单位 | 取值范围 | 读写类型 |
|---|---|---|---|---|---|
| 温度 | Temperature | float | ℃ | -40~85 | 只读 |
| 湿度 | Humidity | float | %RH | 0~100 | 只读 |
| 光照强度 | LightIntensity | float | lx | 0~65535 | 只读 |
| 大气压力 | AtmPressure | float | hPa | 300~1100 | 只读 |
| 电池电压 | BatteryVoltage | float | V | 2.8~4.2 | 只读 |
| 采样间隔 | SampleInterval | int | 分钟 | 1~60 | 读写 |
| 设备故障状态 | DeviceFault | int | – | 0 = 正常,1 = 故障 | 只读 |
定义 “服务指令”(用于远程控制):
指令名称:“ManualCollect”,参数:“ManualCollect”(string,取值 “ON”),功能:触发手动采集;
定义 “事件报警”:
事件名称:“OverThresholdAlarm”,参数:“AlarmType”(int,1 = 高温,2 = 高湿,3 = 低电量),触发方式:设备上报时自动检测阈值。
2. 数据可视化配置
进入阿里云 IoT 平台 “监控运维→数据可视化”,创建 “仪表盘”;添加 “组件” 实现多维度展示:
数值卡片:实时显示温湿度、光照、压力、电池电压,超出阈值时显示红色;折线图:展示 7 天内温湿度 / 光照变化曲线,支持缩放查看细节;设备状态:显示设备在线 / 离线状态(绿色 = 在线,灰色 = 离线),离线时闪烁报警;故障日志:列表展示历史报警记录,包含 “报警类型、时间、处理状态”。
3. 报警推送配置
进入 “监控运维→报警规则”,创建报警策略:
触发条件:温度 > 35℃或 <5℃、湿度> 80% 或 < 20%、电池电压 < 3.0V、设备离线 > 1 小时;推送方式:短信(发送至管理员手机)、钉钉机器人(推送至运维群)、邮件(发送至运维邮箱);推送内容:包含 “设备名称、报警类型、当前值、时间、设备位置”,便于快速定位。
五、项目测试与优化:从实验室到现场
完整项目需经过 “实验室测试→现场测试→迭代优化” 三个阶段,才能确保稳定性,核心测试点与优化方向如下:
1. 核心测试点
| 测试类型 | 测试方法 | 合格标准 |
|---|---|---|
| 功能完整性 | 模拟采集、传输、控制、报警场景 | 所有功能正常,无数据丢失、指令执行错误 |
| 低功耗续航 | 电池满电后连续运行,记录功耗与续航 | 平均功耗≤1mA,满电(2000mAh)续航≥6 个月 |
| 网络稳定性 | 断网 24 小时后恢复,检查数据 | 断网期间数据全部缓存,恢复后无遗漏上传 |
| 环境适应性 | 高低温箱(-20~60℃)、湿度 90% 测试 | 设备正常工作,传感器精度误差≤5% |
| 故障自恢复 | 手动触发传感器 / 通信故障 | 故障检测准确率 100%,恢复时间≤5 分钟 |
2. 典型问题与优化方案
| 问题现象 | 原因分析 | 优化方案 |
|---|---|---|
| 低温下 NB 模块无响应 | 低温导致模块供电电压跌落 | AXP192 增加低温补偿电路,确保 3.3V 稳定输出 |
| 光照数据波动大 | 传感器受环境光瞬间变化影响 | 软件增加 “滑动滤波”(取 5 次采样平均值) |
| 蓝牙调试连接不稳定 | 天线附近有电源干扰 | PCB 重新布局,蓝牙天线远离电源电路,增加屏蔽 |
| 历史数据导出慢 | SPI Flash 读取速度慢(1MHz) | 提高 SPI 时钟至 10MHz,优化数据导出算法 |
六、第 26 篇总结与后续展望
总结
这一篇我们通过 “智能环境监测站” 项目,完成了 STM32 技术栈的全面整合:
硬件层面:掌握了 “多模块选型、电路设计、PCB 布局”,兼顾功能、成本与稳定性;软件层面:搭建了 “分层架构 + 多任务调度”,实现了数据采集、传输、存储、控制的完整逻辑;云平台层面:完成了 “物模型创建、可视化部署、报警配置”,让设备具备远程监控能力;项目层面:掌握了 “测试→优化” 的迭代流程,理解了从 “技术” 到 “产品” 的落地逻辑。
后续展望
本项目可基于实际需求进一步扩展,典型方向包括:
功能扩展:增加 “粉尘浓度(PM2.5)、有害气体(CO)” 传感器,适用于工业场景;控制联动:增加继电器模块,云端下发指令控制风扇、加湿器,实现 “监测→控制” 闭环;定位功能:集成 GPS 模块,户外设备支持 “位置上报 + 地理围栏报警”;边缘计算:本地实现 “数据异常分析”(如温湿度突变预测故障),减少云端依赖。
至此,“100 篇文章精通 STM32F103” 系列已覆盖从基础到项目实战的核心内容,后续文章将聚焦 “进阶技术”(如 STM32H7 高性能系列、AI 模型部署)与 “行业深度项目”(如工业网关、智能家居中控),持续助力你成为嵌入式物联网工程师!