1. 程式人生 > 其它 >Arduino與墨子號 BC26 4G模組的對接開發

Arduino與墨子號 BC26 4G模組的對接開發

技術標籤:arduino物聯網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程式碼都註釋掉,這樣好像可以留出大部分的動態記憶體!

在這裡插入圖片描述