1. 功能介绍
自从地球上出现森林以来,每年平均发生森林火灾超过 20 万次。森林火灾不仅烧毁树木,直接减少森林面积,而且严重破坏森林结构和森林环境,导致森林生态系统失衡,森林生物量下降,生产力下降,牲畜减少和鸟类减少,甚至牺牲一些重要东西。高强度的火灾会破坏土壤的化学和物理性质,降低水的积累和土壤的渗透性,使部分林区地下水位升高而溃败,造成沼泽;此外,通过焦化和使土壤表面变暖,也会加速焦土的干燥,导致杂草过度生长。近年来,世界各地每年都会发生许多火灾。大多数国家的火灾直接损失超过国民经济总量的 0.2%。事实上,除了直接的经济死亡和财产损失外,火灾后还有重大的间接损失,因此,有必要发展和加强防火和火灾报警。
这篇文章就采用华为云 iot 物联网平台快速搭建一个森林火灾预警联动控制系统模型
,模拟演示检测到森林火灾之后,如何快速上报到云平台,向关联的指定服务器发送数据报告,并自动向抽水泵发送指令,打开开关抽水灭火。
硬件平台介绍:
MCU: STM32F103ZET6
物联网云平台: 华为 IOT 云平台
气体检测传感器: MQ2-烟雾传感器、MQ135-空气质量检测传感器
火焰检测: 采用火焰检测传感器
抽水机: 采用直流电机模拟水泵,打开开关抽水喷水灭火
上网网卡: 采用 GSM 模块 SIM800C,使用的是物联网专用卡,包年只能上网这种。
与云端服务器的通信协议: 终端设备采用 MQTT3.1.1 协议与华为云服务器进行登录连接。
温湿度检测传感器: DHT11
供电方式: 电池+太阳能供电
为了演示联动控制,火警(烟雾)检测装置和灭火(直流电机)装置分为两个独立的设备,分别连接上云端。
火警检测装置连接上物联网服务器之后,可以在服务器上配置数据处理规则,如果烟雾浓度超标,可以自动向灭火装置发送指令,进行灭火操作。服务器收到火警检测装置上传的烟雾浓度、空气质量等数据后,可以向自己的私有服务器转发数据,方便自己服务器收到数据后做分析存储处理,比如: 向指定邮箱发送邮件、手机 APP 推送通知栏、向指定用户推送短消息提示等等。
2. 创建产品(火警预警装置)
打开官网链接: https://www.huaweicloud.com/s/JeeJqeiBlOe9kSU
(1)选择设备接入 IotTDA 选项。
(2)选择免费试用。
(3)在产品页面选择创建新的产品。
(4)填入产品信息,创建产品
(5)选择自己刚才创建的产品,创建数据模型,点击自定义模型
(6)选择添加属性
这个添加的属性就是设备端上报的数据类型。 比如: MQ2 烟雾传感器检测的烟雾数据值类型。
在这个页面上还有一个添加命令的功能,这是用于云端下发指令给设备端使用的。当前这个设备是火警检测装置,只需要上报数据给服务器,不需要下发指令,这里就只需要添加属性就行了。
根据自己的设备的具体情况填写即可,如果上报的数据有多种类型就创建多个属性。
3. 创建产品(灭火装置)
创建的流程和上面一样,这是多增加了一个命令下发的功能,方便云端远程控制电机开启和关闭,实现灭火功能。
(1)创建产品
(2)产品创建完毕之后,添加服务器 ID
(3)添加属性,电机属性是可以读可以写的,范围设置为 0 和 1,只能开关
(4)添加命令,这个命令用于云端远程向设备下发指令,设备收到指令后可以做出相应的逻辑处理
接着选择新增输入参数:
最后点击确定即可。
现在产品已经创建完毕。
4. 创建设备(火警预警装置)
(1)在设备页面,选择注册设备,选择自己的对应的产品,设备标识码一般填自己设备的硬件标号。
(2)设备创建成功之后会弹出弹窗,点击保存并关闭,会自动弹出下载窗口,是个文本文件,存放了密匙信息
{
"device_id": "61bacdc02b2aa20288c5a094_QQ1126626497",
"secret": "1126626497"
}
复制代码
5. 创建设备(灭火装置)
流程与上面火警预警装置设备一样的。
{
"device_id": "61bad0564d9b020287193be2_QQ1126626497",
"secret": "1126626497"
}
复制代码
6. 生成 MQTT 协议登录 ID 和密匙
设备创建完成接来下生成 MQTT 登录账号、密匙,方便设备登录云端平台。
官网工具地址: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/
填入刚才创建设备时,保存下载文件里的信息,对着弹窗填入,最后生成了 ID、用户名、密码参数,用于 MQTT 协议登录使用。
(1)火警预警装置生成登录参数
ClientId 61bacdc02b2aa20288c5a094_QQ1126626497_0_0_2021121605
Username 61bacdc02b2aa20288c5a094_QQ1126626497
Password 43ed43bcbddc48772694fc2b18ec1112170f4d6cc52fbf1e01401c2ea1748475
复制代码
(2)灭火装置
ClientId 61bad0564d9b020287193be2_QQ1126626497_0_0_2021121605
Username 61bad0564d9b020287193be2_QQ1126626497
Password 43ed43bcbddc48772694fc2b18ec1112170f4d6cc52fbf1e01401c2ea1748475
复制代码
7. 上报属性格式与主题订阅格式
产品设备、MQTT 登录参数都到位了,接下来需要了解设备向服务器上报数据时,如何上报,格式是怎么样的。
(1)第一个问题是:华为云 IoT 物联网服务器的 IP 和端口号是多少?
在总览选项页面,点击多协议接入选项,就能看到了。
如果选择 MQTT 协议接入:
域名是: a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
如果你的设备不能解析域名,也可以直接填IP地址 121.36.42.100
端口号: 1883
复制代码
(2)第二个问题是:发布数据的主题和订阅数据的主题怎么填?
在产品页面,选择自己的产品,进去之后就能看到主题的格式介绍页面了。
为了方便后续复制粘贴,这里总结下格式:
火警预警装置:
格式: $oc/devices/{device_id}/sys/messages/down
//订阅主题: 平台下发消息给设备
$oc/devices/61bacdc02b2aa20288c5a094_QQ1126626497/sys/messages/down
格式: $oc/devices/{device_id}/sys/properties/report
//设备上报数据
$oc/devices/61bacdc02b2aa20288c5a094_QQ1126626497/sys/properties/report
复制代码
灭火装置:
格式: $oc/devices/{device_id}/sys/messages/down
//订阅主题: 平台下发消息给设备
$oc/devices/61bad0564d9b020287193be2_QQ1126626497/sys/messages/down
格式: $oc/devices/{device_id}/sys/properties/report
//设备上报数据
$oc/devices/61bad0564d9b020287193be2_QQ1126626497/sys/properties/report
复制代码
(3)第三个问题是:上报属性时,数据格式是什么?
官方文档介绍: https://support.huaweicloud.com/devg-iothub/iot_01_2127.html
总结下格式: 上报的数据就是 JSON 格式,一次性可以上传多个属性数据,JSON 数组里按照顺序增加即可。
重要的字段含义解释:这两个字段后面的数据需要自己根据自己的设备产品去填充的。
service_id 示设备服务的ID。
properties 是设备服务的属性列表,具体字段在设备关联的产品模型中定义。
复制代码
火警预警装置上传的数据:
{"services": [{"service_id": "MQ2","properties":{"MQ2":100}}]}
复制代码
灭火装置上传的数据:
{"services": [{"service_id": "motor","properties":{"motor":1}}]}
复制代码
8. 使用 MQTT 客户端模拟设备测试
(1)登录火警预警装置
(2)灭火装置登录
可以看到,设备已经成功登录服务器,完成了数据上报。这也证明服务器端设备创建已经全部 OK,正常。
9. 配置设备联动
(1)创建规则
(2)填写规则信息
(3)添加触发条件,选择需要处理数据的设备,设置条件:当烟雾浓度大于等于 100 就触发
(4)添加执行动作,当烟雾浓度超过 100 就下发指令给灭火装置
(3)最后点击创建规则,生效规则
(4)测试效果
使用两个 MQTT 客户端分别模拟火警预警装置和灭火装置,当烟雾浓度超过 100 时,查看灭火装置是否收到云端下发的指令。
10. 数据转发
如果数据需要转发到其他地方,可以自己创建规则配置。
11. 硬件设备测试
设备端采用 GSM 模块 SIM800C 完成上网功能,主控 MCU 采用 STM32F103ZET6。
任意只要能上网的设备都可以使用当前代码连接服务器,因为当前模拟的是户外设备,只能采用 GSM 模块上网。
如果是智能家居,屋里小区的设备,有 WIFI 的可以采用 ESP8266 这些无线网卡。
代码校对较多,这里只贴出核心代码,需要完整工程的在评论区留言。
1. SIM800C.c
这是 SIM800C 的配置代码
#include "sim800c.h"
/*
函数功能:向SIM800C模块发送指令
函数参数:
char *cmd 发送的命令
char *check_data 检测返回的数据
返回值: 0表示成功 1表示失败
*/
u8 SIM800C_SendCmd(char *cmd,char *check_data)
{
u16 i,j;
for(i=0;i<5;i++) //测试的总次数
{
USART2_RX_FLAG=0;
USART2_RX_CNT=0;
memset(USART2_RX_BUFF,0,sizeof(USART2_RX_BUFF));
USART_X_SendString(USART2,cmd); //发送指令
for(j=0;j<500;j++) //等待的时间(ms单位)
{
if(USART2_RX_FLAG)
{
USART2_RX_BUFF[USART2_RX_CNT]='\0';
if(strstr((char*)USART2_RX_BUFF,check_data))
{
return 0;
}
else break;
}
delay_ms(20); //一次的时间
}
}
return 1;
}
/*
函数 功能:GSM模块初始化检测
函数返回值:1表示模块检测失败,0表示成功
*/
u8 SIM800C_InitCheck(void)
{
if(SIM800C_SendCmd("AT\r\n","OK"))return 1;
else printf("SIM800模块正常!\r\n");
if(SIM800C_SendCmd("ATE0\r\n","OK"))return 2;
else printf("设置模块不回显成功!\r\n");
if(SIM800C_SendCmd("AT+CGMI\r\n","OK"))return 3;
else printf("查询制造商名称成功!%s\r\n",USART2_RX_BUFF);
if(SIM800C_SendCmd("AT+CGMM\r\n","OK"))return 4;
else printf("查询模块型号成功!%s\r\n",USART2_RX_BUFF);
DelayMs(1000);
DelayMs(1000);
if(SIM800C_SendCmd("AT+CNUM\r\n","+CNUM:"))return 5;
else printf("获取本机号码成功!%s\r\n",USART2_RX_BUFF);
/* 返回格式如下:
+CNUM: "","+8613086989413",145,7,4
OK
*/
return 0;
}
/*
函数 功能:GSM模块短信模式设置
函数返回值:0表示模块设置成功
*/
u8 SIM800C_SetNoteTextMode(void)
{
if(SIM800C_SendCmd("AT+CSCS=\"GSM\"\r\n","OK"))return 1;// "GSM"字符集
else printf("短信GSM字符集设置成功!\r\n");
if(SIM800C_SendCmd("AT+CMGF=1\r\n","OK"))return 2; //文本模式
else printf("短信文本模式设置成功!\r\n");
return 0;
}
/*
函数功能:发送短信
函数参数:
num:电话号码
text:短信内容
函数返回值:0表示发送成功
*/
u8 SIM800C_SendNote(u8 *num,u8 *text,u16 len)
{
char data[50];
char send_buf[2];
sprintf(data,"AT+CMGS=\"%s\"\r\n",num);
if(SIM800C_SendCmd(data,">"))return 1; //设置发送的手机号
USART_X_SendData(USART2,text,len); //发送短信内容
send_buf[0] = 0x1a;
send_buf[1] = '\0';
if(SIM800C_SendCmd(send_buf,"+CMGS"))return 2; //发送结束符号
return 0;
}
/*
函数功能:NTP网络同步时间
*/
void SIM800C_NtpUpdate(void)
{
SIM800C_SendCmd("AT+SAPBR=3,1,\"Contype\",\"GPRS\"\r\n","OK");//配置承载场景1
SIM800C_SendCmd("AT+SAPBR=3,1,\"APN\",\"CMNET\"\r\n","OK");
SIM800C_SendCmd("AT+SAPBR=1,1\r\n","OK"); //激活一个GPRS上下文
DelayMs(5);
SIM800C_SendCmd("AT+CNTPCID=1\r\n","OK"); //设置CNTP使用的CID
SIM800C_SendCmd("AT+CNTP=\"202.120.2.101\",32\r\n","OK"); //设置NTP服务器和本地时区(32时区 时间最准确)
SIM800C_SendCmd("AT+CNTP\r\n","+CNTP: 1"); //同步网络时间
printf("同步网络时间:%s\r\n",USART2_RX_BUFF);
}
/*
函数功能:GPRS数据通信初始化
返 回 值: 0表示成功
*/
u8 SIM800C_GPRS_Init(void)
{
SIM800C_SendCmd("AT+CIPCLOSE=1\r\n","CLOSE OK"); //关闭连接
SIM800C_SendCmd("AT+CIPSHUT\r\n","SHUT OK"); //关闭移动场景
if(SIM800C_SendCmd("AT+CGCLASS=\"B\"\r\n","OK"))return 1; //设置GPRS移动台类别为B,支持包交换和数据交换
if(SIM800C_SendCmd("AT+CGDCONT=1,\"IP\",\"CMNET\"\r\n","OK"))return 2;//设置PDP上下文,互联网接协议,接入点等信息
if(SIM800C_SendCmd("AT+CGATT=1\r\n","OK"))return 3; //附着GPRS业务
if(SIM800C_SendCmd("AT+CIPCSGP=1,\"CMNET\"\r\n","OK"))return 4; //设置为GPRS连接模式
if(SIM800C_SendCmd("AT+CIPHEAD=1\r\n","OK"))return 5; //设置接收数据显示IP头(方便判断数据来源)
return 0;
}
/*
函数功能: 连接TCP服务器
函数参数:
ipaddr:ip地址
port:端口
返 回 值: 0表示成功,其他值表示失败
*/
u8 SIM800C_Connect_TCP_Server(char *ipaddr,char *port)
{
char cmd_buff[100];
SIM800C_SendCmd("AT+CIPCLOSE=1\r\n","CLOSE OK"); //关闭连接
SIM800C_SendCmd("AT+CIPSHUT\r\n","SHUT OK"); //关闭移动场景
sprintf(cmd_buff,"AT+CIPSTART=\"TCP\",\"%s\",\"%s\"\r\n",ipaddr,port);
if(SIM800C_SendCmd(cmd_buff,"OK"))return 1; //发起连接
return 0;
}
/*
函数功能: TCP客户端模式下发送数据
返 回 值: 0表示成功,其他值表示失败
*/
u8 SIIM800C_TCP_SendData(u8 *data,u32 len)
{
char send_buf[2];
//准备发送数据
if(SIM800C_SendCmd("AT+CIPSEND\r\n",">")==0)
{
//发送数据
USART_X_SendData(USART2,data,len);
//发送结束符号
DelayMs(50);
send_buf[0] = 0x1a;
send_buf[1] = '\0';
if(SIM800C_SendCmd(send_buf,"SEND OK"))return 2;
else return 0;
}
return 1;
}
复制代码
2. adc.c
这是烟雾传感器的 ADC 通道配置代码。
//////////////////////////////////////////////////////////////////////////////////
// 功能描述 : 智能环境检测系统
// 时间 : 20190605
// 版本 : v3.3
// 版权所有,盗版必究。
//Copyright(C) DS小龙哥 2016 - 2020
///////////////////////////////////////////////////////////////////////////////////
#include "adc.h"
/*
函数功能: ADC1的初始化
规则通道方式
*/
void ADC1_Init(void)
{
/*1. 配置ADC采集输入的IO口*/
RCC->APB2ENR |= 1 << 3;//PB
GPIOB->CRL &= 0xFFFFFFF0;
GPIOB->CRL |= 0x00000000;//配置PB0为模拟输入模式
/*2.配置ADC1时钟*/
RCC->APB2ENR|=1<<9; //开启ADC1时钟
RCC->APB2RSTR|=1<<9; //开启复位时钟
RCC->APB2RSTR&=~(1<<9); //关闭复位时钟
/*3. 配置ADC的预分频器*/
RCC->CFGR&=~(0x3<<14); //清空预分频
RCC->CFGR|=0x2<<14; //12MHZ
/*4. 配置ADC CR1基本寄存器*/
ADC1->CR1&=~(0xF<<16); //0000:独立模式
ADC1->CR2|=1<<23; //1:启用温度传感器和VREFINT。
//ADC1->CR2|=1<<22; //1:开始转换规则通道。
ADC1->CR2|=1<<20; //1:使用外部事件启动转换
ADC1->CR2|=0x7<<17; //111: SWSTART
ADC1->CR2&=~(1<<11); //0:右对齐;
ADC1->CR2&=~(1<<1); //0:单次转换模式;
/*5. 配置ADC规则序列寄存器*/
ADC1->SQR1&=~(0xF<<20); //0000: 1个转换
ADC1->SMPR2|=0x7<<3; //配置通道1 111: 239.5周期
ADC1->SMPR1|=0x7<<18; //配置通道16 111: 239.5周期
ADC1->CR2|=1<<0; //1:开启ADC并启动转换。
ADC1->CR2|=1<<3; //1:初始化校准寄存器。
ADC1->CR2|=1<<2; //1:开始校准
while(ADC1->CR2&1<<2){} //等待校准结束
}
/*
函数功能: 获取指定通道的ADC值
函数参数: u8 ch 通道号
*/
u16 ADC1_GetCHx(u8 ch)
{
ADC1->SQR3&=0xFFFFFFE0; //00000
ADC1->SQR3|=ch<<0; //规则序列中的第1个转换
ADC1->CR2|=1<<22; //1:开始转换规则通道。
while(!(ADC1->SR&1<<1)){} //等待转换完成
return ADC1->DR; //返回接收到的数据值
}
复制代码
3. DHT11.c
这是温湿度检测代码。
//////////////////////////////////////////////////////////////////////////////////
// 功能描述 : 智能环境检测系统
// 时间 : 20190605
// 版本 : v3.3
// 版权所有,盗版必究。
//Copyright(C) DS小龙哥 2016 - 2020
///////////////////////////////////////////////////////////////////////////////////
#include "dht11.h"
#include "delay.h"
/*
复位DHT1
*/
void DHT11_Rst(void)
{
DHT11_IO_OUT(); //SET OUTPUT
DHT11_DQ_OUT=0; //拉低DQ
delay_ms(20); //拉低至少18ms
DHT11_DQ_OUT=1; //DQ=1
delay_us(30); //主机拉高20~40us
}
/*
等待DHT11的回应
返回1:未检测到DHT11的存在
返回0:存在
*/
u8 DHT11_Check(void)
{
u8 retry=0;
DHT11_IO_IN();//SET INPUT
while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
{
retry++;
delay_us(1);
};
if(retry>=100)return 1;
else retry=0;
while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
{
retry++;
delay_us(1);
};
if(retry>=100)return 1;
return 0;
}
/*
从DHT11读取一个位
返回值:1/0
*/
u8 DHT11_Read_Bit(void)
{
u8 retry=0;
while(DHT11_DQ_IN&&retry<100)//等待变为低电平
{
retry++;
delay_us(1);
}
retry=0;
while(!DHT11_DQ_IN&&retry<100)//等待变高电平
{
retry++;
delay_us(1);
}
delay_us(40);//等待40us
if(DHT11_DQ_IN)return 1;
else return 0;
}
/*
从DHT11读取一个字节
返回值:读到的数据
*/
u8 DHT11_Read_Byte(void)
{
u8 i,dat;
dat=0;
for(i=0;i<8;i++)
{
dat<<=1;
dat|=DHT11_Read_Bit();
}
return dat;
}
/*
从DHT11读取一次数据
temp:温度值(范围:0~50°)
humi:湿度值(范围:20%~90%)
返回值:0,正常;1,读取失败
*/
u8 DHT11_Read_Data(u8 *temp,u8 *humi)
{
u8 buf[5];
u8 i;
DHT11_Rst();
if(DHT11_Check()==0)
{
for(i=0;i<5;i++)//读取40位数据
{
buf[i]=DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
{
*humi=buf[0];
*temp=buf[2];
}
}else return 1;
return 0;
}
/*
初始化DHT11的IO口 DQ 同时检测DHT11的存在
返回1:不存在
返回0:存在
*/
u8 DHT11_Init(void)
{
RCC->APB2ENR|=1<<2; //使能PORTG口时钟
GPIOA->CRL&=0XFF0FFFFF;//PORTG.11 推挽输出
GPIOA->CRL|=0X00300000;
GPIOA->ODR|=1<<5; //输出1
DHT11_Rst();
return DHT11_Check();
}
复制代码
项目源码: https://download.csdn.net/download/xiaolong1126626497/81993720
评论