【开源】基于NB-IoT的社区井盖监控系统设计
本项目设计了一种基于NB-IoT技术的智能井盖监控系统,采用STM32F103C8T6作为主控芯片,集成MPU6050角度传感器、水位传感器和MQ-2可燃气体传感器,实时监测井盖状态。当检测到倾斜、积水或气体超标时,系统会通过蜂鸣器报警并通过NB-IoT模块上传数据至云平台。系统具备本地OLED显示和按键控制功能,支持维修模式切换,实现了城市井盖的智能化管理和远程监控,有效提升了市政设施的安全性和
项目编号:STM32-T007
项目名称:基于NB-IoT的社区井盖监控系统设计
【摘要】
本项目设计了一种基于NB-IoT技术的社区井盖监控系统,旨在提升城市基础设施的智能化管理与安全保障能力。系统以STM32F103C8T6单片机为核心,集成MPU6050倾斜传感器、水位传感器和MQ-2甲烷传感器,实时监测井盖是否偏移、井下积水高度以及可燃气体浓度。当任一指标超出设定阈值时,蜂鸣器自动报警,提醒相关人员及时处理。通过按键可切换系统至维修状态,避免误报。同时,系统搭载M5310-A NB-IoT模块,实现低功耗广域网络通信,将各项监测数据和报警信息实时上传至云平台。云端平台具备数据接收、存储、分析与可视化功能,便于管理者远程查看各井盖状态,提升城市运维效率。OLED模块用于本地信息显示,增强现场交互体验。该系统具备智能监测、远程传输、异常报警和平台联动等功能,适用于城市社区、市政道路等场景,具有良好的推广前景与实用价值。
【所用硬件】
1、STM32F103C8T6最小系统板
STM32F103C8T6最小系统板是基于意法半导体(ST)Cortex-M3内核的微控制器开发板,核心芯片为STM32F103C8T6,主频72MHz,具有64KB Flash和20KB SRAM,满足中等复杂度的嵌入式应用需求。该板集成USB、USART、SPI、I2C等通信接口,支持多种外设扩展。最小系统板包含必要的外围电路,如8MHz晶振、复位电路、电源管理(3.3V稳压),并提供GPIO引脚引出,方便连接传感器、显示屏等模块。其低功耗、高性能特性使其广泛应用于智能硬件、工业控制等领域。在本设计中,它作为主控单元,负责数据采集、处理及外设控制,确保电子秤系统的稳定运行。
2、MPU6050陀螺仪模块

MPU6050陀螺仪模块是一款常用的六轴传感器,内部集成了三轴加速度计和三轴陀螺仪,能够实时测量物体的加速度与角速度信息。该模块通过I2C接口与单片机或其他控制器进行通信,体积小巧、功耗低,适用于姿态检测、运动跟踪和机器人平衡控制等场景。由于其数据稳定、价格适中,MPU6050在智能车、无人机以及可穿戴设备中应用十分广泛。
3、OLED显示模块

OLED(有机发光二极管)显示屏模块是一种自发光显示器件,无需背光,具有高对比度、宽视角和快速响应的特点。本设计采用的OLED模块通常为0.96英寸或1.3英寸,分辨率为128×64像素,支持I2C或SPI通信协议,可直接与STM32等微控制器连接。其工作电压为3.3V~5V,功耗低,适合便携式设备。
4、蜂鸣器模块

鸣器模块是一种常见的声音报警装置,广泛应用于电子设备中用于提示或报警功能。该模块通常由一个蜂鸣器和驱动电路组成,能发出高频的蜂鸣声。按照驱动方式可分为有源蜂鸣器和无源蜂鸣器两种类型。有源蜂鸣器只需提供直流电压即可发声,控制简单,适用于固定频率提示场景;无源蜂鸣器则需要外部提供特定频率的驱动信号才能工作,适用于需要调节音调或生成特定声音模式的场合。模块通常采用5V或3.3V供电,可通过三极管或专用驱动芯片控制启停,响应速度快,可靠性高,常用于智能设备、安防系统、工业仪表等场景中。
5、水位传感器模块

水位传感器是一种用于检测和测量液体高度的装置,常应用于水箱、池塘、地下水井等场景。它通过电极、电容或压力等原理,将液位变化转换为电信号,供单片机或控制系统处理,从而实现水位监测与控制。该传感器具有结构简单、安装方便、灵敏度高的特点,能够有效防止水位过高或过低引发的设备损坏和安全隐患,广泛应用于智能家居、农业灌溉和工业自动化领域。
6、MQ-2模块

MQ-2传感器是一种常用的气体检测元件,主要用于检测可燃气体和烟雾浓度,如液化气、丙烷、氢气及烟雾等。它内部采用金属氧化物半导体敏感材料,当遇到可燃气体时电导率会发生变化,从而输出与浓度成比例的电信号。该传感器具有响应速度快、灵敏度高、成本低的优点,但需要一定的预热时间,适合用于家庭防火报警器、燃气泄漏检测及各类安全监测系统中。
7、M5310-A NB-IOT模块

M5310-A NB-IoT模块是一款基于中国移动物联网平台的通信模块,支持窄带物联网(NB-IoT)技术,具备低功耗、广覆盖和大连接的特点。该模块体积小巧,支持UART接口,方便与单片机或嵌入式系统对接,可广泛应用于智能水表、智慧城市、环境监测、智能家居等场景。其稳定的网络连接能力和超低功耗设计,使其在需要长时间待机和远程数据传输的应用中表现出色,具有良好的性价比和实用性。
8、轻触按键

轻触按键(Tactile Switch)是一种瞬时接触式机械开关,通过按压触发导通,松开后自动复位。其结构简单、体积小巧,通常采用金属弹片或导电橡胶实现触点通断,具有手感清晰、寿命长(可达10万次以上操作)的特点。工作电压和电流范围宽(如12V/50mA),可直接与STM32等微控制器的GPIO引脚连接,需配合上拉/下拉电阻使用以避免信号浮动。
【系统框架图】

【软件流程图】

【核心代码展示】
#include "app_task.h"
#include "main.h"
#include "m5310a.h"
#include "delay.h"
#include "usart.h"
#include "timer.h"
#include "esp8266.h"
#include "io.h"
#include "led.h"
#include "oled.h"
#include "sim800l.h"
#include "rtc.h"
#include "Encoder.h"
#include "beep.h"
#include "mpu6050.h"
#include "inv_mpu.h"
#include "adc.h"
#include "key.h"
u8 str[100];
PAGE page;
TRAPDOOR trapdoor;
POINT point;
TaskControlBlock TaskList[MAX_TASKS] = {0};
uint8_t RegisteredTasks = 0; // 已注册任务数
uint32_t SystemTick = 0; // 系统时间基准
// 任务注册函数
int8_t Task_Register(uint8_t id, uint32_t interval, void (*task_func)(void *), void *param)
{
if (RegisteredTasks >= MAX_TASKS)
return -1;
TaskList[RegisteredTasks].TaskID = id;
TaskList[RegisteredTasks].PollingInterval = interval;
TaskList[RegisteredTasks].TimerCounter = 0;
TaskList[RegisteredTasks].RunFlag = 0;
TaskList[RegisteredTasks].EnableFlag = 1;
TaskList[RegisteredTasks].TaskHook = task_func;
TaskList[RegisteredTasks].TaskParam = param;
TaskList[RegisteredTasks].Delay = 0;
RegisteredTasks++;
return 0;
}
// 任务使能控制
void Task_Enable(uint8_t id, uint8_t enable)
{
for (int i = 0; i < RegisteredTasks; i++)
{
if (TaskList[i].TaskID == id)
{
TaskList[i].EnableFlag = enable;
break;
}
}
}
/**
* @brief LED闪烁任务
*
* @param pt
*/
void Led_Task(void *pt)
{
LED1 = !LED1;
}
void Beep_Task(void *pt)
{
if(trapdoor.water < trapdoor.water_value || trapdoor.co > trapdoor.co_value)
{
BEEP = 0;
}
else
{
BEEP = 1;
}
}
void M5310A_Task(void *pt)
{
sprintf(str,"#%d#%d#%d#%d#",trapdoor.lid,trapdoor.water,trapdoor.co,trapdoor.state);
M5310A_SendData(str);
}
void Oled_Task(void *pt)
{
/**井盖监控系统 */
OLED_ShowChinese(16, 0, 5, 16, 1);
OLED_ShowChinese(32, 0, 6, 16, 1);
OLED_ShowChinese(48, 0, 7, 16, 1);
OLED_ShowChinese(64, 0, 8, 16, 1);
OLED_ShowChinese(80, 0, 9, 16, 1);
OLED_ShowChinese(96, 0, 10, 16, 1);
if (page == PAGE1)
{
/**井盖偏移: */
OLED_ShowChinese(0, 16, 11, 16, 1);
OLED_ShowChinese(16, 16, 12, 16, 1);
OLED_ShowChinese(32, 16, 13, 16, 1);
OLED_ShowChinese(48, 16, 14, 16, 1);
OLED_ShowChinese(64, 16, 15, 16, 1);
/**井盖偏移判断 */
if (trapdoor.angle.Pitch > 30 || trapdoor.angle.Pitch < -30 || trapdoor.angle.Roll > 30 || trapdoor.angle.Roll < -30)
{
trapdoor.lid = FAULT;
OLED_ShowChinese(80, 16, 0, 16, 1);
}
else
{
trapdoor.lid = NORMAL;
OLED_ShowChinese(80, 16, 1, 16, 1);
}
/**当前水位: */
OLED_ShowChinese(0, 32, 16, 16, 1);
OLED_ShowChinese(16, 32, 17, 16, 1);
OLED_ShowChinese(32, 32, 18, 16, 1);
OLED_ShowChinese(48, 32, 19, 16, 1);
OLED_ShowChinese(64, 32, 20, 16, 1);
sprintf(str, "%2d%%", trapdoor.water);
OLED_ShowString(80, 32, str, 16, 1);
/**甲烷浓度: */
OLED_ShowChinese(0, 48, 21, 16, 1);
OLED_ShowChinese(16, 48, 22, 16, 1);
OLED_ShowChinese(32, 48, 23, 16, 1);
OLED_ShowChinese(48, 48, 24, 16, 1);
OLED_ShowChinese(64, 48, 25, 16, 1);
sprintf(str, "%2d%%", trapdoor.co);
OLED_ShowString(80, 48, str, 16, 1);
}
else if (page == PAGE2)
{
/**井盖状态: */
if(point.x == 1 && point.y == 1)
{
OLED_ShowChinese(0, 16, 26, 16, 0);
OLED_ShowChinese(16, 16, 27, 16, 0);
OLED_ShowChinese(32, 16, 28, 16, 0);
OLED_ShowChinese(48, 16, 29, 16, 0);
OLED_ShowChinese(64, 16, 30, 16, 1);
}
else
{
OLED_ShowChinese(0, 16, 26, 16, 1);
OLED_ShowChinese(16, 16, 27, 16, 1);
OLED_ShowChinese(32, 16, 28, 16, 1);
OLED_ShowChinese(48, 16, 29, 16, 1);
OLED_ShowChinese(64, 16, 30, 16, 1);
}
if(point.y == 1 && point.x == 2)
{
if (trapdoor.state == FAULT)
{
OLED_ShowChinese(80, 16, 31, 16, 0);
OLED_ShowChinese(96, 16, 32, 16, 0);
}
else if (trapdoor.state == NORMAL)
{
OLED_ShowChinese(80, 16, 33, 16, 0);
OLED_ShowChinese(96, 16, 34, 16, 0);
}
}
else
{
if (trapdoor.state == FAULT)
{
OLED_ShowChinese(80, 16, 31, 16, 1);
OLED_ShowChinese(96, 16, 32, 16, 1);
}
else if (trapdoor.state == NORMAL)
{
OLED_ShowChinese(80, 16, 33, 16, 1);
OLED_ShowChinese(96, 16, 34, 16, 1);
}
}
if(point.y == 2 && point.x == 1)
{
OLED_ShowChinese(0, 32, 35, 16, 0);
OLED_ShowChinese(16, 32, 36, 16, 0);
OLED_ShowChinese(32, 32, 37, 16, 0);
OLED_ShowChinese(48, 32, 38, 16, 0);
OLED_ShowChinese(64, 32, 30, 16, 1);
}
else
{
OLED_ShowChinese(0, 32, 35, 16, 1);
OLED_ShowChinese(16, 32, 36, 16, 1);
OLED_ShowChinese(32, 32, 37, 16, 1);
OLED_ShowChinese(48, 32, 38, 16, 1);
OLED_ShowChinese(64, 32, 30, 16, 1);
}
if(point.y == 2 && point.x == 2)
{
sprintf(str,"%2d%%",trapdoor.water_value);
OLED_ShowString(80,32,str,16,0);
}
else
{
sprintf(str,"%2d%%",trapdoor.water_value);
OLED_ShowString(80,32,str,16,1);
}
if(point.y == 3 && point.x == 1)
{
OLED_ShowChinese(0, 48, 40, 16, 0);
OLED_ShowChinese(16, 48, 41, 16, 0);
OLED_ShowChinese(32, 48, 42, 16, 0);
OLED_ShowChinese(48, 48, 43, 16, 0);
OLED_ShowChinese(64, 48, 30, 16, 1);
}
else
{
OLED_ShowChinese(0, 48, 40, 16, 1);
OLED_ShowChinese(16, 48, 41, 16, 1);
OLED_ShowChinese(32, 48, 42, 16, 1);
OLED_ShowChinese(48, 48, 43, 16, 1);
OLED_ShowChinese(64, 48, 30, 16, 1);
}
if(point.y == 3 && point.x == 2)
{
sprintf(str,"%2d%%",trapdoor.co_value);
OLED_ShowString(80,48,str,16,0);
}
else
{
sprintf(str,"%2d%%",trapdoor.co_value);
OLED_ShowString(80,48,str,16,1);
}
}
OLED_Refresh();
}
void MPU6050_Task(void *pt)
{
mpu_dmp_get_data(&trapdoor.angle.Pitch, &trapdoor.angle.Roll, &trapdoor.angle.Yaw); // 角度
}
void Water_Task(void *pt)
{
trapdoor.water = Get_Adc_Average(ADC_Channel_7, 5) * 100 / 4095;
}
void CO_Task(void *pt)
{
trapdoor.co = Get_Adc_Average(ADC_Channel_6, 5) * 100 / 4095;
}
void Key_Task(void *pt)
{
switch (KEY_Scan(0))
{
case KEY1_PRICE:
if (page == PAGE1)
{
page = PAGE2;
point.x = 1;
point.y = 1;
OLED_Clear();
}
else if(page == PAGE2)
{
if(point.x == 1)
{
if(point.y>1) point.y--;
}
else if(point.x == 2)
{
if(point.y == 1)
{
trapdoor.state = NORMAL;
}
else if(point.y == 2)
{
if(trapdoor.water_value<100) trapdoor.water_value++;
}
else if(point.y == 3)
{
if(trapdoor.co_value<100) trapdoor.co_value++;
}
}
}
;break;
case KEY2_PRICE:
if (page == PAGE2)
{
if(point.x == 1)
{
if(point.y<3) point.y++;
}
else if(point.x == 2)
{
if(point.y == 1)
{
trapdoor.state = FAULT;
}
else if(point.y == 2)
{
if(trapdoor.water_value>0) trapdoor.water_value--;
}
else if(point.y == 3)
{
if(trapdoor.co_value>0) trapdoor.co_value--;
}
}
};
break;
case KEY3_PRICE:
if (page == PAGE2)
{
if(point.x < 2) point.x++;
};
break;
case KEY4_PRICE:
if (page == PAGE2)
{
if(point.x == 2)
{
point.x = 1;
}
else if(point.x == 1)
{
point.x = 1;
point.y = 1;
page = PAGE1;
OLED_Clear();
}
};
break;
default:
break;
}
}
void Task_Init(void)
{
Task_Register(1, 1000, Led_Task, NULL);
Task_Register(2, 1000, Beep_Task, NULL);
Task_Register(3, 2000, M5310A_Task, NULL);
Task_Register(4, 200, Oled_Task, NULL);
Task_Register(5, 200, MPU6050_Task, NULL);
Task_Register(6, 500, Water_Task, NULL);
Task_Register(7, 500, CO_Task, NULL);
Task_Register(8, 1, Key_Task, NULL);
}
void Task_Run(void)
{
for (int i = 0; i < RegisteredTasks; i++)
{
if (TaskList[i].RunFlag && TaskList[i].EnableFlag)
{
// 执行任务函数
if (TaskList[i].TaskHook != NULL)
{
TaskList[i].TaskHook(TaskList[i].TaskParam);
}
// 清除运行标志
TaskList[i].RunFlag = 0;
}
}
}
void TaskTime(void)
{
static u32 tick_time = 0;
tick_time++;
// 遍历任务列表更新计数器
for (int i = 0; i < RegisteredTasks; i++)
{
if (TaskList[i].EnableFlag)
{
TaskList[i].TimerCounter++;
// 达到时间间隔时设置运行标志
if (TaskList[i].TimerCounter >= TaskList[i].PollingInterval)
{
TaskList[i].RunFlag = 1;
TaskList[i].TimerCounter = 0;
}
}
}
}
【原理图】

【硬件实物图】

【实物演示】
STM32-T007-基于NB-IoT的社区井盖监控系统设计
【开源说明】
B赞---->视频下方说明
微信公众号----->阿华学长单片机设计
找我领取!!!
更多推荐



所有评论(0)