摘要:基于STM32+华为云IoT设计的老人防摔倒报警系统。
本文分享自华为云社区《基于STM32+华为云IOT设计的老人防摔倒报警系统【玩转华为云】》,作者: DS小龙哥 。
1. 前言
中国的独生子女、人口老龄化等问题正逐渐成为一大社会问题。随着老年人体能的下降,跌倒带来的安全性和危害性越来越突出。国家和社会越来越重视老年人的健康和安全。开发一种能够实时检测老年人是否跌倒并及时通知其监护人的跌倒检测报警系统具有重要的现实意义。该系统包括三个部分:跌倒检测模块、GPS定位模块和通信模块。通过检测老人的日常状态,可以知道老人的状态。如果检测到老人跌倒,检测结果会通过网络上传到物联网云平台,获取老人跌倒地点的GPS定位,并通过GPRS通信向预设的监护人发送短信。
2. 设计需求
(1)采用STM32单片机作为主控芯片,配合其他模块完成功能设计
(2)通信模块采用SIM800C,支持上传采集的GPS经纬度数据到云端服务器,云端采用华为云物联网平台。
(3)老人摔倒检测采用MPU6050陀螺仪检测,当检测到老人摔倒之后,会通过SIM800C发送短信到紧急联系人,设备上的蜂鸣器会发出警报声,周围行人听到也可以进行帮助;并且会将GPS数据上传到云端,通过地图显示老人的位置,家人通过短信知道老人摔倒后,通过云端地图显示的位置,可以快速赶到老人身边,或者报警求助,报告位置。
(4)老人摔倒后,如果自己能行动,没有大问题,可以自己按下设备上的按键取消蜂鸣器报警,并且通过SIM800C向家人发送一条短信,报平安。
3. 设计的实物效果
为了快速验证方案的可行性,利用现成的模块和Du-Bang线连接完成预期的功能设计。
以下是硬件连接后的效果图。所选硬件型号已全部发布在第4章中;为了方便户外测试,这里使用充电宝或者电池盒作为电源。
可以设置电子围栏,坐标超出之后进行提示。
4. 硬件选型
主控芯片为STM32RCT6,通信模块为SIM800C,GPS采集模块为ATGM336北斗BDS+GPS双模模块,老年人跌倒检测模块为MPU6050陀螺仪。
这些都是现成的成品模块,都是淘宝上买的。模块的模型和截图贴在下面。如果想自己做一个,可以在淘宝上找同款买。
4.1 SIM800C
SIM800C模块是一款高性能高性价比工业级的GSM/GPRS模块。本模块采用SIMCOM公司的工业级四频850/900/ 1800/1900MHz SM800芯片,可以低功耗实现语音、SMS、数据和传真信息的传输。
模块特点:
1、支持极限DC5V-18V宽电压输入
2、有电源使能开关引脚EN
3、支持锂电池供电接口VBAT3.5-4.5V
4、输入支持移动和联通手机卡Micro SIM卡
5、送51/STM32/ARDUINO驱动例程
6、DC 5V-18V电源输入,推荐使用DC 9V
7、电源开始使能引脚默认使能
8、电源地
9、GSM模块的TXD引脚接其它模块的RXD
10、GSM模块的RXD引脚接其它模块的TXD
11、数据终端准备
12、内核音频输出引脚
13、内核音频输出引脚
14、锂电池输入引脚,DC 3.5 – 4.5V
15、电源地
16、启动引脚和GND短路可实现开机自启动
17、电源地
18、RTC外置电池引脚
19、内核振铃提示引脚
20、内合音频输入引脚
21、内核音频输入引脚
加粗的引脚一般都用到。
建议使用V_IN单独供电DC5-18V输入(建议9V),或者VBAT供电锂电池。这两种供电模式最稳定。简单调试也可以用USB-TTL或者开发板5V直接给模块供电。但一般电脑或开发板的功率有限,可能不稳定。请根据具体情况选择合适的电源。
总结:
模块本身支持自适应波特率,可以自动根据发送过去的指令计算对应的波特率,一般使用115200即可。
模块调试总结:
(1)供电电压5V也可以,采用电脑USB供电(直接插电脑USB口)。正常供电之后,模块上有电源指示灯。
(2)SIM800C的TX脚接单片机的RX脚
(3)SIM800C的RX脚接单片机的TX脚
(4)SIM800C的第11个引脚(PWK)和12个引脚(GND)短接接在一起,才可以开机。
电源正常后,右上角有一个黄色的电源灯。
通过串口发送AT指令过去测试模块效果。
4.2 STM32F103C8T6开发板
4.3 GPS模块
GPS模块正常定位后,模块上的LED灯会按照1秒钟闪烁一次。
返回的字段里GNRMC表示当前定位的GPS经纬度,解析代码只需要解析GNRMC表示当前定位的GPS经纬度,解析代码只需要解析GNRMC字段。
第一次启动GPS模块,定位差不多要几分钟时间,定位成功后,第二次启动定位就很快,最好是在室外,室内信号差,定位时间更久。
4.4 MPU6050陀螺仪
陀螺仪选择的是正点原子的模块,比较稳定,质量较好。
4.5 蜂鸣器
蜂鸣器选择的高电平触发。
5. 创建云端物联网服务器
为了查看老人摔倒后的位置,需要将设备采集的GPS数据通过SIM800C上传到云平台服务器进行存储。即使老人没有摔倒,也可以实时关注老人的位置,在地图上画出轨迹线,方便家人随时联系,了解老人的情况。
这里物联网的平台选择是华为云物联网平台,目前免费使用。在云端创建产品等信息后,设备通过MQTT协议连接云平台,上传GPS数据。目前,华为云的拖拽试用网页开发页面已经下架。目前要开发相应的主机,可以使用最近推广的低代码开发平台,或者通过云平台的应用端开发接口开发自己的主机。这里我是自己开发的主机,通过QT编写的主机app支持windows、Android、Linux等多种平台。,所以跨平台使用还是挺方便的。
接下来,我们将介绍如何登录官网创建产品、设备并在云端完成产品部署。
5.1 创建产品
官网地址: 设备接入_IoTDA_IoT_物联网IoT平台-华为云
打开官网后没有华为云账号需要先注册账号,这些步骤就不多说了,接下来就直接介绍如何创建产品、设备、配置属性、完成数据上传交互的流程。
点击免费使用进去页面。
点击左边产品选项,点击右上角创建产品按钮,弹出参数填充对话框。
根据自己的设备情况填入信息之后保存。
产品创建成功,点击查看详细信息。
5.2 创建模型文件
在现在的详情页面往下翻,可以看到模型创建的选项。
点击自定义模型选项,创建模型。
这里的模型就是设备上传的数据属性。
添加服务ID。
点击创建属性,这里选择JSON类型的数据,上传的GPS有经纬度两个数据,方便保存。
创建成功。
5.3 创建设备
产品是一个大的框架模型,下面可以创建很多具体的设备。目前,我这里只有一个硬件设备,所以只创建一个设备。设备可以手动创建,也可以自动创建,就像现在市面上的智能设备产品一样。拿到设备后,你可以扫描设备上的二维码,然后使用手机应用程序创建产品并添加设备。目前我这里只有一个设备,还要演示整个过程,所以我可以在网页上完成整个设备的创建。
链接地址: https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/device/all-device
点击左边的设备选项,再点击右上角的注册设备。
填充好信息之后,点击确定。
创建后保存设备的数据。
{
"device_id": "GPS1",
"secret": "12345678"
}
创建成功,目前设备处于未激活状态。
5.4 获取MQTT登录参数
目前产品和设备创建后,需要通过设备连接上传数据。要完成这一步,您还需要了解一些先决条件。
【A】华为云服务器IP地址、域名、端口号
【B】主题订阅的格式、主题发布的格式
【C】MQTT协议登录的三元组信息
充分了解了这3个信息之后就可以编写设备端代码了。下面就详细介绍这些信息怎么得到。
【1】华为云的服务器地址信息
在这里查看: https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/instance/detail?id=8fe87243-d97d-4c1e-bb34-186a60ca2d14&type=public
华为云物联网平台的域名是: 161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
华为云物联网平台的IP地址是:121.36.42.100
端口号是1883
【2】主题订阅的格式、主题发布的格式
主题订阅上报的格式在产品的详情页面可以看到。
主题发布官方的详细介绍在这里:
使用MQTT.fx调测_设备接入 IoTDA_开发指南_设备侧开发_使用MQTT Demo接入_华为云
主题上报的属性格式说明文档地址:
设备属性上报_设备接入 IoTDA_API参考_设备侧MQTT/MQTTS接口参考_设备属性_华为云
根据当前设备的信息总结,得到的信息如下:
//订阅主题: 平台下发消息给设备
$oc/devices/GPS1/sys/messages/down
//设备上报数据
$oc/devices/GPS1/sys/properties/report
//上报的属性消息 (一次可以上报多个属性,在json里增加就行了)
{"services": [{"service_id": "GPS","properties":{"GPS":{"lon":106.53,"lat":29.46}}}]}
【3】MQTT协议登录的三元组信息
华为云提供了MQTT协议参数的生成工具,非常方便,根据提示填入参数一键生成三元组。
MQTT设备登陆密匙生成地址: Huaweicloud IoTDA Mqtt ClientId Generator
得到的三元组如下:
ClientId GPS1_0_0_2022060716
Username GPS1
Password 27a2d2dd716fac29a0041beec1d7cf5f5b529fac65cc815c7eed9adb04d7364b
5.5 采用MQTT客户工具登录测试
为了验证服务器的配置、主题、属性是否OK,可以先用MQTT客户端模拟真实设备登录测试。下面这个MQTT工具是我自己开发的。为了方便测试物联网平台,我用QT写了这样一个工具软件。
软件名称:MQTT Client _ v 2.5(protocol 3 . 1 . 1). exe,我已经上传到CSDN的资源库了。你可以直接在CSDN搜索找到该软件的下载地址。我也会在下面文章的附件里上传一份。
在软件的左侧,根据提示填写相应的参数,然后点击登录,订阅主题,发布主题。
这时打开网页可以看到设备已经在线了。
在设备影子页面上可以看到上传的数据内容。
启动消息跟踪,可以了解通信的过程。
6. STM32硬件设备端程序设计
第五章搭建物联网云平台,第六章编写STM32设备端代码。
STM32设备端开发环境采用keil5开发,编程风格采用寄存器风格的形式。不管用库函数、寄存器还是HAL库,都是一样的,没有太大区别。我写STM32代码的时候习惯用寄存器开发,主要是因为寄存器的代码简洁,工程文件简洁。
keil5软件的下载过程,安装过程,基本用法这里就不详细介绍了。看了这篇文章的道友应该都知道这些基础练习。这里主要介绍基于项目的核心知识点。
6.1 硬件接线
下面是介绍使用的硬件模块与STM32开发板之间的硬件连线。
SIM800C接线说明:
GND----GND
PA2----SIM800C_RXD
PA3----SIM800C_TXD
CH340模块接线说明:
GND----GND
RX-----PA9
GPS接线说明: (波特率需要根据GPS模块实际情况进行修改)
GND----GND
VCC---3.3V
PB11----GPS_TX
蜂鸣器模块: 高电平响
BEEP----->PB8
板载LED灯:
LED1--PC13 低电平亮
板载按键:
KEY1--PA0 按下为高电平
外接按键:
KEY1 -PB3 按下是低电平
KEY2 -PB2 按下是低电平
外接LED灯模块:
LED1-PB4 低电平亮
LED2-PB5 低电平亮
硬件接线:
1 VCC 3.3V/5V 电源输入 ---->接3.3V
2 GND 地线 --->接GND
3 IIC_SDA IIC 通信数据线 -->PB6
4 IIC_SCL IIC 通信时钟线 -->PB7
5 MPU_INT 中断输出引脚 ---->未接
6 MPU_AD0 IIC 从机地址设置引脚-->PA15
AD0引脚说明:ID=0X68(悬空/接 GND) ID=0X69(接 VCC)
注意:陀螺仪初始化的时候,必须正常摆放才可以初始化成功
这是通过杜邦线接好模块后的效果图:
6.2 keil工程截图
6.3 原理图
下面是绘制的原理图。
6.4 MQTT协议实现代码以及MQTT参数
SIM800C本身没有内置MQTT协议指令,只有TCP通信的指令,所以需要自己封装MQTT协议,然后通过TCP通信的相关指令完成云端服务器的连接,实现数据交互。
下面的代码是MQTT协议的参数定义。为了便于修改,使用宏定义来分配这些参数。
//华为物联网服务器的设备信息
#define MQTT_ClientID "GPS1_0_0_2022060716"
#define MQTT_UserName "GPS1"
#define MQTT_PassWord "27a2d2dd716fac29a0041beec1d7cf5f5b529fac65cc815c7eed9adb04d7364b"
//订阅与发布的主题
#define SET_TOPIC "$oc/devices/GPS1/sys/messages/down" //订阅
#define POST_TOPIC "$oc/devices/GPS1/sys/properties/report" //发布
这是封装的几个MQTT协议核心函数:
/*
函数功能: 登录服务器
函数返回值: 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);
return 1;
}
6.4 MPU6050.c代码
这是MPU6050陀螺仪的核心驱动代码,方便检测老人的姿态,判断是否摔倒。
因为文章发表的字数限制问题,无法贴出代码,代码后面整理了通过附件上传。
6.5 GPS.c代码
接收GPS数据之后进行解析,得到经纬度,方便上传到物联网云平台。
#include "gps.h"
/*
函数功能:从buf里面得到第cnt个逗号所在的位置
返 回 值:0~254,代表逗号所在位置的偏移.
255,代表不存在第cnt个逗号
*/
u8 GPS_GetCommaOffset(char *buf,u8 cnt)
{
char *p=buf;
while(cnt)
{
if(*buf=='*'||*buf'z')return 255;//遇到'*'或者非法字符,则不存在第cx个逗号
if(*buf==',')cnt--;
buf++;
}
return buf-p; //计算偏移量
}
/*
函数功能: 获取GPS经纬度数据值
函数参数:
double *Longitude :经度
double *latitude :纬度
返回值: 0表示定位成功,1表示定位失败
说明: 解析$GPRMC命令,得到经纬度
$GNRMC,023705.000,A,2842.4164,N,11549.5713,E,1.73,91.65,150319,,,A*41
转换公式示例:
经度: dddmm.mmmm 东经 11408.4790 114+(08.4790/60)=114.141317
纬度: ddmm.mmmm 北纬 2236.9453 22+(36.9453/60)= 22.615755
中科微返回的数据
$GNRMC,144435.000,A,2942.1201,N,10636.6466,E,1.50,64.42,190422,,,A*40
*/
u8 GPS_GPRMC_Decoding(u8 *gps_buffer,double *Longitude,double *latitude)
{
u8 Offset;
u32 int_data;
double s_Longitude,s_latitude;
char *p;
/*1. 确定下定位是否成功*/
p=strstr((char*)gps_buffer,"$GNRMC");
if(!p)return 1;
Offset=GPS_GetCommaOffset(p,2);
if(Offset==255)return 2;
if(*(p+Offset)!='A')return 3; //定位不准确
/*2. 得到纬度*/
Offset=GPS_GetCommaOffset(p,3);
if(Offset==255)return 4;
sscanf(p+Offset,"%lf",&s_latitude);
s_latitude=s_latitude/100;
int_data=s_latitude;//得到纬度整数部分
s_latitude=s_latitude-int_data;//得到纬度小数部分
s_latitude=(s_latitude)*100;
*latitude=int_data+(s_latitude/60.0); //得到转换后的值
/*3. 得到经度*/
Offset=GPS_GetCommaOffset(p,5);
if(Offset==255)return 5;
sscanf(p+Offset,"%lf",&s_Longitude);
s_Longitude=s_Longitude/100;
int_data=s_Longitude;//得到经度整数部分
s_Longitude=s_Longitude-int_data; //得到经度小数部分
s_Longitude=s_Longitude*100;
*Longitude=int_data+(s_Longitude/60.0);
return 0;
}
7. 上位机APP开发
为了方便查看地图位置,轨迹等信息,当前采用QT编写了一个配套的上位机,通过华为云IOT的应用侧开发接口,获取设备的影子数据,然后再调用百度地图进行显示目标位置。
接下来就介绍上位机软件的开发流程。
7.1 安装Qt开发环境
Qt是个跨平台的C++开发框架,一份代码支持在不同系统平台编译运行。支持Android、IOS、Windows、Linux等平台。
目前我使用的开发环境是:QT 5.12.6 ,其他版本也可以的。
QT5.12.6的下载地址:
https://download.qt.io/archive/qt/5.12/5.12.6/
打开下载链接后选择下面的版本进行下载:
qt-opensource-windows-x86-5.12.6.exe 13-Nov-2019 07:28 3.7G Details
软件安装时断网安装,否则会提示输入账户。安装的时候,第一个复选框里勾选一个mingw 32编译器即可,其他的不管默认就行,直接点击下一步继续安装。
如果只是在windows下开发,最简单的安装就只选择 MinGW编译器即可,其他的编译器不用勾选。
7.2 应用侧开发接口文档
帮助文档地址:数据提交_设备访问IoTDA_用户指南_消息沟通_华为云
目前我的应用主要是读取设备上传的GPS数据。有两种方法可以获得数据:
[1]读取设备的影子数据,即获取设备上传到服务器后的历史数据(非实时数据),也就是设备最后上传的数据。获取影子数据,无论设备是否在线都可以获取。
这里的GPS采集是采集的影子设备数据,也就是上次设备上传的数据。
地址:查询设备影子数据_设备访问IoTDA_API参考_应用API参考_设备影子_华为云
【2】(查询设备属性)读取实时数据,如果需要设备立即发布当前的数据上来,可以发送同步指令给设备,设备端需要编写解析指令的代码,收到指令后设备根据格式回应数据回来。这种同步实时数据需要设备保持在线才可以响应指令。 具体的使用场景可以根据自己需求设计。
通过查询属性的接口,可以主动请求获取设备详细属性。
流程是:应用层调用这个API接口—->请求服务器—–>请求客户端设备——>返回给服务器—–>返回给应用层调用处。
文档地址:查询设备属性_设备接入 IoTDA_API参考_应用侧API参考_设备属性_华为云
7.3 创建IAM账户
这一步很重要,在开发上位机时,需要调用应用侧的一些接口,这些接口都需要带上token登录密匙。而token登录密匙的生成需要IAM账户才获取。
地址:https://console.huaweicloud.com/iam/?region=cn-north-4#/iam/users
(1)创建用户
(2)填充参数
(3)完成创建
7.4 实现代码
下面贴出请求接口的核心代码。
/*
功能: 获取token
*/
void Widget::GetToken()
{
//表示获取token
function_select=3;
QString requestUrl;
QNetworkRequest request;
//设置请求地址
QUrl url;
//获取token请求地址
requestUrl = QString("https://iam.%1.myhuaweicloud.com/v3/auth/tokens")
.arg(SERVER_ID);
//自己创建的TCP服务器,测试用
//requestUrl="http://10.0.0.6:8080";
//设置数据提交格式
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8"));
//构造请求
url.setUrl(requestUrl);
request.setUrl(url);
QString text =QString("{"auth":{"identity":{"methods":["password"],"password":"
"{"user":{"domain": {"
""name":"%1"},"name": "%2","password": "%3"}}},"
""scope":{"project":{"name":"%4"}}}}")
.arg(MAIN_USER)
.arg(IAM_USER)
.arg(IAM_PASSWORD)
.arg(SERVER_ID);
//发送请求
manager->post(request, text.toUtf8());
}
//获取影子设备数据
void Widget::on_pushButton_addr_clicked()
{
//表示影子设备数据获取
function_select=0;
QString requestUrl;
QNetworkRequest request;
//设置请求地址
QUrl url;
//获取token请求地址
requestUrl = QString("https://iotda.%1.myhuaweicloud.com/v5/iot/%2/devices/%3/shadow")
.arg(SERVER_ID)
.arg(PROJECT_ID)
.arg(Device_id);
//自己创建的TCP服务器,测试用
//requestUrl="http://10.0.0.6:8080";
//设置数据提交格式
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
//设置token
request.setRawHeader("X-Auth-Token",Token);
//构造请求
url.setUrl(requestUrl);
request.setUrl(url);
//发送请求
manager->get(request);
}
主要贴出2个比较重要的函数,一个获取token,一个查询设备影子数据。
本文章来源于互联网,如有侵权,请联系删除!