回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页
best-icon

家庭安全相册(三) - Tinkernode部分 简单

头像 szjuliet 2022.07.24 322 3

材料清单

  • TinkerNode NB-IoT 物联网开发板 X1 链接

完整项目

家庭安全相册(一)- 行空板

家庭安全相册(二)- 手机端(被监护人)

家庭安全相册(三)- Tinkernode

家庭安全相册(四)- 手机端(监护人)

开发板简介

物联网与NB-IoT简介

物联网(The Internet of things,IoT)顾名思义,就是物与物相连的互联网。这有两层意思:第一,物联网的核心和基础仍然是互联网,是在互联网基础上的延伸和扩展的网络;第二,其用户端延伸和扩展到了任何物品与物品之间,进行信息交换和通信。

窄带物联网(Narrow Band Internet of Things,NB-IoT)是物联网领域一个新兴的技术,主要用于低移动性、小数据量、对时延不敏感的连接服务,其支持低功耗设备在网络中的数据传输,因此也是一种低功耗广域网(Low Power Wide Area Network,LPWAN)通信技术。相对于被逐渐淘汰的2G通信,NB-IoT具有三大优势:

大连接:海量链接的能力,在同一基站的情况下, NB-IoT 可以比现有无线技术提供 50~100 倍的接入数。一个扇区能够支持,10 万个连接,设备成本与功耗有效降低,网络架构得到优化。广覆盖:在同样的频段下, NB-IoT比现有的网络增益提升了 20 dB,相当于提升了 100 倍的覆盖面积。低功耗:NB-IoT借助 PSM(Power Saving Mode,节电模式)和 eDRX(Extended Discontinuous Reception,超长非连续接收)可实现更长待机。

特色

TinkerNode NB-IoT物联网开发板具有:NB-IoT全网通、GPS/北斗双星精确定位、全方位电源管理、太阳能供电、WiFi+蓝牙4.0二合一EPS32主控、微型U盘等六大特色功能,全方位覆盖物联网项目中的各类需要。

Tinkernode接口说明

project-image

Tinkernode在本项目的作用

因为Tinkernode搭载了北斗和GPS模块,同时有具有窄带物联网的功能。因此对于无法携带手机进校园的中小学生来说,可以作为可穿戴设备携带(如腕带),走到哪里,通过卫星实时获得的位置信息就可以随时发送出去。

但是经过我的数次尝试,随Tinkernode购买附送的联通NB IoT卡信号不好,打过客服的电话,了解到在深圳自己常呆的地方信号都不太好,“很少有绿色,基本都是灰色的”。因此无法通过sim卡将信息发送出来。所以只能通过Tinkernode的wifi功能连接手机热点将信息发送。后面可以尝试购买其他网络运营商的NB IoT卡来看看网络效果。

因为正值暑假,手头材料有限,只能裸机展示。

步骤1 接线

接天线

GSM天线插在板子的NB孔上,GPS/BeiDou陶瓷有源天线连接在开发板背面标有GNSS的u.FL天线连接孔上。这两个孔都非常小,一定要对准才能插的上去。

说明:如果不需要用到NBIoT,可以不用接GSM天线。

project-image

接电池

Tinkernode有一个3.7V锂电池接口,可以接上3.7V的锂电池,带电状态下是给锂电池充电,此时板子上的CHG灯亮红色,脱机运行时可由接在电池接口上的锂电池供电。

project-image

注意事项:

GPS/BeiDou陶瓷有源天线的陶瓷面应该面向天空,尽可能在室外使用,否则无法获取有效的定位信息。

某天晚上在学校测试,当时在空旷的走廊上都没有任何卫星信号,索性抱着手提电脑到操场上绕了一圈,虽然NBIoT仍然没有信号,但是能看到的卫星一下子多了起来。如下图,能够看到20颗卫星,GPS9颗,北斗11颗,一共19颗卫星被用到。需要提醒的是北京在东8区,Tinkernode返回的时间需要加上8小时,实际时间是21点41分多。

project-image

步骤2 编写程序

使用合适的示例程序

本项目只需要实时的经纬度数据,在Tinkernode示例程序中的GNSS(全球导航卫星系统)中有多个示例程序,我们要弄清楚它们代表的是什么意思。

project-image

上图中的GGA、GSA、GSV、RMC、VTG、GLL等是常见的GPS数据协议格式,我们只有理解了这些数据格式,才能对数据进行解析并找到我们需要的数据。

比如GGA返回的数据为:

//$GNGGA,023100.000,2240.63547,N,11403.56956,E,1,22,0.7,230.0,M,0.0,M,,*7D

这些数据代表的含义如下,我们可以根据自己的需要来解析相应字段的数据。

代码
    //$GNGGA,023100.000,2240.63547,N,11403.56956,E,1,22,0.7,230.0,M,0.0,M,,*7D
    字段0:$GPGGA,语句ID,表明该语句为Global Positioning System Fix Data(GGA)GPS定位信息
    字段1:UTC 时间,hhmmss.sss,时分秒格式
    字段2:纬度ddmm.mmmm,度分格式(前导位数不足则补0)
    字段3:纬度N(北纬)或S(南纬)
    字段4:经度dddmm.mmmm,度分格式(前导位数不足则补0)
    字段5:经度E(东经)或W(西经)
    字段6:GPS状态,0=未定位,1=非差分定位,2=差分定位,3=无效PPS,6=正在估算
    字段7:正在使用的卫星数量(00 - 12)(前导位数不足则补0)
    字段8:HDOP水平精度因子(0.5 - 99.9)
    字段9:海拔高度(-9999.9 - 99999.9)
    字段10:M
    字段11:地球椭球面相对大地水准面的高度
    字段12:M
    字段13:差分修正的数据龄期
    字段14:差分参考站的ID
    字段15:CRC
    字段16:回车换行

GNSS示例程序

本项目中,我们只需要经纬度信息(及时间信息),几乎所有示例程序我们都可以使用,这里我们使用GNSS示例程序,它返回的有时间信息、位置信息和卫星信息。

代码
/*!   
 * @file getGNSS.ino
 * @brief Print all the GNSS info available in BC20.
 * 
 * @copyright   Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
 * @licence     The MIT License (MIT)
 * @author      [Wuxiao](xiao.wu@dfrobot.com)
 * @version  V1.0
 * @date  2019-10-29
 * @get from https://www.dfrobot.com
 */
#include "DFRobot_BC20.h"
DFRobot_BC20 myBC20;

void Display_Location_Information(){
  
    //UTC time of the anchor point
    Serial.print("Time:\t\t");
    Serial.print(sCLK.Year);
    Serial.print("/");
    Serial.printf("%02d",sCLK.Month);
    Serial.print("/");
    Serial.printf("%02d ",sCLK.Day);
    Serial.printf(" %02d",sCLK.Hour);
    Serial.printf(":%02d",sCLK.Minute);
    Serial.printf(":%02d\r\n",sCLK.Second);
    
    Serial.print("Latitude:\t");
    Serial.print(sGGNS.LatitudeVal,6);
    Serial.print(" deg ");
    Serial.println(sRMC.LatitudeDir);
    Serial.print("Longitude:\t");
    Serial.print(sGGNS.LongitudeVal,6);
    Serial.print(" deg ");
    Serial.println(sRMC.LongitudeDir);
    Serial.print("Altitude:\t");
    Serial.print(sGGNS.Altitude,1);
    Serial.println(" m");
    Serial.print("Speed:\t\t");
    Serial.print(sGGNS.Speed);
    Serial.println(" km/h");
    Serial.print("Heading:\t");
    Serial.print(sGGNS.Heading);
    Serial.println(" deg");
    Serial.print("Status:\t\t");
    Serial.println(sGGNS.FixStatus);
    Serial.print("PDOP:\t\t");
    Serial.println(sGGNS.PDOP);
    Serial.print("HDOP:\t\t");
    Serial.println(sGGNS.HDOP);
    Serial.print("VDOP:\t\t");
    Serial.println(sGGNS.VDOP);
    Serial.println();
}

void Display_Satellite_Information(){
    Serial.print(sSAT.NUM);
    Serial.println(" in view.");
    Serial.print(sSAT.USE);
    Serial.println(" in used.");
    Serial.print("PRN\t");
    Serial.print("Elev(deg)\t");
    Serial.print("Azim(deg)\t");
    Serial.print("SNR(dBHz)\t");
    Serial.print("SYS\t");
    Serial.println("Used");
    for(uint8_t i = 0; i <sSAT.NUM; i++){
        Serial.print(sSAT.data[i].PRN);
        Serial.print("\t");
        Serial.print(sSAT.data[i].Elev);
        Serial.print("\t\t");
        Serial.print(sSAT.data[i].Azim);
        Serial.print("\t\t");
        Serial.print(sSAT.data[i].SNR);
        Serial.print("\t\t");
        Serial.print(sSAT.data[i].SYS);
        Serial.print("\t");
        Serial.println(sSAT.data[i].Status);
    }
}

void setup(){
    Serial.begin(115200);
    Serial.print("Starting the BC20.Please wait. . . ");
    while(!myBC20.powerOn()){
        delay(1000);
        Serial.print(".");
    }
    Serial.println("BC20 started successfully !");
    Serial.println("check OK");
    if(myBC20.getQGNSSC() == OFF){
        Serial.println("open QGNSSC");
        myBC20.setQGNSSC(ON);
    }
}

void loop(){
    delay(5000);
    myBC20.getQGNSSRD();
    Display_Location_Information();
    Display_Satellite_Information();
}

改写程序

因为我们使用wifi来进行MQTT连接并发布消息,所以需要添加wifi头文件。

虽然使用Tinkernode发送消息没有20个字符的限制(如下图是在EasyIoT上接收到的Tinkernode发送的消息),但是由于我们在行空板已经进行了数据格式的定义,所以在Tinkernode端我们也需要对经纬度进行编码。即经度、纬度加上100再乘以10的6次方,连同家庭成员ID一起发送。

project-image

进行编码后再发送的经纬度消息:

使用相同的经纬度数据,家庭成员ID在1~6中随机产生。

project-image

头文件

代码
#include "DFRobot_BC20.h"
#include <WiFi.h>
#include <PubSubClient.h>
#include "DFRobot_Iot.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

配置wifi

代码
/*配置WIFI名和密码*/
const char * WIFI_SSID     = "";        //填入WiFi SSID
const char * WIFI_PASSWORD = "";        //填入WiFi 密码

配置MQTT参数

代码
/*配置设备的认证信息*/
String Iot_id = "";                        //填入Iot_id
String Client_ID  = "";                        //任意数值,用于区分不同设备
String Iot_pwd    = "";                    //填入Iot_pwd

/*配置IoT云平台的IP地址和端口号*/
String EasyIot_SERVER = "182.254.130.180";
uint16_t PORT = 1883;

/*配置要推送(Publish)或订阅(Subscribe)的topic*/
const char * pubTopic = "";                //填入发布消息的Topic

定义变量

代码

/*位置信息变量*/
float Latitude; /* 经度原始值*/
float Longitude; /* 纬度原始值*/
int Latitude_int; /* 经度转换为整数值*/
int Longitude_int; /* 纬度转换为整数值*/

int id; /*家庭成员ID号,1:爸爸, 2: 妈妈, 3:爷爷, 4:奶奶, 5: 大宝, 6: 小宝*/
String str; /*存放待发送的MQTT消息:ID号,转换为整数的经纬度值*/
String str1; /*存放转换为字符串的原始的经度*/
String str2; /*存放转换为字符串的原始的纬度*/
String joint; /*用于合并信息发送*/

连接WiFi

代码
/*连接WiFi*/
void connectWiFi() {
  WiFi.disconnect();
  delay(100);
  Serial.print("Connecting to ");
  Serial.println(WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected");
  Serial.print("IP Adderss: ");
  Serial.println(WiFi.localIP());
}

连接到IoT云服务器

代码
/*连接到IoT云服务器*/
void ConnectCloud() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect(myEasyIoT._clientId, myEasyIoT._username, myEasyIoT._password)) {
      Serial.println("Connect Server OK");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" Try again in 5 seconds");
      delay(5000);
    }
  }
}

对经纬度进行编码

由于需要在空旷的地方才能获得卫星信号并传回经纬度信息,为了方便展示,这里我们使用固定数值来演示。只要程序能够测试通过,等实际使用时效果是完全一样的。

为了能够在20位以内的字符将信息完整的发送出去,我们仍然要对经纬度进行适当的编码。经度、纬度加上100后再乘以10的6次方转换为整数,前面拼接上ID发送到行空板,行空板再进行解码即可获得原始的经纬度数据。

代码
/*显示位置信息*/
void Display_Location_Information(){    

    Serial.print("Latitude:\t");
    Serial.print(sGGNS.LatitudeVal,6);
    Serial.print(" deg ");
    Serial.println(sRMC.LatitudeDir);
    //Latitude = sGGNS.LatitudeVal;
    Latitude = 22.70537;
    Serial.print("Longitude:\t");
    Serial.print(sGGNS.LongitudeVal,6);
    Serial.print(" deg ");
    Serial.println(sRMC.LongitudeDir);
    //Longitude = sGGNS.LongitudeVal;
    Longitude = 114.2185209;
    str1 = String(Longitude,6); 
    str2 = String(Latitude,6);
    Longitude_int = (Longitude+100) * pow(10,6); /* 将经度编码,加100后转换为整数*/
    Latitude_int = (Latitude+100) * pow(10,6); /* 将纬度编码,加100后转换为整数*/
    str = (String(rand()%6+1)) + String(Longitude_int) + String(Latitude_int); /* 将随机生成的id号和经度纬度拼接*/
}

setup函数

代码
void setup(){
    Serial.begin(115200);

    /*连接WIFI*/
    connectWiFi();

    /*配置需要连接的IoT云服务器(Easy-IoT)*/
    myEasyIoT.init(EasyIot_SERVER, Iot_id, Client_ID, Iot_pwd);
    client.setServer(myEasyIoT._mqttServer, PORT);

    /*连接到Easy-IoT*/
    ConnectCloud();
  
    /*启动BC20*/
    Serial.print("Starting the BC20.Please wait. . . ");
    while(!myBC20.powerOn()){
        delay(1000);
        Serial.print(".");
    }
    Serial.println("BC20 started successfully !");
    Serial.println("check OK");
    if(myBC20.getQGNSSC() == OFF){
        Serial.println("open QGNSSC");
        myBC20.setQGNSSC(ON);
    }
}

loop函数

代码
void loop(){
    delay(5000);
    myBC20.getQGNSSRD();
    Display_Location_Information();
    // Display_Satellite_Information();
    
    /*IoT云服务器断线重连*/
    if (!client.connected()) {
      ConnectCloud();
    }
    client.loop();

    /*每隔一段时间向Easy-IoT发送消息*/
    Serial.println("Sending message to cloud...");
    joint = String("Latitude:") + str1 + String("   Longitude:" )+ String(str2); 
    Serial.println(joint); //将经度和纬度合并一起打印
    client.publish(pubTopic, str.c_str()); //将字符串str转换为publish支持的格式:const char* 
    Serial.println("Message is sent.");
    delay(10000);
}

完整的程序

代码
#include "DFRobot_BC20.h"
#include <WiFi.h>
#include <PubSubClient.h>
#include "DFRobot_Iot.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*配置WIFI名和密码*/
const char * WIFI_SSID     = "";        //填入WiFi SSID
const char * WIFI_PASSWORD = "";        //填入WiFi 密码

/*配置设备的认证信息*/
String Iot_id = "S1ZmZq37SX";                        //填入Iot_id
String Client_ID  = "12345";                        //任意数值,用于区分不同设备
String Iot_pwd    = "SkGQ-937Hm";                    //填入Iot_pwd

/*配置IoT云平台的IP地址和端口号*/
String EasyIot_SERVER = "182.254.130.180";
uint16_t PORT = 1883;

/*配置要推送(Publish)或订阅(Subscribe)的topic*/
const char * pubTopic = "";                //填入Topic

/*位置信息变量*/
float Latitude; /* 经度原始值*/
float Longitude; /* 纬度原始值*/
int Latitude_int; /* 经度转换为整数值*/
int Longitude_int; /* 纬度转换为整数值*/
int id; /*家庭成员ID号,1:爸爸, 2: 妈妈, 3:爷爷, 4:奶奶, 5: 大宝, 6: 小宝*/
String str; /*存放待发送的MQTT消息:ID号,转换为整数的经纬度值*/
String str1; /*存放转换为字符串的原始的经度*/
String str2; /*存放转换为字符串的原始的纬度*/
String joint; /*用于合并信息发送*/

DFRobot_Iot myEasyIoT;
WiFiClient espClient;
PubSubClient client(espClient);
DFRobot_BC20 myBC20;

/*连接WiFi*/
void connectWiFi() {
  WiFi.disconnect();
  delay(100);
  Serial.print("Connecting to ");
  Serial.println(WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected");
  Serial.print("IP Adderss: ");
  Serial.println(WiFi.localIP());
}

/*连接到IoT云服务器*/
void ConnectCloud() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect(myEasyIoT._clientId, myEasyIoT._username, myEasyIoT._password)) {
      Serial.println("Connect Server OK");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" Try again in 5 seconds");
      delay(5000);
    }
  }
}

/*显示位置信息*/
void Display_Location_Information(){
  
    //UTC time of the anchor point

    Serial.print("Latitude:\t");
    Serial.print(sGGNS.LatitudeVal,6);
    Serial.print(" deg ");
    Serial.println(sRMC.LatitudeDir);
    //Latitude = sGGNS.LatitudeVal;
    Latitude = 22.70537;
    Serial.print("Longitude:\t");
    Serial.print(sGGNS.LongitudeVal,6);
    Serial.print(" deg ");
    Serial.println(sRMC.LongitudeDir);
    //Longitude = sGGNS.LongitudeVal;
    Longitude = 114.2185209;
    str1 = String(Longitude,6); 
    str2 = String(Latitude,6);
    Longitude_int = (Longitude+100) * pow(10,6); /* 将经度加100后转换为整数*/
    Latitude_int = (Latitude+100) * pow(10,6); /* 将纬度加100后转换为整数*/
    str = (String(rand()%6+1)) + String(Longitude_int) + String(Latitude_int); /* 将随机生成的id号和经度纬度拼接*/
}

void setup(){
    Serial.begin(115200);

    /*连接WIFI*/
    connectWiFi();

    /*配置需要连接的IoT云服务器(Easy-IoT)*/
    myEasyIoT.init(EasyIot_SERVER, Iot_id, Client_ID, Iot_pwd);
    client.setServer(myEasyIoT._mqttServer, PORT);

    /*连接到Easy-IoT*/
    ConnectCloud();
  
    /*启动BC20*/
    Serial.print("Starting the BC20.Please wait. . . ");
    while(!myBC20.powerOn()){
        delay(1000);
        Serial.print(".");
    }
    Serial.println("BC20 started successfully !");
    Serial.println("check OK");
    if(myBC20.getQGNSSC() == OFF){
        Serial.println("open QGNSSC");
        myBC20.setQGNSSC(ON);
    }
}

void loop(){
    delay(5000);
    myBC20.getQGNSSRD();
    Display_Location_Information();
    // Display_Satellite_Information();
    
    /*IoT云服务器断线重连*/
    if (!client.connected()) {
      ConnectCloud();
    }
    client.loop();

    /*每隔一段时间向Easy-IoT发送消息*/
    Serial.println("Sending message to cloud...");
    joint = String("Latitude:") + str1 + String("   Longitude:" )+ String(str2); 
    Serial.println(joint); //将经度和纬度合并一起打印
    client.publish(pubTopic, str.c_str()); //将字符串str转换为publish支持的格式:const char*
    Serial.println("Message is sent.");
    delay(10000);
}

演示视频

评论

user-avatar
  • DeadWalking

    DeadWalking2024.04.05

    厉害,安全设备自己也能做了

    0
    • 三春牛-创客

      三春牛-创客2023.01.28

      0
      • Fish01

        Fish012022.09.08

        这个太牛了。

        0