物联网之NB-IoT技术实践开发五

NB-IoT人体红外感应传感器

1、人体红外感应传感器原理

2、人体红外感应传感器开发

人体红外感应传感器原理

光谱

红外线属于一种电磁射线,特性等同于无线电或X射线。

人眼可见的光波是380nm-780nm

发射波长为780nm-1mm的长射线为红外线

红外线能够做什么

人体红外感应传感器应用

人体红外感应原理

任何发热体都会产生红外线

辐射的红外线波长跟物体温度有关,表面温度越高,辐射能力越强

人体正常温度36~37.5℃,其辐射最强的红外线波长为9.67~9.64,中心波长为9.65

人体红外感应传感器

自动感应:人进入其感应范围则输出高电平, 人离开感应范围则自动延时关闭高电平,输出低电平 

人体红外感应传感器

人体红外感应传感器开发

人体红外感应传感器开发需求

当有人经过时立刻触发状态数据上报

平时1小时上传一次数据

人体红外感应传感器驱动开发

分析传感器原理图

编写驱动程序

    -->配置P8为外部中断模式

    -->使能NVIC中断源

    -->配置为上升沿触发,IO为下拉输入模式,平时为低电平,保证产品的稳定性

    -->编写中断回调函数

人体红外感应传感器采集功能

人体红外感应传感器上传功能

#ifndef _SENSOR_H
#define _SENSOR_H

#include "stm32f0xx.h"

extern uint8_t IrStatus;//红外传感器状态,在sensor.c中定义

extern uint8_t IrValue;//红外状态的值,在sensor.c中定义

void SensorTask(void);

#endif

#include "sensor.h"
#include "nbiotdriver.h"

uint8_t IrStatus = 0;//定义红外传感器状态

uint8_t IrValue = 0;//定义红外状态的值

void SensorTask(void)
{
	if((IrStatus ==1)&&ReadAccessStatus())//判断红外传感器状态是否为1,并且读取入网状态是否完成
	{
		IrStatus = 0;//清除红外传感器状态
		
		TriggerSendData();//触发数据上报
		
		printf("IR is Trigger!\r\n");
	}
	IrValue = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_8);
}

#include "sensor.h"

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_8)
	{
		IrStatus = 1;//当有人经过的时候。红外传感器输出高电平,则M0的PB8输入为高电平,故这里设置红外传感器状态为1
	}
}

 

static uint8_t IsAccess;//定义静态变量:入网完成

uint8_t ReadAccessStatus(void)//读取入网是否完成:初始化时将其赋值为0,入网完成时赋值为1
{
	return IsAccess;//下面两个函数:一个初始化将其赋值为0,一个入网完成将其赋值为1
}

void NB_Init(void)//NB初始化
{
    IsAccess = 0;//初始化时将其赋值为0
	NB_TaskStatus = NB_SEND;//任务状态设置为发送状态
	ATCurrentCmdNum = AT_CFUN0;//当前指令设置为第一条指令
	ATNextCmdNum = AT_CGSN;//下一条指令设置为第二条指令
}
 
void NB_Task(void)//NB不同任务状态的处理程序,一般开始时NB_TaskStatus状态为NB_SEND,故可以从NB_SEND开始分析
{
    while(1)//死循环,为了高效率处理
	{
		switch(NB_TaskStatus)
		{
			case NB_IDIE:
				 if(CompareTime(&TimeNBSendData))//判断发送指令是否超时
				 {
						ATCurrentCmdNum = AT_NMGS;//当前指令设置为发送数据指令
						ATNextCmdNum = AT_IDIE;//下一条指令设置为空闲指令
						NB_TaskStatus = NB_SEND;//任务状态设置为发送
						SetTime(&TimeNBSendData,10000);//每隔10秒发送一次数据
						break;//跳转到发送状态
				 }
			    return;
			case NB_SEND:
				if(ATCurrentCmdNum != ATNextCmdNum)//如果当前指令不等于下一条指令,则该指令是第一次运行
				{
					CurrentRty = ATCmds[ATCurrentCmdNum].RtyNum;//获取当前指令的重发次数
				}
				ATSend(ATCurrentCmdNum);//发送指令
				NB_TaskStatus = NB_WAIT;//更改为等待态
				return;//因为有超时
			case NB_WAIT:
				ATRec();//AT接收字符串的解析
				if(ATCmds[ATCurrentCmdNum].ATStatus == SUCCESS_REC)//判断接收状态是否成功
				{
					if(ATCurrentCmdNum == AT_CGPADDR)//判断当前指令是否为入网指令
					{
						NB_TaskStatus = NB_ACCESS;//如果是则状态设置为入网完成
						break;//跳转指令
					}
					else if(ATCurrentCmdNum == AT_NMGS)//判断当前指令是否为发送数据指令
					{
						NB_TaskStatus = NB_IDIE;//设置任务状态为空闲状态
						return;
					}
					else
					{
						ATCurrentCmdNum += 1;//如果不是入网指令,则当前指令加1
						
						ATNextCmdNum = ATCurrentCmdNum+1;//下一条指令在当前指令的基础上再加1
						NB_TaskStatus = NB_SEND;//设置为发送状态
						break;//跳转指令
					}
				}
				else if(CompareTime(&TimeNB))//判断发送指令之后接收是否超时
				{
					ATCmds[ATCurrentCmdNum].ATStatus = TIME_OUT;//改变当前指令的状态:设置超时
					if(CurrentRty > 0)//判断当前重发的次数是否大于零
					{
						CurrentRty--;
						ATNextCmdNum = ATCurrentCmdNum;//下一条指令等于当前指令
						NB_TaskStatus = NB_SEND;//改变任务状态为发送状态
						break;//跳转到发送状态的处理程序
					}
					else//否则重发次数已经达到最高的重发次数限制
					{
						NB_Init();//NB初始化,函数具体实现见下方
						return;
					}
				}
				return;
			case NB_ACCESS://如果是入网完成的状态
                IsAccess = 1;//入网完成时赋值为1
			    LedOn(LED_NET);//打开入网完成的指示灯
			    ATCurrentCmdNum = AT_NMGS;//当前指令设置为发送数据的指令
			    ATNextCmdNum = AT_IDIE;//下一条指令设置为空闲指令
			    NB_TaskStatus = NB_SEND;//任务状态设置为发送状态
			    SetTime(&TimeNBSendData,10000);//发送指令超时设置
			    break;//跳转到发送状态
			default:
				return;
		}
	}
}

 

void TriggerSendData(void)//触发数据上报
{
	ATCurrentCmdNum = AT_NMGS;
	ATNextCmdNum = AT_IDIE;
	SetTime(&TimeNBSendData,SENDDATATIME);
	NB_TaskStatus = NB_SEND;
}

void NB_Task(void)//改写发送指令部分的函数
{
	while(1)
	{
		switch(NB_TaskStatus)
		{
			case NB_IDIE:
					 if(CompareTime(&TimeNBSendData))
					 {
							TriggerSendData();
							break;
					 }
				return;
			case NB_SEND:
						if(ATCurrentCmdNum != ATNextCmdNum)
						{
							CurrentRty = ATCmds[ATCurrentCmdNum].RtyNum;
						}
						ATSend(ATCurrentCmdNum);
						NB_TaskStatus = NB_WAIT;
				return;
			case NB_WAIT:
					ATRec();
					if(ATCmds[ATCurrentCmdNum].ATStatus == SUCCESS_REC)
					{
						if(ATCurrentCmdNum == AT_CGPADDR)
						{
							NB_TaskStatus = NB_ACCESS;
							break;
						}
						else if(ATCurrentCmdNum == AT_NMGS)
						{
							NB_TaskStatus = NB_IDIE;
							break;
						}
						else
						{
							ATCurrentCmdNum += 1;
						
							ATNextCmdNum = ATCurrentCmdNum+1;
							NB_TaskStatus = NB_SEND;
							break;
						}
					}
					else if(CompareTime(&TimeNB))
					{
						ATCmds[ATCurrentCmdNum].ATStatus = TIME_OUT;
						if(CurrentRty > 0)
						{
							CurrentRty--;
							ATNextCmdNum = ATCurrentCmdNum;
							NB_TaskStatus = NB_SEND;
							break;
						}
						else
						{
							NB_Init();
							return;
						}
					}
				return;
			case NB_ACCESS:
						LedOn(LED_NET);
						IsAccess = 1;
						TriggerSendData();//上报数据
						break;
			default:
				return;
		}
	}
}

void ATSend(teATCmdNum ATCmdNum)//修改上报数据,之前是写死在里面,现要上报IrValue的值
{
	//清空接收缓存区
	memset(Usart2type.Usart2RecBuff,0,USART2_REC_SIZE);
	ATCmds[ATCmdNum].ATStatus = NO_REC;
	
	ATRecCmdNum = ATCmdNum;
	
	if(ATCmdNum == AT_NMGS)
	{
		memset(NB_SendDataBuff,0,100);

            //注意:一个字符需要用两个16进制数表示,所以第一个数为0时需要把0补齐	
	    sprintf(NB_SendDataBuff,"%s%d,%02x%02x\r\n",ATCmds[ATCmdNum].ATSendStr,2,0x00,IrValue);
		HAL_UART_Transmit(&huart2,(uint8_t*)NB_SendDataBuff,strlen(NB_SendDataBuff),100);
		HAL_UART_Transmit(&huart1,(uint8_t*)NB_SendDataBuff,strlen(NB_SendDataBuff),100);
	}
	else
	{
		HAL_UART_Transmit(&huart2,(uint8_t*)ATCmds[ATCmdNum].ATSendStr,strlen(ATCmds[ATCmdNum].ATSendStr),100);
		HAL_UART_Transmit(&huart1,(uint8_t*)ATCmds[ATCmdNum].ATSendStr,strlen(ATCmds[ATCmdNum].ATSendStr),100);
	}
		//打开超时定时器
	SetTime(&TimeNB,ATCmds[ATCmdNum].TimeOut);
	
	//打开发送指示灯
	SetLedRun(LED_TX);
}

 

#define SENDDATATIME 3600*1000
main函数中:

...

while (1)
  {
		LedTask();
		SensorTask();
		NB_Task();
  }

...

NB-IoT可燃气体检测

1、可燃气体传感器原理

2、可燃气体传感器开发

可燃气体传感器原理

天然气:甲烷

液化石油气:丙烷和丁烷

MQ-5可燃气体传感器

MQ-5气体传感器所使用的气敏材料是在清洁空气中电导率较低的二氧化锡,当传感器所处环境中存在可燃气体时,传感器的电导率随空气中可燃气体浓度的增大而增大

MQ-5气体传感器对丁烷、丙烷、甲烷灵敏度高,对甲烷和丙烷可较好的兼顾,这种传感器可检测多种可燃性气体

MQ-5基本电路

Vh:为传感器提供工作温度,可接直流也可接交流

Vc:为负载电阻Rl提供测量电压

Vrl:传感器串联电阻Rl上电压用于浓度测量

MQ-5传感器模块

可燃气体传感器开发

可燃气体传感器开发需求

10秒钟定时采集可燃气体浓度

1小时上传一次数据

可燃气体传感器驱动开发

分析传感器原理图

编写驱动程序

    -->配置PA4为ADC采集模式

    -->修改采样周期为239.5(为了满足精度和稳定性

可燃气体采集功能

可燃气体传感器上传功能

#ifndef _SENSOR_H
#define _SENSOR_H

#include "stm32f0xx.h"
#define TIMESEOSOR 10*1000//为了测试的可行性,每隔10秒上传一次数据
extern uint8_t IrStatus;

extern uint8_t IrValue;

extern uint16_t AdcValue;

void SensorTask(void);

#endif

#include "sensor.h"
#include "nbiotdriver.h"
#include "time.h"
#include "adc.h"

uint8_t IrStatus = 0;

uint8_t IrValue = 0;

uint16_t AdcValue = 0;//采集的可燃气体的值

static tsTimeType TimeSensor;

void SensorTask(void)
{

	if(CompareTime(&TimeSensor))//定时器时间到,则开始对ADC进行采集。因为一开始是没有对TimeSensor做初始化,所以获取的当前时间为0,一上电就会采集一次
	{
		HAL_ADC_Start(&hadc);//开启ADC
		
		HAL_ADC_PollForConversion(&hadc,1);//轮询等待转换完成,第二个参数为采集超时时间设置为1毫秒
		
		if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc), HAL_ADC_STATE_REG_EOC))//判断采集是否完成
		{
			AdcValue = HAL_ADC_GetValue(&hadc);//获取ADC的值
			printf("AdcValue = %d\r\n",AdcValue);
		}
		SetTime(&TimeSensor,TIMESEOSOR);//重启定时器
	}
}

 

//存取ADC值的是一个16位的整数,所以拆解成两个字节
sprintf(NB_SendDataBuff,"%s%d,%02x%02x\r\n",ATCmds[ATCmdNum].ATSendStr,2,(uint8_t)(AdcValue>>8),(uint8_t)AdcValue);
HAL_UART_Transmit(&huart2,(uint8_t*)NB_SendDataBuff,strlen(NB_SendDataBuff),100);
HAL_UART_Transmit(&huart1,(uint8_t*)NB_SendDataBuff,strlen(NB_SendDataBuff),100);

其他代码同 人体红外数据采集,这里不做赘述

NB-IoT火焰检测

1、火焰传感器原理

2、火焰传感器开发

火焰传感器原理(代码与可燃气体传感器代码一致,不做赘述)

火焰

火焰的热辐射具有离散光谱的气体辐射和连续光谱的固体辐射。不同燃烧物的火焰辐射强度、波长分布有所差异,但总体来说,其对应火焰温度的近红外波长域及紫外光域具有很大的辐射强度,根据这种特性可制成火焰传感器

远红外火焰传感器

远红外火焰传感器能够探测到波长在700纳米~1000纳米范围内的红外光,探测角度为60,其中红外光波长在880纳米附近时,其灵敏度达到最大。远红外火焰探头将外界红外光的强弱变化转化为电流的变化

火焰传感器硬件设计

开关量输出

模拟量输出

火焰传感器开发

火焰传感器开发需求

10秒钟定时采集可燃气体浓度

1小时上传一次数据

可燃气体传感器驱动开发

分析传感器原理图

编写驱动程序

    -->配置PA4为ADC采集模式

    -->修改采样周期为239.5

可燃气体采集功能

可燃气体传感器上传功能

NB-IoT继电器控制

1、继电器工作原理

2、继电器控制开发

继电器工作原理

继电器

继电器是一种自动电气开关

    -->输入输出电路隔离

    -->信号转换(从断开到接通)

    -->增加输出能力

继电器工作原理

利用电磁铁控制工作电路通断的开关

继电器模块设计

D1拉高,常开触点闭合

D1拉低,常开触点断开

继电器控制开发

继电器开发需求

5秒钟上报一次继电器工作状态

解析下发命令并执行动作

继电器驱动开发

分析传感器原理图

编写驱动程序

    -->配置PB7为输出模式

    -->编写打开/关闭接口函数

uint8_t RelayStatus = 0;//定义变量表示继电器的状态

void RelayOn(void)//继电器打开(吸合)
{
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);
	RelayStatus = 1;//继电器状态设置为1,表示吸合
	printf("RelayOn\r\n");
}

void RelayOff(void)//继电器关闭(断开)
{
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET);
	RelayStatus = 0;//继电器状态设置为0,表示断开
	printf("RelayOff\r\n");
}

下行命令解析

void ATRec(void)//解析接收到的数据,控制继电器的状态
{
	char *p;
	if(Usart2type.Usart2RecFlag)//解析数据缓存区中是否有数据需要解析
	{
		if(strstr((const char*)Usart2type.Usart2RecBuff,ATCmds[ATRecCmdNum].ATRecStr) != NULL)
		{
			ATCmds[ATRecCmdNum].ATStatus = SUCCESS_REC;
		}
        //判断是否有下行控制数据到来:下行发送过来的数据,一个字符(字节)需要用2个16进制的数据表示,所以
            +NNMI:1,00 表示断开(关闭)继电器
            +NNMI:1,01 表示吸合(打开)继电器
            如此一来只需要比较最后一个数字是'0'还是'1'即可判断下行数据是想关闭还是想打开继电器
		if(strstr((const char*)Usart2type.Usart2RecBuff,"+NNMI:1,0") != NULL)
		{
			p = strstr((const char*)Usart2type.Usart2RecBuff,"+NNMI:1,0");//p指向接收到的数据字符串首地址
			if(p[strlen("+NNMI:1,0")] == '1')//判断接收到的数据字符串的最后一个是否为'1'
			{
				RelayOn();//打开继电器
			}else if(p[strlen("+NNMI:1,0")] == '0')//判断接收到的数据字符串的最后一个是否为'0'
			{
				RelayOff();//关闭继电器
			}
		}
			
		SetLedRun(LED_RX);
		HAL_UART_Transmit(&huart1,Usart2type.Usart2RecBuff,Usart2type.Usart2RecLen,100);
		Usart2type.Usart2RecFlag = 0;
		Usart2type.Usart2RecLen = 0;
	}
}

 

void ATSend(teATCmdNum ATCmdNum)
{
	//清空接收缓存区
	memset(Usart2type.Usart2RecBuff,0,USART2_REC_SIZE);
	ATCmds[ATCmdNum].ATStatus = NO_REC;
	
	ATRecCmdNum = ATCmdNum;
	
	if(ATCmdNum == AT_NMGS)
	{
		memset(NB_SendDataBuff,0,100);
		sprintf(NB_SendDataBuff,"%s%d,%02x%02x\r\n",ATCmds[ATCmdNum].ATSendStr,2,0x0,RelayStatus);
		HAL_UART_Transmit(&huart2,(uint8_t*)NB_SendDataBuff,strlen(NB_SendDataBuff),100);
		HAL_UART_Transmit(&huart1,(uint8_t*)NB_SendDataBuff,strlen(NB_SendDataBuff),100);
	}
	else
	{
		HAL_UART_Transmit(&huart2,(uint8_t*)ATCmds[ATCmdNum].ATSendStr,strlen(ATCmds[ATCmdNum].ATSendStr),100);
		HAL_UART_Transmit(&huart1,(uint8_t*)ATCmds[ATCmdNum].ATSendStr,strlen(ATCmds[ATCmdNum].ATSendStr),100);
	}
		//打开超时定时器
	SetTime(&TimeNB,ATCmds[ATCmdNum].TimeOut);

	//打开发送指示灯
	SetLedRun(LED_TX);
}

 

void NB_Task(void)//改写发送指令部分的函数
{
	while(1)
	{
		switch(NB_TaskStatus)
		{
			case NB_IDIE:
				if(CompareTime(&TimeNBSendData))
				{
					TriggerSendData();
					break;
			         }
                               ATRec();//此处添加该函数,其他地方不用变动
				return;
			case NB_SEND:
				if(ATCurrentCmdNum != ATNextCmdNum)
				{
					CurrentRty = ATCmds[ATCurrentCmdNum].RtyNum;
				}
				ATSend(ATCurrentCmdNum);
				NB_TaskStatus = NB_WAIT;
				return;
			case NB_WAIT:
			        ATRec();
				if(ATCmds[ATCurrentCmdNum].ATStatus == SUCCESS_REC)
				{
					if(ATCurrentCmdNum == AT_CGPADDR)
					{
						NB_TaskStatus = NB_ACCESS;
						break;
					}
					else if(ATCurrentCmdNum == AT_NMGS)
					{
						NB_TaskStatus = NB_IDIE;
						break;
					}
					else
					{
						ATCurrentCmdNum += 1;
						
						ATNextCmdNum = ATCurrentCmdNum+1;
						NB_TaskStatus = NB_SEND;
						break;
					}
				}
				else if(CompareTime(&TimeNB))
				{
					ATCmds[ATCurrentCmdNum].ATStatus = TIME_OUT;
					if(CurrentRty > 0)
					{
						CurrentRty--;
						ATNextCmdNum = ATCurrentCmdNum;
						NB_TaskStatus = NB_SEND;
						break;
					}
					else
					{
						NB_Init();
				    	return;
					}
				}
				return;
			case NB_ACCESS:
				LedOn(LED_NET);
				IsAccess = 1;
				TriggerSendData();//上报数据
				break;
			default:
				return;
		}
	}
}

#define SENDDATATIME 5*1000

NB任务功能

在空闲任务里添加解析函数,保证下行命令实时解析

项目扩展及技术提炼

1、技术提炼

2、项目扩展

技术提炼

关键技术-STM32

关键技术-NB-IoT

关键技术-传感器

项目扩展

智慧水务项目

NB-IoT传输及云平台对接我们已熟练掌握

我们只需要做传感器对接即可

水位监测

液位传感器:12V供电、4-20mA输出

电流信号变电压信号,AD采集即可

水质监测

PH值传感器、电导率传感器:12V供电、4-20mA输出

电流信号变电压信号,AD采集即可

管网监测

管道压力传感器:12V供电、0-5V输出

电压信号,AD采集即可

智慧烟感

烟感传感器:MQ-2接口与MQ-5一样,直接驱动即可

智慧停车

地磁传感器:HMC5883L:I2C接口,HAL库直接驱动

定位跟踪

GPS定位模块:串口通信

猜你喜欢

转载自blog.csdn.net/weixin_39148042/article/details/81946893