项目编号: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赞---->视频下方说明

微信公众号----->阿华学长单片机设计

找我领取!!!

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐