概述
最近开始要做物联网的一个设备,由于是外用就考虑到了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("rn", "|");
// 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代码都注释掉,这样好像可以留出大部分的动态内存!
最后
以上就是冷静毛巾为你收集整理的Arduino与墨子号 BC26 4G模块的对接开发的全部内容,希望文章能够帮你解决Arduino与墨子号 BC26 4G模块的对接开发所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复