飞道的博客

基于STM32设计的物流追踪系统(GPS+BC20+华为云IOT)

462人阅读  评论(0)

1. 前言

随着人们生活节奏的加快,促使物流行业突飞猛进的快速的发展,物流行业的快速发展也导致物流过程出现了一系列的问题。近年来贵重物品在物流中的比例越来越多,同时贵重物流在物流过程中的丢失也越来越多,贵重物品丢失后无法查询在哪个环节丢失的问题引起了物流行业的高度重视。不仅贵重物品需要全程监控,危险品的运输也急需全程的监控,确保在危险品出现事故时可以第一时间解决。

利用GPS技术能够动态采集物流过程中物品的变化信息和地理位置信息,在该系统中加入GPRS模块,利用当前成熟的移动通信技术,在没有有线网络的情况下,也能做到与管理平台数据库之间的通信,既能满足监管平台对物品实时信息的需求,当物品出现丢失时或出现异常替换时,实时报告给监管平台,对物流环节中物品进行全程追踪。有效解决了目前贵重物品和危险品物流过程中信息不能实时采集和物品丢失、掉包的问题。

物流追踪系统结构介绍:基于对贵重物品物流环节的考察,提出了以STM32F103系列的MCU为核心的物流追踪系统,采用移远BC20模块作为上网和GPS定位设备,利用GPS技术实时采集物流过程中的物品的具体信息如地理位置信息和物品变动信息,实时上传到物联网数据平台,这里物联网平台采用的是华为云IOT,并且设计了专用的Android手机APP和windows桌面管理软件,可以实时获取所有电子标签的地理位置,调用百度地图接口,显示地理位置。这样能够时,用户,监管平台能实时了解到标签的位置。

整个产品实现流程是:

设备端实时采集GPS数据,并登录华为云物联网服务器,保持与服务器连接;手机APP或者电脑管理软件,可以查看当前在线的设备列表,可以选择某个设备主动获取信息,当设备收到APP发来的数据获取请求后,就将GPS数据传递出去,手机APP收到数据后就解析GPS经纬度,调用百度地图显示当前设备所处的位置。

运行效果

2. 创建产品

(1)找到物联网设备的页面,选择设备接入

官网地址: https://www.huaweicloud.com/s/JeeJqeiBlOe9kSU

(2)选择免费试用

(3)创建产品

(4)设置产品的一些信息,完成注册

(5)产品创建成功后,提示了产品ID,点击查看详情,可以看到创建的产品

(6)查看产品信息。到此,产品创建完成

3. 自定义数据模型

(1)在产品的页面,下面有一个自定义产品模型的选项,这个选项可以定义设备上传的数据模型,也就是数据的类型。

(2)添加属性。

增加经度、纬度两个属性。

4. 创建设备

一个产品目录下可以创建很多个设备,作为电子标签设备,设备端一般支持自动创建设备,这样设备在终端添加进来的时候,就自动在平台产品下完成注册,方便设备的动态添加和删除。如果有固定的设备,不需要动态添加和删除,也可以在网页上手动创建设备。

下面介绍,在网页上手动创建设备的步骤。

(1)选择设备页面,点击右上角的设备创建,填入设备的信息,点击确定

(2)注册成功会提示设备的很多信息,需要保存起来

(3)弹出的下载框里就是设备的ID等信息

{
   
    "device_id": "6210e8acde9933029be8facf_dev1",
    "secret": "12345678"
}

(4)设备创建成功,在设备页面上可以看到设备还未激活,需要设备终端登录一次就可以成功激活

5. 生成MQTT登录信息

设备终端登录服务器需要账号、密码等信息,有了这些身份验证才能正常的登录到服务器。

注意:一个账号不能在多台设备终端使用,每个设备终端应该是自己唯一的账号密匙,这样上传到服务器的数据才不会错乱。

华为云提供的MQTT账户信息生成在线小工具: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/

这里填入的信息,就是在创建设备时提示下载保存的密匙信息,照着填入,点击生成即可得到MQTT的密匙。

ClientId  6210e8acde9933029be8facf_dev1_0_0_2022021913
Username  6210e8acde9933029be8facf_dev1
Password  6cea55404b463e666cd7a6060daba745bbaa17fe7078dfef45f8151cdf19673d

6. 设备与服务器数据交互的流程

当前设备登录华为云物联网服务器的协议是采用MQTT,MQTT协议里的数据交互方法就是:主题的订阅与发布。

这个订阅比较好理解,就相当于抖音里的关注一下,你关注某个主播或者好友,当他发布新动态时,你就可以收到他的消息了。所以,对于设备端而言,需要订阅服务器的主题,当服务器有新消息时,设备端就能收到数据。设备端的主题发布就是向服务器发送信息。

(1)华为云物联网服务器的地址信息

端口: 1883
域名: a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
IP地址: 121.36.42.100

(2)华为云IOT平台MQTT协议订阅主题的格式

格式: $oc/devices/{
   device_id}/sys/messages/down
//订阅主题: 平台下发消息给设备
$oc/devices/6210e8acde9933029be8facf_dev1/sys/messages/down

(3)华为云IOT平台MQTT协议上报主题的格式

格式: $oc/devices/{
   device_id}/sys/properties/report
//设备上报主题请求
$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/report
//上报的数据格式如下
{
   "services": [{
   "service_id": "gps","properties":{
   "longitude":12.345,"latitude":33.345}}]}

参数说明:

device_id 设备ID这个字段,在创建设备时生成的。

service_id这个字段填服务ID,这个ID在产品页面下面自定义模型的位置可以看到,这个服务ID就在创建数据模型时设置的。

后面的{}里longitude和latitude表示上传到服务器的数据属性字段。这个属性的名称是自己在自定义模型页面自己填的。

7. 软件模拟设备登录测试

这里先使用MQTT客户模拟真实设备,登录服务器激活设备,上传数据测试。

当前使用的MQTT客户端软件下载地址:https://download.csdn.net/download/xiaolong1126626497/18784012

(1)填入参数登录,上传数据

(2)登录云端服务器查看设备效果。正常情况下设备已经处于在线状态,并且收到了设备上传的数据。

8. 开发电子标签管理软件

软件采用QT设计的,获取在线设备,获取设备数据,并调用百度地图显示设备的位置。

STM32设备端的完整源码下载: https://download.csdn.net/download/xiaolong1126626497/85688864

Android、windows上位机源码、可执行文件完整资料包下载:https://download.csdn.net/download/xiaolong1126626497/85688883

8.1 创建IAM账户

创建一个IAM账户,方便接下来使用API接口访问华为云服务时,生成token登录密匙。

地址: https://console.huaweicloud.com/iam/?region=cn-north-4#/iam/users

账户创建好之后,代码里就可以编写一个获取Token的函数。

/*
功能: 获取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());
}

8.2 查询设备列表

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

官方提供了API接口,可以直接获取产品下面的所有设备详细信息返回。

关于请求参数,返回结果的字段含义,在帮助文档里有详细介绍。

URL格式: /v5/iot/{
   project_id}/devices

示例:  

https://iotda.cn-north-4.myhuaweicloud.com/v5/iot/0f2d61e43600f4e22f74c003616710bc/devices?product_id=6210e8acde9933029be8facf&is_cascade_query=false&limit=10&marker=ffffffffffffffffffffffff&offset=0

接口的在线调试地址:https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=IoTDA&api=ListDevices

返回的结果:

{
   
 "devices": [
  {
   
   "app_id": "1af45e3938bb4482bc0be0a5cb3089e3",
   "app_name": "DefaultApp_620esbs1",
   "device_id": "6210e8acde9933029be8facf_dev2",
   "node_id": "dev2",
   "gateway_id": "6210e8acde9933029be8facf_dev2",
   "device_name": "dev2",
   "node_type": "GATEWAY",
   "description": null,
   "fw_version": null,
   "sw_version": null,
   "device_sdk_version": null,
   "product_id": "6210e8acde9933029be8facf",
   "product_name": "GPS",
   "status": "INACTIVE",
   "tags": []
  },
  {
   
   "app_id": "1af45e3938bb4482bc0be0a5cb3089e3",
   "app_name": "DefaultApp_620esbs1",
   "device_id": "6210e8acde9933029be8facf_dev1",
   "node_id": "dev1",
   "gateway_id": "6210e8acde9933029be8facf_dev1",
   "device_name": "dev1",
   "node_type": "GATEWAY",
   "description": null,
   "fw_version": null,
   "sw_version": null,
   "device_sdk_version": null,
   "product_id": "6210e8acde9933029be8facf",
   "product_name": "GPS",
   "status": "OFFLINE",
   "tags": []
  }
 ],
 "page": {
   
  "count": 2,
  "marker": "6210efa980c60c11be19ead1"
 }
}

上面的返回结果里通过JSON数组保存了设备信息,每一个设备就是一个独立的对象,上面的数据里返回了两个设备的信息,说明产品的目录下创建了两个设备。

应用层编写代码完成设备列表获取:

//查询所有设备
void Widget::Get_AllDevice()
{
   
    //查询设备列表
    function_select=1;

    QString requestUrl;
    QNetworkRequest request;

    //设置请求地址
    QUrl url;

    //设备列表请求地址
    requestUrl = QString("https://iotda.%1.myhuaweicloud.com/v5/iot/%2/devices?product_id=%3&is_cascade_query=false&limit=10&marker=ffffffffffffffffffffffff&offset=0")
                 .arg(SERVER_ID)
            .arg(PROJECT_ID)
            .arg(Product_id);

    //设置数据提交格式
    request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));

    //设置token
    request.setRawHeader("X-Auth-Token",Token);

    //构造请求
    url.setUrl(requestUrl);

    request.setUrl(url);

    //发送请求
    manager->get(request);
}

服务器返回的结果解析:

//查询设备列表
    if(function_select==1)
    {
   
        //清空原来的设备列表
        ui->comboBox->clear();
        device_id_lis.clear();

        //解析数据
        QJsonParseError json_error;
        QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
        if(json_error.error == QJsonParseError::NoError)
        {
   
            QJsonObject obj = document.object();

            //判断是否是对象,然后开始解析数据
            if(document.isObject())
            {
   
                QJsonObject obj = document.object();
                if(obj.contains("devices"))
                {
   
                    QJsonArray array=obj.take("devices").toArray();
                    for(int i=0;i<array.size();i++)
                    {
   
                        QJsonObject obj1=array.at(i).toObject();

                        //得到设备ID
                        if(obj1.contains("device_id"))
                        {
   
                            QString device_id=obj1.take("device_id").toString();
                            device_id_lis.append(device_id);
                            ui->comboBox->addItem(device_id);

                            qDebug()<<"device_id:"<<device_id;
                        }
                    }
                }
            }
         }
        return;
    }

8.3 查询设备属性

(1)应用端查询设备属性的请求

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

(2)在线调试地址

接口的在线调试地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=IoTDA&api=ListProperties

(3)设备端响应的数据格式

帮助文档: https://support.huaweicloud.com/api-iothub/iot_06_v5_3011.html

(4)使用总结

上位机APP向设备端请求查询设备属性时,设备端会收到如下的消息:

$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/get/request_id=5f359b5c-542f-460e-9f51-85e82150ff4a{
   "service_id":"gps"}

设备端需要解析这个字符串,得到里面的request_id=5f359b5c-542f-460e-9f51-85e82150ff4a 值。在向服务器回应时,要带上这个请求ID。

设备端响应的主题格式: $oc/devices/{
   device_id}/sys/properties/get/response/request_id={
   request_id}

示    例:
$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/get/response/request_id=6c36c85e-68e1-4d01-a2a3-b89f09bd0427

设备端响应的数据格式:
{
   "services": [{
   "service_id": "gps","properties":{
   "longitude":12.345,"latitude":33.345}}]}


应用端上位机收到设备端的响应数据:
"{\"response\":{\"services\":[{\"service_id\":\"gps\",\"properties\":{\"longitude\":12.345,\"latitude\":33.345}}]}}"

(5)应用端获取设备属性

   //查询设备属性
void Widget::Get_device_properties()
{
   
    //表示获取token
    function_select=0;

    QString requestUrl;
    QNetworkRequest request;

    //设置请求地址
    QUrl url;

    if(device_id_lis.size()<=0)
    {
   
        //显示错误代码
        QMessageBox::information(this,"提示","未选择设备,请先获取设备列表\n选择设备后重试.",QMessageBox::Ok,QMessageBox::Ok);
        return;
    }

    //获取token请求地址
    requestUrl = QString("https://iotda.%1.myhuaweicloud.com/v5/iot/%2/devices/%3/properties?service_id=%4")
                 .arg(SERVER_ID)
            .arg(PROJECT_ID)
            .arg(device_id_lis.at(ui->comboBox->currentIndex()))
            .arg(service_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);
}

(6)应用端解析数据

    //查询设备属性
    if(function_select==0)
    {
   
        //解析数据
        QJsonParseError json_error;
        QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
        if(json_error.error == QJsonParseError::NoError)
        {
   
            //判断是否是对象,然后开始解析数据
            if(document.isObject())
            {
   
                QJsonObject obj = document.object();
                if(obj.contains("response"))
                {
   
                    QJsonObject obj1=obj.take("response").toObject();
                    if(obj1.contains("services"))
                    {
   
                         QJsonArray array=obj1.take("services").toArray();

                         QDateTime current_date_time =QDateTime::currentDateTime();
                         QString current_date =current_date_time.toString("yyyy/MM/dd hh:mm:ss.zzz");
                         QString display_time="更新时间:"+current_date+"\n";
                         for(int i=0;i<array.size();i++)
                         {
   
                             QJsonObject obj2=array.at(i).toObject();
                             if(obj2.contains("properties"))
                             {
   
                                 QJsonObject obj3=obj2.take("properties").toObject();


                                 if(obj3.contains("longitude"))
                                 {
   
                                     //经度
                                     longitude_jin=obj3.take("longitude").toDouble();
                                     qDebug()<<"longitude_jin:"<<longitude_jin;
                                 }

                                 if(obj3.contains("latitude"))
                                 {
   
                                     //纬度
                                     latitude=obj3.take("latitude").toDouble();
                                     qDebug()<<"latitude:"<<latitude;
                                 }

                                 display_time+=QString("GPS经度:%1\n").arg(longitude_jin);
                                 display_time+=QString("GPS纬度:%1\n").arg(latitude);
                                 ui->label_gps_info->setText(display_time);
                             }
                         }
                    }
                }
            }
         }
        return;
    }

9. 开发电子标签设备端(软件模拟)

下面采用QT加MQTT协议开发一款真实设备模拟软件,与电子标签上位机管理软件完成数据交互。

下面图片左边是模拟设备端,右边是电子标签管理的上位机。可以快速模拟应用层与设备端的交互。

软件与服务器的交互都是通过华为云提供的HTTP接口实现的,具体的交互方法和格式,在第8小节已经讲过了,软件里只是使用具体的代码来实现。

工程代码:

10. STM32+BC20开发设备端(真实设备)

STM32设备端的完整源码下载: https://download.csdn.net/download/xiaolong1126626497/85688864

Android、windows上位机源码、可执行文件完整资料包下载:https://download.csdn.net/download/xiaolong1126626497/85688883

本小节介绍STM32+BC20连接华为云物联网平台,实现与上位机之间进行数据交互,完成真实的产品开发。

10.1 BC20模块

BC20是一款高性能、低功耗、多频段、支持 GNSS 定位功能的 NB-IoT 无线通信模块。BC20 在设计上兼容移远通信 GSM/GPRS/GNSS 系列的 MC20 模块,方便客户快速、灵活的进行产品设计和升级。BC20 提供丰富的外部接口和协议栈,同时支持中国移动 OneNET 物联网云平台,为客户的应用提供极大的便利。

BC20支持北斗、GPS、QZSS 等多星座卫星系统解调算法,其定位更加精准,抗多路径干扰能力更强,比传统的单GPS 模块具有更多优势。另外,BC20 模块中内置 LNA 和低功耗算法:前者保证更高的灵敏度,后者保证低功耗模式下更低的耗流。

BC20 模块较传统 NB-IoT+GNSS 方案体积减少 40%。凭借其紧凑尺寸、超低功耗和超宽工作温度范围,BC20 在各种应用中占具更大优势;其主要应用领域为:自行车和摩托车防盗、宠物追踪、金融财产追踪及行车记录仪等等。

C20 模块集成了 NB-IoT 和 GNSS(GPS+BeiDou) 双系统,在网络交互的同时, 实现 GNSS 系统的
快速、精准定位, 满足客户低功耗与高定位精度的应用场景。

相比传统的具有单一 GPS 功能的模块, BC20 的主要优势如下:

a. 内嵌的 GNSS 模块,支持 GPS+BeiDou 双系统定位: 相同环境下可使用的卫星数量更多, 搜星的
b. 时间更短, 可加快定位速度, 提高定位精度;
c. NB 和 GNSS 组合的小尺寸模块, 具备优良的环境适应性, 具备低功耗、抗干扰、高精度的特性;
d. 内置 Sensor Hub 及领先的 PDR 算法,完美提升定位精度;
e. 智能的 AGPS 辅助定位功能,加快冷启动模式下的定位速度

淘宝商店地址: https://m.tb.cn/h.fOCCkgV?sm=5ffdfe?tk=MkB92eHI0ZV

模块上有两排接口,一个是GPS信号输出接口,一个是BC20控制接口。

使用USB转TTL模块,将BC20板子与电脑连起来,调试板子是否正常。

10.2 测试模块

第一步接上之后,串口调试助手选择波特率为115200,勾选软件上的发送新行选项。发送AT过去,正常模块会返回OK

10.3 上电初始化操作

查询模块是否正常
AT

OK


获取卡号,查询卡是否插好
AT+CIMI

460041052911195

OK


激活网络
AT+CGATT=1

OK


获取网络激活状态
AT+CGATT?

+CGATT: 1

OK


查询网络质量
AT+CSQ

+CSQ: 26,0

OK
    
AT+CEREG=? //检查网络状态
+CEREG: 0,1 //找网成功
OK

10.4 开启GPS定位

官方文档:

激活GPS,要等一段时间
AT+QGNSSC=1

OK


查询激活状态,1表示成功激活
AT+QGNSSC?

+QGNSSC: 1

OK


获取一次GPS定位语句
AT+QGNSSRD="NMEA/RMC"
+QGNSSRD: $GNRMC,120715.00,A,3150.78179,N,11711.93433,E,0.000,,310818,,,A,V*19
OK

10.5 连接MQTT服务器

下面通过MC20的AT指令连接华为云服务器,上传数据测试。

官方文档:

连接MQTT服务器
AT+QMTOPEN=0,"a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com",1883

OK

+QMTOPEN: 0,0


登录MQTT服务器
命令格式: AT+QMTCONN=<tcpconnectID>,<clientID>,<username>,<password>
AT+QMTCONN=0,"6210e8acde9933029be8facf_dev1_0_0_2022021913","6210e8acde9933029be8facf_dev1","6cea55404b463e666cd7a6060daba745bbaa17fe7078dfef45f8151cdf19673d"

OK

+QMTCONN: 0,0,0


订阅主题
命令格式: AT+QMTSUB=<tcpconnectID>,<msgID>,"<topic1>”,<qos1>[,"<topic2>,<qos2>]

AT+QMTSUB=0,1,"$oc/devices/6210e8acde9933029be8facf_dev1/sys/messages/down",2

OK

+QMTSUB: 0,1,0,2


发布主题
命令格式:AT+QMTPUB=<tcpconnectID>,<msgID>,<qos>,<retain>,"<topic>","<msg>"

先发送指令: 
AT+QMTPUB=0,0,0,0,"$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/repor"

等待返回 ">" 
接着发送数据.不需要加回车。
"{"services": [{"service_id": "gps","properties":{"longitude":12.345,"latitude":33.345}}]}"
数据发送完毕,再发送结束符。 十六进制的值--0x1a  。某些串口调试助手可以适应ctrl+z 快捷键输入0xA
等待模块返回"OK",到此数据发送完成。    
OK

+QMTPUB: 0,0,0

10.6 STM32开发板

10.7 STM32工程代码

(1)程序下载

(2)工程源码

10.8 设备登录到华为云调试

(1)硬件实物图



(2)上电后,串口助手打印的信息

(3)在华为云上使用消息跟踪,查看设备与云端交互的过程

到此,整个产品开发测试完成。


转载:https://blog.csdn.net/xiaolong1126626497/article/details/123767239
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场