乐鑫ESP32+BC260Y+L76K物联网开发板编程入门(物联网篇)

一、开发环境

1.1.概述

​ 由于之前在ESP32最小开发板的文章中已经详细说明了如何实现WiFi联网并获取天气数据,因此这里不在详细介绍ESP32模组的WiFi功能,可参考之前的文章介绍,这个文章重点实现通过运营商基站网络进行通讯,并读取位置传感器信息发布至MQTT服务器。

​ 我们的ESP32+BC260Y+L76K物联网开发板目的是实现GPS卫星定位功能,并通过运营商基站网络进行通讯,将位置信息发送到物联网平台进行下一步的处理,GPS定位是需要户外进行,否则会搜索不到卫星数据,在户外我们是无法使用ESP32的WiFi功能(即便是有),因此我们只能借助运营商的基站无线网络,这里选择的是NB-IoT网络,选用的模组是移远的BC260Y,选用NB-IoT通讯协议主要是为了低成本,低功耗,覆盖面广,它无法与4G网络速度相比,如果想要实时传输大量数据还是需要4G网络,后期我们会陆续迭代升级。

​ 定位模组选用的是移远的L76K,关于通讯模组和定位模组这里不在详细说明,可参考官方的数据手册。

​ 在户外需要电池来供电,我们在开发板中设计了电池供电、充电功能,以方便实验测试。

1.2.准备工作

1.2.1.硬件

  • ESP32+BC260Y+L76K物联网开发板
  • 物联网卡
  • 网络通讯天线
  • GPS天线
  • USB 数据线 (Type-C 数据线)
  • 电脑(Windows 10)

1.2.2.软件

准备软件安装包:

我们在开发资料包中提供的Arduino IDE版本是 2.3.2

如需要安装其它版本,可到Arduino官网去另行下载并安装。

Arduino官网地址: https://www.arduino.cc/en/software

安装过程不在赘述,可参考之前的文档。

二、运营商基站NB-IoT网络通讯

2.1.初始化BC260Y通讯模组

我们打开Arduino IDE开发工具,新建项目ESP32_BC260Y_INIT,输入代码:

#include <Arduino.h>
#include <Quectel_BC260Y.h>

/*定义移远BC260Y串口,使用ESP32的UART2:RX=GPIO16, TX=GPIO17*/
#define SERIAL_PORT Serial2

// 初始化移远BC260Y库
Quectel_BC260Y quectel;

void setup() {
  // 初始化串口波特率
  Serial.begin(9600);
  Serial.println("\r\nQuectel BC260Y Module init example");
  Serial.println("=================================================================");
  // 初始化BC260Y
  quectel.begin(&SERIAL_PORT);
  // 查询固件版本号
  Serial.print("FirmwareVersion: ");
  Serial.println(quectel.getFirmwareVersion());
  // 查询网络日期时间
  Serial.print("Time: ");
  Serial.println(quectel.getDateAndTime());
  // 查询IMSI
  Serial.print("IMSI: ");
  Serial.println(quectel.getIMSI());
  // 查询ICCID
  Serial.print("ICCID: ");
  Serial.println(quectel.getICCID());
  // 查询网络信号质量
  Serial.print("RSSI: ");
  Serial.println(quectel.getRSSI());
  // 查询网络状态
  Serial.print("Status code: ");
  Serial.println(quectel.getStatusCode());
  Serial.print("Status: ");
  Serial.println(quectel.getStatus());
}

void loop() {
}

编译并上传代码到开发板,可以看到运行结果,串口监视器中会打印出网络基本信息。

image-20241207133255513

返回Status code状态码为1表明已经正常注册到网络。

三、GPS/GNSS定位

3.1.初始化L76K定位模组

开发之前,我们需要在Arduino中引入两个第三方库,EspSoftwareSerial和TinyGPSPlus。

我们打开Arduino IDE开发工具,新建项目L76K_INIT,输入代码:

#include <Arduino.h>
#include <SoftwareSerial.h>
#include <TinyGPSPlus.h>

// GPIO16、GPIO17引脚用于实现跟BC260Y的UART通讯,使用ESP32的UART2
#define RXD2 16
#define TXD2 17
// GPIO4、GPIO14引脚用于实现跟L76K的UART通讯 //
#define GPSRXD 14
#define GPSTXD 4

HardwareSerial SerialPort(2);
SoftwareSerial GPSSerial(GPSTXD, GPSRXD);
TinyGPSPlus gps;

// 定义GNSS数据结构
struct gnssDataStruct {
  String StatelliteNum;  // 卫星数量
  bool StatelliteIsValid;
  bool GpsLocationIsValid;
  double Latitude;   // 经度
  double Longitude;  // 纬度
  String Altitude;   // 海拔高度
  bool AltitudeIsValid;
  String Speed;     // 速度
  String UTC_Date;  // 网络授时
  String UTC_Time;  // 网络授时
  TinyGPSDate date;
  TinyGPSTime time;
};
gnssDataStruct gnssData;

// 定义延迟函数
static void smartDelay(unsigned long ms) {
  unsigned long start = millis();
  do {
    while (GPSSerial.available())
      gps.encode(GPSSerial.read());
  } while (millis() - start < ms);
}

// 获取GPS/GNSS数据
void get_GNSSData(unsigned long ms) {
  while (GPSSerial.available() > 0) {
    int c = GPSSerial.read();
    if (gps.encode(c)) {
      gnssData.StatelliteNum = gps.satellites.value();
      gnssData.StatelliteIsValid = gps.satellites.isValid();
      gnssData.GpsLocationIsValid = gps.location.isValid();
      gnssData.Latitude = gps.location.lat();
      gnssData.Longitude = gps.location.lng();
      gnssData.Altitude = gps.altitude.meters();
      gnssData.AltitudeIsValid = gps.altitude.isValid();
      gnssData.Speed = gps.speed.mps();
      String day = String(gps.date.day());
      String month = String(gps.date.month());
      String year = String(gps.date.year());
      gnssData.UTC_Date = day + "/" + month + "/" + year;
      String hour = String(gps.time.hour());
      String minute = String(gps.time.minute());
      String second = String(gps.time.second());
      gnssData.UTC_Time = hour + "/" + minute + "/" + second;
      gnssData.date = gps.date;
      gnssData.time = gps.time;
    }
  }
  smartDelay(1000);
}

void setup() {
  // 初始化串口波特率
  Serial.begin(9600);
  SerialPort.begin(9600, SERIAL_8N1, RXD2, TXD2);
  GPSSerial.begin(9600);
}

void loop() {
  // 获取GPS数据
  get_GNSSData(3000);
  // 串口打印数据
  // 纬度
  Serial.print("Latitude:");
  Serial.println(gnssData.Latitude);
  // 经度
  Serial.print("Longitude:");
  Serial.println(gnssData.Longitude);
  // 海拔
  Serial.print("Altitude:");
  Serial.println(gnssData.Altitude);
  // 速度
  Serial.print("Speed:");
  Serial.println(gnssData.Speed);
  // 卫星数量
  Serial.print("StatelliteNum:");
  Serial.println(gnssData.StatelliteNum);
  // 日期
  Serial.print("Date:");
  Serial.println(gnssData.UTC_Date);
  // 时间
  Serial.print("Time:");
  Serial.println(gnssData.UTC_Time);

  delay(200);
}

编译并上传代码到开发板,可以看到运行结果,串口监视器中会打印出解析到的GPS基本信息。

L76K_INIT

注意事项:程序初次运行,由于有搜索卫星的动作,需要等待一段时间才可以看到解析到的GPS位置数据

四、物联网平台IoT

4.1.向MQTT服务器发布GPS数据

我们在最小开发板的文章中详细讲解了MQTT服务器的环境安装和配置,这里就不在赘述关于开发环境的搭建,利用之前学习过的知识我们可以轻松的实现将GPS数据构建成json格式的字符串,通过NB-IoT网络发送到MQTT服务器。

我们打开Arduino IDE开发工具,新建项目Esp32_GpsData_Send_MQTTServer,输入代码:

#include <Arduino.h>
#include <Quectel_BC260Y.h>
#include <ArduinoJson.h>

// 定义移远BC260Y串口,使用ESP32的UART2:RX=GPIO16, TX=GPIO17//
#define SERIAL_PORT Serial2

// 初始化移远BC260Y库
Quectel_BC260Y quectel;

// MQTT服务器
const char *mqtt_broker = "MQTT服务器地址";   // 服务器地址
const char *clientID = "HTKI-0001";         // 自定义clientid
const char *topic = "hantu_iot/testtopic";  // 定义 Topic
const char *mqtt_username = "用户名";        // 用户名
const char *mqtt_password = "密码";          // 密码
const int mqtt_port = 1883;                 // 端口

// 定义变量
String payload = "";
JsonDocument jsonbuf;

const long interval = 20000;//定义发布数据时间间隔,单位ms,这里表示20秒发一次
unsigned long previousMillis = 0;
int cnt = 0;

// 生成payload上传信息json格式
void genPayloadInfo() {
  payload = "";

  // 序列化网络基础信息
  jsonbuf["FirmwareVersion"] = quectel.getFirmwareVersion();
  jsonbuf["DateTime"] = removeQuotes(quectel.getDateAndTime());
  jsonbuf["IMSI"] = quectel.getIMSI();
  jsonbuf["ICCID"] = quectel.getICCID();
  jsonbuf["RSSI"] = quectel.getRSSI();

  // 序列化GPS信息
  char lat[sizeof(double)];
  char lng[sizeof(double)];
  jsonbuf["Latitude"] = quectel.gnssData.Latitude;
  jsonbuf["Longitude"] = quectel.gnssData.Longitude;
  jsonbuf["Altitude"] = quectel.gnssData.Altitude;
  jsonbuf["Speed"] = quectel.gnssData.Speed;
  jsonbuf["Nsat"] = quectel.gnssData.StatelliteNum;
  jsonbuf["Date"] = quectel.gnssData.UTC_Date;
  jsonbuf["Time"] = quectel.gnssData.UTC_Time;

  // 序列化电池数据信息
  quectel.readBattery();
  jsonbuf["Battery_Voltage"] = quectel.batData.Voltage;
  jsonbuf["Battery_Percent"] = quectel.batData.Percent;

  // 生成json字符串payload
  serializeJson(jsonbuf, payload);
}

// 读取GPS/GNSS位置传感器值
void readSensors() {
  quectel.read_GNSS_Data(3000);
}

// 发布数据到MQTT服务器
void publishData() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // 计数器,用于显示发送数据的条数,这里是为了观察发送数据的稳定性
    cnt++;
    // 读取传感器数据
    readSensors();
    // 生成传感器数据发布payload
    genPayloadInfo();
    String lastTemp = String(payload) + String(cnt);
    int lastTempLength = lastTemp.length();
    quectel.publishMQTT(lastTemp.c_str(), lastTempLength, "hantu_iot/testtopic");
    previousMillis = currentMillis;
  }
}

// 连接到NB-IoT运营商网络
void connectToNBIoT() {
  Serial.println("Connect to NB-IoT network");
  Serial.println("=================================");
  while (!quectel.getRegistrationStatus(5)) {
    Serial.println("Waiting for network registration...");
    delay(5000);
  }
  Serial.println("Module is successfully registered to network");
}

// 创建连接到MQTT服务器
void connectToMQTT() {

  quectel.configMQTT();
  Serial.println("\nTurn off deep sleep mode");
  quectel.setDeepSleep();
  delay(1000);

  Serial.println("\nOpen MQTT connection");
  quectel.openMQTT(mqtt_broker);
  delay(1000);

  Serial.println("\nConnect to MQTT broker");
  quectel.connectMQTT(clientID, mqtt_username, mqtt_password);
  delay(1000);
}

void setup() {
  // 初始化串口波特率
  Serial.begin(9600);
  // 初始化BC260Y
  quectel.begin(&SERIAL_PORT);
  // BC260Y模组连接NB-IoT网络并注册
  connectToNBIoT();
  // 连接到MQTT服务器
  connectToMQTT();
}

void loop() {
  // 发布数据
  publishData();
}

//去除字符串中的引号
String removeQuotes(String str) {
  String result = "";
  for (int i = 0; i < str.length(); i++) {
    if (str[i] != '"' && str[i] != '\'') {
      result += str[i];
    }
  }
  return result;
}

编译并上传代码到开发板,程序运行后会发送数据到MQTT服务器,我们需要借助MQTTX工具来查看服务器接收到的数据

image-20241208120621449

通过数据我们可以看到开发板会每20秒向服务器发送一条数据信息,包含了我们的BC260Y硬件信息,GPS位置信息,以及我们的电池电量。

目前我们只是得到了这些数据,下一步我们要做的处理是把数据展示并存储到数据库中,以便后期进一步的应用开发,这里我们需要用Node-RED工具进一步的处理。

五、Node-RED数据面板

5.1.Node-RED数据面板

之前我们介绍过使用Node-RED来开发数据面板,使用的组件是dashboard,dashboard有了新的升级版本V2.0,我们目前也升级到了该版本

安装节点@flowfuse/node-red-dashboard

官网:Node-RED Dashboard 2.0

在线文档:Widgets | Node-RED Dashboard 2.0

在线示例:km/dashboard_2_0.json at main · nygma2004/km · GitHub

由于需要使用地图,这里我们也引入了高德地图,需要开发者到高德地图开发平台上申请开发者key和密钥,这个过程不在赘述。

Node-RED工作流如下所示
image-20241218124642320

运行界面截图:
image-20241216155739594

image-20241216154329243

因为是Web界面服务,手机端也可以直接访问
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值