Arduino与墨子号 BC26 4G模块的对接开发

最近开始要做物联网的一个设备,由于是外用就考虑到了Arduino UNO小板跟4G模块,后面淘宝找到了一款已用的4G模块-墨子号BC26(注意:不是打广告哦),由于4G模块都是At命令操作的,跟Arduino UNO对接使用有些不方便使用,所以封装成了一个Arduino的c++类,很简单,但是也遇到一些奇葩问题,发到csdn,做个备注!
我的c++基础不是很好,有什么地方可以优化内存的,欢迎各位大佬指出!

Audrion 主类:

#include "BC26Socket.h"

int socketId = 1;
int socketPort = 8888;
String socketHost = "47.92.146.210";
Socket socket;
SoftwareSerial socketSerial = SoftwareSerial(A0, A1); // arduino与bc26通讯的串口
boolean isCreateConnect = false; // 是否创建连接成功
boolean isConnect = false; // 是否连接到服务器
boolean isConnectNetWork = false; // 是否连接到网络
long connectTime = 0L; // 开始连接时间
long lastSendHeartbeatTime = 0L; // 最后发送心跳包时间
long lastHeartbeatTime = 0L; // 最后接收到心跳时间

void socketConnect(); // socket连成功监听
void socketClose(); // socket断开监听
void socketMessage(String msg, int length); // socket消息监听
void sendHeartbeat(); // 发送心跳检测
void checkConnect(); // 检测连接
void checkHeartbeat(); // 检测心跳

const char* Heartbeat = "hb"; // 心跳

void setup() {
  Serial.begin(9600);
  socketSerial.begin(9600);
  socketSerial.setTimeout(100); // 设置每次读取间隔时间 , socket采用readString方法进行读取数据
  socket.initSocket(socketId, socketHost, socketPort, &socketSerial);
  socket.addSocketConnectListener(socketConnect);
  socket.addSocketCloseListener(socketClose);
  socket.addSocketMessageListener(socketMessage);
  socket.setLocalPort(6666);
  socket.seeIpAt();
}

void loop() {
  if (!isConnectNetWork) {// 如果未连接到网络 继续监测网络状态
    isConnectNetWork = socket.checkNetWork();
    if (isConnectNetWork) {
      Serial.println("netWork OK");
    } else {
      Serial.println("netWork Error");
      delay(1000);
    }

  } else {
    if (!isCreateConnect) { // 如果没有成功创建socket
      Serial.println("start  connect");
      socket.close();
      socket.connect();
      connectTime = millis();
      isCreateConnect = true;
      if (isCreateConnect) {
        Serial.println("socket success");
      } else {
        Serial.println("socket error");
        delay(1000);
      }
    } else {
      socket.loopSocket();
      checkConnect();
      checkHeartbeat();
      sendHeartbeat();
      String command = Serial.readString();
      if (!command.equals("")) {
        Serial.println("client:");
        Serial.println(command);
        socket.send(command);
      }
    }
  }
}

void sendHeartbeat() {
  if (isConnect && millis() - lastSendHeartbeatTime >= 1000L * 30) { // 如果连接成功 并且30秒内没有发送心跳包
    lastSendHeartbeatTime = millis();
    socket.send(Heartbeat);
  }
}

void checkHeartbeat() {
  if (isConnect && millis() - lastHeartbeatTime > 1000L * 60) { // 如果一分钟未接收到心跳,断开重连
    Serial.println("heartbeat time out");
    socketClose();
  }
}

// 检测连接
void checkConnect() {
  if (!isConnect && millis() - connectTime > 1000L * 10) { // 如果未连接成功 10秒等待检测
    Serial.println("time out");
    socketClose();
  }
}

// socket连成功监听 该方法不知道为啥有些时候不会进入,可自行使用心跳机制完善
void socketConnect() {
  isConnect = true;
  lastHeartbeatTime = millis();
  Serial.println("connect success");
}

// socket断开监听
void socketClose() {
  isConnect = false;
  isCreateConnect = false;
  isConnectNetWork = false;
  Serial.println("connect close");
}

// socket消息监听
void socketMessage(String msg, int length) {
  Serial.print("server:");
  Serial.println(length);
  Serial.println(msg);
  if (msg.length() == length) {
    if (msg.equals(Heartbeat)) {
      lastHeartbeatTime = millis();
    }
  }
}

4G BC26封装.h文件

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

class Socket {
  public :
    void initSocket(int socketId, String host, int port, SoftwareSerial *serial); // 初始化socket
    void addSocketConnectListener(void(*socketConnectListener)()); // 添加socket连接成功回调
    void addSocketCloseListener(void(*socketCloseListener)()); // 添加socket断开连接回调
    void addSocketMessageListener(void(*socketMessageListener)(String,int)); // 添加socket消息回调
    void setLocalPort(int localPort); // 设置socket本地接收端口
    void useUDP(); // 设置使用udp
    void useTCP(); // 设置使用tcp
    void useIPV4(); // 使用ipv4
    void useIPV6(); // 使用ipv6
    void testAT(); // 测试AT指令
    void seeIpAt(); // 查看ip AT指令
    void loopSocket(); // 轮询socket消息
    boolean connect(); // 开始建立连接socket
    boolean checkNetWork(); // 检测是否连接到网络
    boolean close(); // 关闭socket
    boolean send(String msg); // 发送消息

  private:
    boolean connectBC26(); // 开始连接bc26
    boolean analysisBC26(String *types, int typesLength); // 解析bc26消息
    void readBC26(); // 读取bc26
    void sendBC26(String command); // 发送命令到bc26

  private :
    int mSocketId; // socket通道id
    int mPort; // socket连接端口
    int mLocalPort = 0; // socket本地接收端口 0:bc26自动选择
    int mAccessMode = 1; // 0:buff模式需要手动去读取 1:push模式主动推送(现在主要使用该方式)
    int mProtocolType = 0; // 0:IPV4 1:IPV6
    int mReadLength = 0; // 读取到的数据的有效长度
    String mServiceType = String("TCP"); // 连接方式 "TCP","UDP"
    String mHost; // socket连接地址
    String mReadDoc[5]; //保存读取到的数据
    SoftwareSerial *mSerial = NULL;
    void(*connectListener)(); // 添加socket连接成功回调
    void(*closeListener)(); // 添加socket断开连接回调
    void(*messageListener)(String,int); // 添加socket消息回调
};

4G BC26封装.cpp实现文件

#include "BC26Socket.h"


// 初始化 socket 连接信息
void Socket::initSocket(int socketId, String host, int port, SoftwareSerial *serial) {
  mSocketId = socketId;
  mHost = host;
  mPort = port;
  mSerial = serial;
}

// 添加socket连接成功回调
void Socket::addSocketConnectListener(void(*socketConnectListener)()) {
  connectListener = socketConnectListener;
}

// 添加socket断开连接回调
void Socket::addSocketCloseListener(void(*socketCloseListener)()) {
  closeListener = socketCloseListener;
}

// 添加socket消息回调
void Socket::addSocketMessageListener(void(*socketMessageListener)(String, int)) {
  messageListener = socketMessageListener;
}

void Socket::setLocalPort(int localPort) {
  mLocalPort = localPort;
}

void Socket::useUDP() {
  mServiceType = String("TCP");
}


void Socket::useTCP() {
  mServiceType = String("TCP");
}

void Socket::useIPV4() {
  mProtocolType = 0;
}

void Socket::useIPV6() {
  mProtocolType = 1;
}

void Socket::testAT() {
  sendBC26("AT");
  delay(300);
  readBC26();
  analysisBC26(NULL, 0);
}

void Socket::seeIpAt() {
  sendBC26("AT+CGPADDR");
  delay(300);
  readBC26();
  analysisBC26(NULL, 0);
}


// 轮询socket消息
void Socket::loopSocket() {
  readBC26();
  analysisBC26(NULL, 0);
}

// 检测网络状态
boolean Socket::checkNetWork() {
  sendBC26("AT+CGATT?");
  delay(300);
  readBC26();
  String types[] = {"+CGATT: 1"};
  return analysisBC26(types, 1);
}

// 开始建立连接socket
boolean Socket::connect() {
  if (mHost == NULL || mHost.length() == 0) {
    Serial.println("error1");
    return false;
  }
  if (mSerial == NULL) {
    Serial.println("error2");
    return false;
  }
  return connectBC26();
}


// 开始连接bc26
boolean Socket::connectBC26() {
  String command = "AT+QIOPEN=1,";
  command += mSocketId;
  command += ",\"";
  command += mServiceType;
  command += "\",\"";
  command += mHost;
  command += "\",";
  command += mPort;
  command += ",";
  command += mLocalPort;
  command += ",";
  command += mAccessMode;
  command += ",";
  command += mProtocolType;
  sendBC26(command);
  command = "";
  delay(300);
  readBC26();
  String types[1] = {"OK"};
  return analysisBC26(types, 1);
}

// 关闭socket
boolean Socket::close() {
  String command = "AT+QICLOSE=";
  command += mSocketId;
  sendBC26(command);
  command = "";
  delay(300);
  readBC26();
  String types[1] = {"CLOSE OK"};
  return analysisBC26(types, 1);
}

// 发送消息
boolean Socket::send(String msg) {
  String command = "AT+QISEND=";
  command += mSocketId;
  command += ",";
  command += msg.length();
  command += ",";
  command += msg;
  sendBC26(command);
  command = "";
  delay(300);
  readBC26();
  String types[1] = {"SEND OK"};
  return analysisBC26(types, 1);
}

// 发送命令到bc26
void Socket::sendBC26(String command) {
  mSerial->println(command);
  //  Serial.println("+ send:");
//  Serial.println(command);
}

// 读取bc26
void Socket::readBC26() {
  String command2 = mSerial->readString();
  mReadLength = 0;
  if (!command2.equals("")) {
    //    Serial.println("返回数据:");
    command2.replace("\r\n", "|");
    //    Serial.println(command2);
    do {
      int indexof = command2.indexOf("|");
      if (indexof == -1) {
        if (command2.length() > 0) {
          mReadDoc[mReadLength] = command2;
          mReadLength++;
          command2 = "";
        }
      } else {
        String str = command2.substring(0, indexof);
        if (str.length() > 0) {
          mReadDoc[mReadLength] = str;
          mReadLength++;
          str = "";
        }
        command2 = command2.substring(indexof + 1, command2.length());
      }
    } while (command2.length() > 0);
    //    command2 = "";
    //    if (mReadLength > 0) {
    //      Serial.print("- receive:");
    //      Serial.print(mReadLength);
    //      Serial.println("):");
    //      Serial.print("\t");
    //      for (int i = 0; i < mReadLength; i++) {
    //        Serial.print("|");
    //        Serial.print(mReadDoc[i]);
    //      }
    //    }
    //    Serial.println();
  }
}

// 解析bc26消息
boolean Socket::analysisBC26(String *types, int typesLen) {
  int haveSize = 0;
  for (int i = 0; i < mReadLength; i++) {
    String type = mReadDoc[i];
    for (int s = 0; s < typesLen; s++) {
      String item = types[s];
      if (type.equals(item)) {
        haveSize++;
      }
    }
    if (type.startsWith("+")) {
      String types = type;
      types.trim();
      types.replace(" ", "");
      //      Serial.println("get+:" + types);
      if (type.startsWith("+QIOPEN:")) { // 如果开头是连接成功
        //        Serial.println("连接状态更改:" + types);
        String head = "+QIOPEN:";
        String command1 = head + mSocketId + ",0";
        String command2 = head + mSocketId + ",566";
        if (types.equals(command1)) { // 连接成功 可进行通讯
          connectListener();
        } else if (types.equals(command2)) { // 连接超时
          closeListener();
        }
      } else if (type.startsWith("+QIURC:")) { // 如果开头是socket关闭
        //        Serial.println("BC23上报:" + types);
        String command1 = "+QIURC:\"recv\"," ;
        command1 += mSocketId;
        command1 += ",";
        String command2 = "+QIURC:\"closed\"," ;
        command2 += mSocketId;
        if (types.startsWith(command1)) { // scket接收到消息
          String lengthStr = "0";
          if (type.lastIndexOf(",") != -1) {
            lengthStr = type.substring(type.lastIndexOf(",") + 1, type.length());
          }
          int length = lengthStr.toInt();
          String msg = "";
          if (i + 1 < mReadLength) {
            msg = mReadDoc[i + 1];
          }
          messageListener(msg, length);
        } else if (types.equals(command2)) { // socket 关闭
          closeListener();
        }
      }
    }
  }
  return haveSize >= mReadLength;
}

需要注意一下,可能我c++学艺不精问题吧,大家使用的使用尽量让Arduino可用动态内存大于50%,不然续写4g数据跟解析数据的时候会造成字符串丢失哦!

正式运行时,建议把调试的log代码都注释掉,这样好像可以留出大部分的动态内存!

在这里插入图片描述

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
Wio Tracker(无线输入输出)是一个开源网关,可以实现更快的 IoT GPS 解决方案。 这是兼容 Arduino 和 Grove 的开发板,可以帮助您跟踪地球上几乎任何移动的东西,然后以无线方式上传这些数据。Wio LTE 是 Wio Tracker 的 LTE 版本,所以现在我们已经有了 Wio Tracker 和 LTE 两个版本,而 LTE4G通信版本会有所不同。 除了三个主要更新之外,LTE 版本几乎与 2G 版本相同。如果您的项目使用的是 2G 版本,那么更新到 LTE 版本将非常容易,因为我们已经准备了可移植和可扩展的 AT 命令库。兼容 Arduino 和 Grove 可以使用众多库和支持社区。可以在板上使用的 GPS 库不仅限于 Arduino--如果您选择使用 C/C++ 进行开发,它也可以正常工作。通过板载的 6 个 Grove 接口,开发人员可以创建 180 多种传感器和执行器的任意组合,以构建项目并解决任何问题。简化原型和开发阶段是我们的目标。 Wio LTE 非常适合户外项目,其自带的 GPS 天线设备可以连接到 GPS 卫星,并提供与其相连的物品的实时位置。LTE 提供了宽带宽,允许用户和设备之间更快的交互。如果您打算建设自行车共享服务,追踪宠物或牲畜,定位车辆,甚至追踪孩子等项目,Wio LTE 是最好的解决方案。 硬件概述: 特性: 为宽带物联网应用优化的低成本,低功耗LTE连接 全球 LTE 和 UMTS/HSPA + 嵌入式电源管理单元(PMU)具有超低的深度睡眠电流消耗 GPS/北斗/GLONASS/伽利略/QZSS 可移植和可扩展的 AT 命令库兼容 Wio Tracker 兼容 Arduino IDE 6 个 Grove 接口 Nano SIM 和 TF 卡二合一插槽 说明: EC21 模块与现有的 EDGE 和 GSM/GPRS 网络向后兼容,确保它可以轻松地从 LTE 迁移到 2G 或 3G 网络。并且 EC21-A 我们用在了 WIO Tracker - LTE 上,支持 AT&T 和 T-mobile SIM 卡。 附件资料截图: 可能感兴趣的项目设计: 基于MC20模块物联网GPS跟踪器-跟踪任何移动的物体,附原理图/PCB/demo程序等

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值