基于STM32+华为云IOT设计的智能防盗单车锁【玩转华为云】

网友投稿 679 2022-05-29

一、前言

近年来随着国民经济的发展,交通拥堵和环境污染问题越来越突出,而自行车对改善交通与环境起到了重要作用。中国本身是一个自行车使用大国,随着自行车的发展,自行车的科技含量越来越高,然而自行车安防问题突出。目前市场上自行车锁大多是传统机械结构车锁,没有实现智能化,急需解决。本文提出一种基于STM32单片机的智能自行车锁(马蹄锁)的设计方法,来提高自行车锁的智能化及安防等级。

硬件选项说明:单片机采用STM32F103RCT6,GSM模块采用SIM800C,完成网络连接、数据上传,GPS经纬度解析,短信发送,物联网平台采用华为云IOT,作为数据存储端,蓝牙模块采用正点原子低功耗BLE蓝牙,支持蓝牙开锁解锁,车辆的状态使用ADXL345三轴加速度传感器检测,密码键盘采用电容矩阵键盘。

二、设计思路总结

需要设计一款Android手机APP,可以远程开锁解锁,手机APP对接华为云物联网平台,实现远程与自行车锁完成数据交互,命令下发。智能锁与华为云IOT服务器之间的通信协议采用MQTT协议,手机APP与华为云IOT服务器之间采用HTTP协议。智能锁除了支持远程开锁关锁之外,还支持蓝牙解锁和输入密码开始,设计的APP支持蓝牙功能,可以连接智能锁上的蓝牙完成开锁和关锁,如果没有带手机,可以输入密码完成开锁。

车辆的状态检测通过ADXL345三轴加速度计检测,如果车辆处于锁定状态,发现车辆被移动了会触发报警,锁里的蜂鸣器会持续响,并且SIM800C会向指定的手机号码发送短信,提示车辆可能被盗,同时上传GPS经纬度到云端服务器,手机APP上可以获取智能锁上传的GPS经纬度,调用百度地图显示车辆的位置,方便寻车。

三、硬件选型

(1) 加速度计传感器

ADXL345是一款小尺寸、薄型、低功耗、完整的三轴加速度计,提供经过信号调理的电压输出。

说明:CS接高电平则选择IIC通信,反之则SPI通信。SDO(地址引脚)接高电平,根据手册器件的7位I2C地址是0x1D,后面跟上读取/写位(R/W),则写寄存器为0x3A,读寄存器为0x3B;接低电平,则7位I2C地址是0x53,同理,跟上读写标志位后写寄存器为0xA6,读寄存器为0xA7;

(2) STM32开发板

STM32F103RCT6的芯体规格是32位,速度是72MHz,程序存储器容量是256KB,程序存储器类型是FLASH,RAM容量是48K。

(3) BLE低功耗蓝牙模块

(4) SIM800C

模块特点:

1、支持极限DC5V-18V宽电压输入

2、有电源使能开关引脚EN

3、支持锂电池供电接口VBAT3.5-4.5V

4、输入支持移动和联通手机卡Micro SIM卡

5、送51/STM32/ARDUINO驱动例程

1、DC 5V-18V电源输入,推荐使用DC 9V

2、电源开始使能引脚默认使能

3、电源地

4、GSM模块的TXD引脚接其它模块的RXD

5、GSM模块的RXD引脚接其它模块的TXD

6、数据终端准备

7、内核音频输出引脚

8、内核音频输出引脚

9、锂电池输入引脚,DC 3.5 - 4.5V

10、电源地

11、启动引脚和GND短路可实现开机自启动

12、电源地

13、RTC外置电池引脚

14、内核振铃提示引脚

15、内合音频输入引脚

16、内核音频输入引脚

加粗的引脚一般都用到。

建议使用V_IN单独供电DC5-18V输入(推荐使用9V),或者VBAT供电锂电池两种供电方式这两种供电方式最稳定。如果只是简单调试,也可使用USB-TTL或者开发板的5V直接给模块供电。不过一般电脑或者开发板的功率有限,可能会不稳定。请根据具体情况自己取舍选择合适电源。

3. 手机APP软件设计

3.1 通信说明

上位机与设备之间支持通过BLE低功耗串口蓝牙进行通信,支持通过网络连接华为云服务器进行通信,手机APP下发open_lock和close_lock实现关锁开锁。

3.2 搭建开发环境

上位机软件采用Qt框架设计,Qt是一个跨平台的C++图形用户界面应用程序框架。Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。简单来说,QT可以很轻松的帮你做带界面的软件,甚至不需要你投入很大精力。

QT官网: https://www.qt.io/

QT学习入门实战专栏文章: https://blog.csdn.net/xiaolong1126626497/category_11400392.html

QT5.12.6的-:

https://download.qt.io/archive/qt/5.12/5.12.6/

4. 创建云端设备

4.1 创建产品

登录官网: https://www.huaweicloud.com/product/iothub.html

直接搜索物联网,打开页面。

4.2 自定义模型

4.3 注册设备

设备创建成功:

{ "device_id": "6274b1d62d5e854503d3a67e_lock", "secret": "12345678" }

4.4 MQTT设备密匙

创建完产品、设备之后,接下来就需要知道如何通过MQTT协议登陆华为云服务器。

官方的详细介绍在这里:

https://support.huaweicloud.com/devg-iothub/iot_01_2127.html#ZH-CN_TOPIC_0240834853__zh-cn_topic_0251997880_li365284516112

属性上报格式:

https://support.huaweicloud.com/api-iothub/iot_06_v5_3010.html

MQTT设备登陆密匙生成地址:

DeviceId 6274b1d62d5e854503d3a67e_lock DeviceSecret 12345678 ClientId 6274b1d62d5e854503d3a67e_lock_0_0_2022050605 Username 6274b1d62d5e854503d3a67e_lock Password 334dd7c0c10e47280880e9dd004ae0d8c5abc24dbbc9daa735315722707fe13b

4.5 使用MQTT客户端软件登录

所有的参数已经得到,接下来采用MQTT客户端登录华为云进行测试。

华为云物联网平台的域名是: 161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com

华为云物联网平台的IP地址是:121.36.42.100

在软件里参数填充正确之后,就看到设备已经连接成功了。

接下来打开设备页面,可以看到设备已经在线了。

4.6 数据上报测试

//订阅主题: 平台下发消息给设备 $oc/devices/6274b1d62d5e854503d3a67e_lock/sys/messages/down //设备上报数据 $oc/devices/6274b1d62d5e854503d3a67e_lock/sys/properties/report //上报的属性消息 (一次可以上报多个属性,在json里增加就行了) {"services": [{"service_id": "lock","properties":{"lock":1}}]}

//订阅主题: 平台下发消息给设备 $oc/devices/6274b1d62d5e854503d3a67e_lock/sys/messages/down //设备上报数据 $oc/devices/6274b1d62d5e854503d3a67e_lock/sys/properties/report //上报的属性消息 (一次可以上报多个属性,在json里增加就行了) {"services": [{"service_id": "lock","properties":{"GPS信息":"lat:12.345,lng:45.678"}}]}

4.7 应用侧开发

为了更方便的展示设备数据,与设备完成交互,还需要开发一个配套的上位机,官方提供了应用侧开发的API接口、SDK接口,为了方便通用一点,我这里采用了API接口完成数据交互,上位机软件采用QT开发。

帮助文档地址: ttps://support.huaweicloud.com/api-iothub/iot_06_v5_0034.html

设备属性就是设备上传的传感器状态数据信息,应用侧提供了API接口,可以主动向设备端下发请求指令;设备端收到指令之后需要按照约定的数据格式上报数据;所以,要实现应用层与设备端的数据交互,需要应用层与设备端配合才能完成。

5. STM32开发

5.1 ADXL345.c

#include "app.h" /* 函数功能: 各种硬初始化 继电器模块--DAT--->PA4 PB12-----输入引脚,检测模块是否连接或者断开 */ void Hardware_Init(void) { RCC->APB2ENR|=1<<2; GPIOA->CRL&=0xFFF0FFFF; GPIOA->CRL|=0x00030000; RCC->APB2ENR|=1<<3; GPIOB->CRH&=0xFFF0FFFF; GPIOB->CRH|=0x00080000; } ////////////////////////////////////////////////////////////////////////////////// //初始化ADXL345. //返回值:0,初始化成功;1,初始化失败. u8 ADXL345_Init(void) { IIC_Init(); //初始化IIC总线 if(ADXL345_RD_Reg(DEVICE_ID)==0XE5) //读取器件ID { ADXL345_WR_Reg(DATA_FORMAT,0X2B); //低电平中断输出,13位全分辨率,输出数据右对齐,16g量程 ADXL345_WR_Reg(BW_RATE,0x0A); //数据输出速度为100Hz ADXL345_WR_Reg(POWER_CTL,0x28); //链接使能,测量模式 ADXL345_WR_Reg(INT_ENABLE,0x00); //不使用中断 ADXL345_WR_Reg(OFSX,0x00); ADXL345_WR_Reg(OFSY,0x00); ADXL345_WR_Reg(OFSZ,0x00); return 0; } return 1; } //写ADXL345寄存器 //addr:寄存器地址 //val:要写入的值 //返回值:无 void ADXL345_WR_Reg(u8 addr,u8 val) { IIC_Start(); IIC_Send_Byte(ADXL_WRITE); //发送写器件指令 IIC_Wait_Ack(); IIC_Send_Byte(addr); //发送寄存器地址 IIC_Wait_Ack(); IIC_Send_Byte(val); //发送值 IIC_Wait_Ack(); IIC_Stop(); //产生一个停止条件 } //读ADXL345寄存器 //addr:寄存器地址 //返回值:读到的值 u8 ADXL345_RD_Reg(u8 addr) { u8 temp=0; IIC_Start(); IIC_Send_Byte(ADXL_WRITE); //发送写器件指令 temp=IIC_Wait_Ack(); IIC_Send_Byte(addr); //发送寄存器地址 temp=IIC_Wait_Ack(); IIC_Start(); //重新启动 IIC_Send_Byte(ADXL_READ); //发送读器件指令 temp=IIC_Wait_Ack(); temp=IIC_Read_Byte(0); //读取一个字节,不继续再读,发送NAK IIC_Stop(); //产生一个停止条件 return temp; //返回读到的值 } //读取ADXL的平均值 //x,y,z:读取10次后取平均值 void ADXL345_RD_Avval(short *x,short *y,short *z) { short tx=0,ty=0,tz=0; u8 i; for(i=0;i<10;i++) { ADXL345_RD_XYZ(x,y,z); delay_ms(10); tx+=(short)*x; ty+=(short)*y; tz+=(short)*z; } *x=tx/10; *y=ty/10; *z=tz/10; } //自动校准 //xval,yval,zval:x,y,z轴的校准值 void ADXL345_AUTO_Adjust(char *xval,char *yval,char *zval) { short tx,ty,tz; u8 i; short offx=0,offy=0,offz=0; ADXL345_WR_Reg(POWER_CTL,0x00); //先进入休眠模式. delay_ms(100); ADXL345_WR_Reg(DATA_FORMAT,0X2B); //低电平中断输出,13位全分辨率,输出数据右对齐,16g量程 ADXL345_WR_Reg(BW_RATE,0x0A); //数据输出速度为100Hz ADXL345_WR_Reg(POWER_CTL,0x28); //链接使能,测量模式 ADXL345_WR_Reg(INT_ENABLE,0x00); //不使用中断 ADXL345_WR_Reg(OFSX,0x00); ADXL345_WR_Reg(OFSY,0x00); ADXL345_WR_Reg(OFSZ,0x00); delay_ms(12); for(i=0;i<10;i++) { ADXL345_RD_Avval(&tx,&ty,&tz); offx+=tx; offy+=ty; offz+=tz; } offx/=10; offy/=10; offz/=10; *xval=-offx/4; *yval=-offy/4; *zval=-(offz-256)/4; ADXL345_WR_Reg(OFSX,*xval); ADXL345_WR_Reg(OFSY,*yval); ADXL345_WR_Reg(OFSZ,*zval); } //读取3个轴的数据 //x,y,z:读取到的数据 void ADXL345_RD_XYZ(short *x,short *y,short *z) { u8 buf[6]; u8 i; IIC_Start(); IIC_Send_Byte(ADXL_WRITE); //发送写器件指令 IIC_Wait_Ack(); IIC_Send_Byte(0x32); //发送寄存器地址(数据缓存的起始地址为0X32) IIC_Wait_Ack(); IIC_Start(); //重新启动 IIC_Send_Byte(ADXL_READ); //发送读器件指令 IIC_Wait_Ack(); for(i=0;i<6;i++) { if(i==5)buf[i]=IIC_Read_Byte(0);//读取一个字节,不继续再读,发送NACK else buf[i]=IIC_Read_Byte(1); //读取一个字节,继续读,发送ACK } IIC_Stop(); //产生一个停止条件 *x=(short)(((u16)buf[1]<<8)+buf[0]); *y=(short)(((u16)buf[3]<<8)+buf[2]); *z=(short)(((u16)buf[5]<<8)+buf[4]); } //读取ADXL345的数据times次,再取平均 //x,y,z:读到的数据 //times:读取多少次 void ADXL345_Read_Average(short *x,short *y,short *z,u8 times) { u8 i; short tx,ty,tz; *x=0; *y=0; *z=0; if(times)//读取次数不为0 { for(i=0;iAPB2ENR|=1<<3; //先使能外设IO PORTB时钟 GPIOB->CRL&=0X00FFFFFF; //6/7 推挽输出 GPIOB->CRL|=0X33000000; GPIOB->ODR|=3<<6; //6,7 输出高 } //产生IIC起始信号 void IIC_Start(void) { SDA_OUT(); //sda线输出 IIC_SDA=1; IIC_SCL=1; delay_us(4); IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4); IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 } //产生IIC停止信号 void IIC_Stop(void) { SDA_OUT();//sda线输出 IIC_SCL=0; IIC_SDA=0;//STOP:when CLK is high DATA change form low to high delay_us(4); IIC_SCL=1; IIC_SDA=1;//发送I2C总线结束信号 delay_us(4); } //等待应答信号到来 //返回值:1,接收应答失败 // 0,接收应答成功 u8 IIC_Wait_Ack(void) { u8 ucErrTime=0; SDA_IN(); //SDA设置为输入 IIC_SDA=1;delay_us(1); IIC_SCL=1;delay_us(1); while(READ_SDA) { ucErrTime++; if(ucErrTime>250) { IIC_Stop(); return 1; } } IIC_SCL=0;//时钟输出0 return 0; } //产生ACK应答 void IIC_Ack(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=0; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //不产生ACK应答 void IIC_NAck(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=1; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //IIC发送一个字节 //返回从机有无应答 //1,有应答 //0,无应答 void IIC_Send_Byte(u8 txd) { u8 t; SDA_OUT(); IIC_SCL=0;//拉低时钟开始数据传输 for(t=0;t<8;t++) { IIC_SDA=(txd&0x80)>>7; txd<<=1; delay_us(2); //对TEA5767这三个延时都是必须的 IIC_SCL=1; delay_us(2); IIC_SCL=0; delay_us(2); } } //读1个字节,ack=1时,发送ACK,ack=0,发送nACK u8 IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; SDA_IN();//SDA设置为输入 for(i=0;i<8;i++ ) { IIC_SCL=0; delay_us(2); IIC_SCL=1; receive<<=1; if(READ_SDA)receive++; delay_us(1); } if (!ack) IIC_NAck();//发送nACK else IIC_Ack(); //发送ACK return receive; }

5.2 sim800.c

基于STM32+华为云IOT设计的智能防盗单车锁【玩转华为云】

#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_BUFFER,0,sizeof(USART2_RX_BUFFER)); USARTx_StringSend(USART2,cmd); //发送指令 for(j=0;j<500;j++) //等待的时间(ms单位) { if(USART2_RX_FLAG) { USART2_RX_BUFFER[USART2_RX_CNT]='

#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_BUFFER,0,sizeof(USART2_RX_BUFFER)); USARTx_StringSend(USART2,cmd); //发送指令 for(j=0;j<500;j++) //等待的时间(ms单位) { if(USART2_RX_FLAG) { USART2_RX_BUFFER[USART2_RX_CNT]='\0'; if(strstr((char*)USART2_RX_BUFFER,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_BUFFER); if(SIM800C_SendCmd("AT+CGMM\r\n","OK"))return 4; else printf("查询模块型号成功!%s\r\n",USART2_RX_BUFFER); DelayMs(1000); DelayMs(1000); if(SIM800C_SendCmd("AT+CNUM\r\n","+CNUM:"))return 5; else printf("获取本机号码成功!%s\r\n",USART2_RX_BUFFER); /* 返回格式如下: +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; //设置发送的手机号 USARTx_DataSend(USART2,text,len); //发送短信内容 send_buf[0] = 0x1a; send_buf[1] = '\0'; if(SIM800C_SendCmd(send_buf,"+CMGS"))return 2; //发送结束符号 return 0; }

'; if(strstr((char*)USART2_RX_BUFFER,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_BUFFER); if(SIM800C_SendCmd("AT+CGMM\r\n","OK"))return 4; else printf("查询模块型号成功!%s\r\n",USART2_RX_BUFFER); DelayMs(1000); DelayMs(1000); if(SIM800C_SendCmd("AT+CNUM\r\n","+CNUM:"))return 5; else printf("获取本机号码成功!%s\r\n",USART2_RX_BUFFER); /* 返回格式如下: +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; //设置发送的手机号 USARTx_DataSend(USART2,text,len); //发送短信内容 send_buf[0] = 0x1a; send_buf[1] = '

#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_BUFFER,0,sizeof(USART2_RX_BUFFER)); USARTx_StringSend(USART2,cmd); //发送指令 for(j=0;j<500;j++) //等待的时间(ms单位) { if(USART2_RX_FLAG) { USART2_RX_BUFFER[USART2_RX_CNT]='\0'; if(strstr((char*)USART2_RX_BUFFER,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_BUFFER); if(SIM800C_SendCmd("AT+CGMM\r\n","OK"))return 4; else printf("查询模块型号成功!%s\r\n",USART2_RX_BUFFER); DelayMs(1000); DelayMs(1000); if(SIM800C_SendCmd("AT+CNUM\r\n","+CNUM:"))return 5; else printf("获取本机号码成功!%s\r\n",USART2_RX_BUFFER); /* 返回格式如下: +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; //设置发送的手机号 USARTx_DataSend(USART2,text,len); //发送短信内容 send_buf[0] = 0x1a; send_buf[1] = '\0'; if(SIM800C_SendCmd(send_buf,"+CMGS"))return 2; //发送结束符号 return 0; }

'; if(SIM800C_SendCmd(send_buf,"+CMGS"))return 2; //发送结束符号 return 0; }

5.3 MQTT信息

//华为物联网服务器的设备信息 #define MQTT_ClientID "62381267575fb713ee164ad2_xl_1_0_0_2022032106" #define MQTT_UserName "62381267575fb713ee164ad2_xl_1" #define MQTT_PassWord "124344feff3e3d96ff6af13cf36af36766619ff1eeee40e99cbae9b7b9739fe4" //订阅与发布的主题 #define SET_TOPIC "$oc/devices/62381267575fb713ee164ad2_xl_1/sys/messages/down" //订阅 #define POST_TOPIC "$oc/devices/62381267575fb713ee164ad2_xl_1/sys/properties/report" //发布 //设置连接的路由器信息 #define CONNECT_WIFI "abc" //将要连接的路由器名称 --不要出现中文、空格等特殊字符 #define CONNECT_PASS "1234567890" //将要连接的路由器密码 #define CONNECT_SERVER_IP "a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com" //服务器IP地址 #define CONNECT_SERVER_PORT 1883 //服务器端口号 u8 *mqtt_rxbuf; u8 *mqtt_txbuf; u16 mqtt_rxlen; u16 mqtt_txlen; u8 _mqtt_txbuf[256];//发送数据缓存区 u8 _mqtt_rxbuf[256];//接收数据缓存区 typedef enum { //名字 值 报文流动方向 描述 M_RESERVED1 =0 , // 禁止 保留 M_CONNECT , // 客户端到服务端 客户端请求连接服务端 M_CONNACK , // 服务端到客户端 连接报文确认 M_PUBLISH , // 两个方向都允许 发布消息 M_PUBACK , // 两个方向都允许 QoS 1消息发布收到确认 M_PUBREC , // 两个方向都允许 发布收到(保证交付第一步) M_PUBREL , // 两个方向都允许 发布释放(保证交付第二步) M_PUBCOMP , // 两个方向都允许 QoS 2消息发布完成(保证交互第三步) M_SUBSCRIBE , // 客户端到服务端 客户端订阅请求 M_SUBACK , // 服务端到客户端 订阅请求报文确认 M_UNSUBSCRIBE , // 客户端到服务端 客户端取消订阅请求 M_UNSUBACK , // 服务端到客户端 取消订阅报文确认 M_PINGREQ , // 客户端到服务端 心跳请求 M_PINGRESP , // 服务端到客户端 心跳响应 M_DISCONNECT , // 客户端到服务端 客户端断开连接 M_RESERVED2 , // 禁止 保留 }_typdef_mqtt_message; //连接成功服务器回应 20 02 00 00 //客户端主动断开连接 e0 00 const u8 parket_connetAck[] = {0x20,0x02,0x00,0x00}; const u8 parket_disconnet[] = {0xe0,0x00}; const u8 parket_heart[] = {0xc0,0x00}; const u8 parket_heart_reply[] = {0xc0,0x00}; const u8 parket_subAck[] = {0x90,0x03}; void MQTT_Init(void) { //缓冲区赋值 mqtt_rxbuf = _mqtt_rxbuf; mqtt_rxlen = sizeof(_mqtt_rxbuf); mqtt_txbuf = _mqtt_txbuf; mqtt_txlen = sizeof(_mqtt_txbuf); memset(mqtt_rxbuf,0,mqtt_rxlen); memset(mqtt_txbuf,0,mqtt_txlen); //无条件先主动断开 MQTT_Disconnect(); delay_ms(100); MQTT_Disconnect(); delay_ms(100); } /* 函数功能: 登录服务器 函数返回值: 0表示成功 1表示失败 */ u8 MQTT_Connect(char *ClientID,char *Username,char *Password) { u8 i,j; int ClientIDLen = strlen(ClientID); int UsernameLen = strlen(Username); int PasswordLen = strlen(Password); int DataLen; mqtt_txlen=0; //可变报头+Payload 每个字段包含两个字节的长度标识 DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2); //固定报头 //控制报文类型 mqtt_txbuf[mqtt_txlen++] = 0x10; //MQTT Message Type CONNECT //剩余长度(不包括固定头部) do { u8 encodedByte = DataLen % 128; DataLen = DataLen / 128; // if there are more data to encode, set the top bit of this byte if ( DataLen > 0 ) encodedByte = encodedByte | 128; mqtt_txbuf[mqtt_txlen++] = encodedByte; }while ( DataLen > 0 ); //可变报头 //协议名 mqtt_txbuf[mqtt_txlen++] = 0; // Protocol Name Length MSB mqtt_txbuf[mqtt_txlen++] = 4; // Protocol Name Length LSB mqtt_txbuf[mqtt_txlen++] = 'M'; // ASCII Code for M mqtt_txbuf[mqtt_txlen++] = 'Q'; // ASCII Code for Q mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T //协议级别 mqtt_txbuf[mqtt_txlen++] = 4; // MQTT Protocol version = 4 对于 3.1.1 版协议,协议级别字段的值是 4(0x04) //连接标志 mqtt_txbuf[mqtt_txlen++] = 0xc2; // conn flags mqtt_txbuf[mqtt_txlen++] = 0; // Keep-alive Time Length MSB mqtt_txbuf[mqtt_txlen++] = 100; // Keep-alive Time Length LSB 100S心跳包 保活时间 mqtt_txbuf[mqtt_txlen++] = BYTE1(ClientIDLen);// Client ID length MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(ClientIDLen);// Client ID length LSB memcpy(&mqtt_txbuf[mqtt_txlen],ClientID,ClientIDLen); mqtt_txlen += ClientIDLen; if(UsernameLen > 0) { mqtt_txbuf[mqtt_txlen++] = BYTE1(UsernameLen); //username length MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(UsernameLen); //username length LSB memcpy(&mqtt_txbuf[mqtt_txlen],Username,UsernameLen); mqtt_txlen += UsernameLen; } if(PasswordLen > 0) { mqtt_txbuf[mqtt_txlen++] = BYTE1(PasswordLen); //password length MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(PasswordLen); //password length LSB memcpy(&mqtt_txbuf[mqtt_txlen],Password,PasswordLen); mqtt_txlen += PasswordLen; } memset(mqtt_rxbuf,0,mqtt_rxlen); MQTT_SendBuf(mqtt_txbuf,mqtt_txlen); for(j=0;j<10;j++) { delay_ms(50); if(USART2_RX_FLAG) { memcpy((char *)mqtt_rxbuf,USART2_RX_BUFFER,USART2_RX_CNT); //memcpy for(i=0;i 0 ) encodedByte = encodedByte | 128; mqtt_txbuf[mqtt_txlen++] = encodedByte; }while ( DataLen > 0 ); //可变报头 mqtt_txbuf[mqtt_txlen++] = 0; //消息标识符 MSB mqtt_txbuf[mqtt_txlen++] = 0x0A; //消息标识符 LSB //有效载荷 mqtt_txbuf[mqtt_txlen++] = BYTE1(topiclen);//主题长度 MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(topiclen);//主题长度 LSB memcpy(&mqtt_txbuf[mqtt_txlen],topic,topiclen); mqtt_txlen += topiclen; if(whether) { mqtt_txbuf[mqtt_txlen++] = qos;//QoS级别 } for(i=0;i<10;i++) { memset(mqtt_rxbuf,0,mqtt_rxlen); MQTT_SendBuf(mqtt_txbuf,mqtt_txlen); for(j=0;j<10;j++) { delay_ms(50); if(USART2_RX_FLAG) { memcpy((char *)mqtt_rxbuf,(char*)USART2_RX_BUFFER,USART2_RX_CNT); USART2_RX_FLAG=0; USART2_RX_CNT=0; } if(mqtt_rxbuf[0]==parket_subAck[0] && mqtt_rxbuf[1]==parket_subAck[1]) //订阅成功 { return 0;//订阅成功 } } } return 1; //失败 } //MQTT发布数据打包函数 //topic 主题 //message 消息 //qos 消息等级 u8 MQTT_PublishData(char *topic, char *message, u8 qos) { int topicLength = strlen(topic); int messageLength = strlen(message); static u16 id=0; int DataLen; mqtt_txlen=0; //有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度 //QOS为0时没有标识符 //数据长度 主题名 报文标识符 有效载荷 if(qos) DataLen = (2+topicLength) + 2 + messageLength; else DataLen = (2+topicLength) + messageLength; //固定报头 //控制报文类型 mqtt_txbuf[mqtt_txlen++] = 0x30; // MQTT Message Type PUBLISH //剩余长度 do { u8 encodedByte = DataLen % 128; DataLen = DataLen / 128; // if there are more data to encode, set the top bit of this byte if ( DataLen > 0 ) encodedByte = encodedByte | 128; mqtt_txbuf[mqtt_txlen++] = encodedByte; }while ( DataLen > 0 ); mqtt_txbuf[mqtt_txlen++] = BYTE1(topicLength);//主题长度MSB mqtt_txbuf[mqtt_txlen++] = BYTE0(topicLength);//主题长度LSB memcpy(&mqtt_txbuf[mqtt_txlen],topic,topicLength);//拷贝主题 mqtt_txlen += topicLength; //报文标识符 if(qos) { mqtt_txbuf[mqtt_txlen++] = BYTE1(id); mqtt_txbuf[mqtt_txlen++] = BYTE0(id); id++; } memcpy(&mqtt_txbuf[mqtt_txlen],message,messageLength); mqtt_txlen += messageLength; MQTT_SendBuf(mqtt_txbuf,mqtt_txlen); return mqtt_txlen; }

云端实践 移动APP IoT

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:云游戏能驱动手游市场二次爆发吗?
下一篇:疫情之下,精准测试的智能可信模式正在成为中流砥柱
相关文章