From 3cc15addf3c30ef7deffdd6de52005a6378aa42a Mon Sep 17 00:00:00 2001 From: luyang14 Date: Wed, 16 Mar 2022 11:38:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96mqtt=E5=8F=8Ahttp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化mqtt及http,增加N716模块 --- EC20xlib.cpp | 735 +++++++++++++++------------------------------ EC20xlib.h | 66 ++-- N716lib.cpp | 361 ++++++++++++++++++++++ N716lib.h | 58 ++++ iolte_base.cpp | 349 +++++++++++++++++++++ iolte_base.h | 90 ++++++ library.json | 2 +- library.properties | 4 +- 8 files changed, 1120 insertions(+), 545 deletions(-) create mode 100644 N716lib.cpp create mode 100644 N716lib.h create mode 100644 iolte_base.cpp create mode 100644 iolte_base.h diff --git a/EC20xlib.cpp b/EC20xlib.cpp index c41f758..05bb58b 100644 --- a/EC20xlib.cpp +++ b/EC20xlib.cpp @@ -1,126 +1,42 @@ #include #include "EC20xlib.h" +#include "iolog.h" #define min _min #define max _max +uint8_t pubsub_f = 0; -///////////////////////////////////////////// -// Public methods. -// - -EC20xlib::EC20xlib(Stream &serial) { -#ifndef ESP32 -#ifdef ESP8266 - EC20xconn = new SoftwareSerial(receivePin, transmitPin, false, 1024); -#else - EC20xconn = new SoftwareSerial(receivePin, transmitPin, false); - -#endif - EC20xconn->setTimeout(100); -#else - EC20xconn = &serial; - - EC20xconn->setTimeout(50);//阻塞时间 -#endif -} - - -EC20xlib::~EC20xlib() { - - //delete EC20xconn; +void EC20xlib::attachsubcallback(subcallbackFunction newFunction){ + subdatecallback = newFunction; } - - -// Block until the module is ready. -byte EC20xlib::blockUntilReady(long baudRate) { - - byte response = EC20x_NOTOK; - while (EC20x_OK != response) { - response = begin(); - // This means the modem has failed to initialize and we need to reboot - // it. - if (EC20x_FAILURE == response) { - return EC20x_FAILURE; - } - delay(1000); - logln("Waiting for module to be ready..."); - } - return EC20x_OK; +void EC20xlib::attachtcpcallback(recallbackFunction newFunction){ + getdatecallback = newFunction; } - - -// Initialize the software serial connection and change the baud rate from the -// default (autodetected) to the desired speed. -byte EC20xlib::begin() { - - EC20xconn->flush(); - - // Factory reset. - if (EC20x_OK != EC20xcommand("AT", "OK", "yy", EC20x_CMD_TIMEOUT, 20, NULL)) return EC20x_FAILURE; - - // Echo off. - if (EC20x_OK != EC20xcommand("ATE0", "OK", "yy", EC20x_CMD_TIMEOUT, 2, NULL)) return EC20x_FAILURE; - - // Echo off. - if (EC20x_OK != EC20xcommand("ATI", "OK", "yy", EC20x_CMD_TIMEOUT, 2, NULL)) return EC20x_FAILURE; - // 检查SIM卡是否在位 - if (EC20x_OK != EC20xcommand("AT+CPIN?", "+CPIN: READY", "yy", EC20x_CMD_TIMEOUT, 2, NULL)) return EC20x_FAILURE; - - // 检查CSQ - if (EC20x_OK != EC20xcommand("AT+CSQ", "OK", "yy", EC20x_CMD_TIMEOUT, 2, NULL)) return EC20x_FAILURE; - - // 查看是否注册GSM网络 - if (EC20x_OK != EC20xcommand("AT+CREG?", "+CREG: 0,1", "+CREG: 0,5", EC20x_CMD_TIMEOUT, 20, NULL)) return EC20x_FAILURE; - - // 查看是否注册GPRS网络 - if (EC20x_OK != EC20xcommand("AT+CGREG?", "+CGREG: 0,1", "+CGREG: 0,5", EC20x_CMD_TIMEOUT, 20, NULL)) return EC20x_FAILURE; - - return EC20x_OK; +void EC20xlib::attacheventcallback(eventcallbackFunction newFunction){ + eventcallback = newFunction; } +EC20xlib::EC20xlib(Stream &serial) : IOLTE(serial){ - -// Reboot the module by setting the specified pin HIGH, then LOW. The pin should -// be connected to a P-MOSFET, not the EC20x's POWER pin. -void EC20xlib::powerCycle(int pin) { - logln("Power-cycling module..."); - - powerOff(pin); - - delay(2000); - - powerOn(pin); - - // Give the module some time to settle. - logln("Done, waiting for the module to initialize..."); - delay(20000); - logln("Done."); - - EC20xconn->flush(); } +EC20xlib::~EC20xlib() { - -// Turn the modem power completely off. -void EC20xlib::powerOff(int pin) { - pinMode(pin, OUTPUT); - digitalWrite(pin, LOW); } - -// Turn the modem power on. -void EC20xlib::powerOn(int pin) { - pinMode(pin, OUTPUT); - digitalWrite(pin, HIGH); +byte EC20xlib::begin() { + int returnValue = IOLTE_NOTOK; + returnValue = initcom(); + return returnValue; } // Get the strength of the GSM signal. -int EC20xlib::getSignalStrength() { +int EC20xlib::Read_Signal() { String response = ""; uint32_t respStart = 0; int strength, error = 0; // Issue the command and wait for the response. - EC20xcommand("AT+CSQ", "OK", "+CSQ", EC20x_CMD_TIMEOUT, 2, &response); + IOLtecommand("AT+CSQ", "OK", "+CSQ", IOLTE_CMD_TIMEOUT, 2, &response); respStart = response.indexOf("+CSQ"); if (respStart < 0) { @@ -134,7 +50,6 @@ int EC20xlib::getSignalStrength() { strength = (strength * 100) / 31; return strength; } - /************************************************ *函数名:ReadCCID *功能 :读取SIM卡CCID号 @@ -147,8 +62,8 @@ byte EC20xlib::Read_CCID(char *DataBuf) uint32_t respStart = 0; String response = ""; //发送读取IMSI命令 - re=EC20xcommand("AT+CCID\r\n","+CCID","yy",EC20x_CMD_TIMEOUT, 2, &response); - Serial.println(response); + re=IOLtecommand("AT+CCID\r\n","+CCID","yy",IOLTE_CMD_TIMEOUT, 2, &response); + //Serial.println(response); respStart = response.indexOf("+CCID"); if (respStart < 0) { return 0; @@ -168,190 +83,28 @@ byte EC20xlib::Read_IMEI(char *DataBuf) uint8_t re = 0; String response = ""; //发送读取IMSI命令 - re = EC20xcommand("AT+GSN","OK","yy",EC20x_CMD_TIMEOUT, 2, &response); - Serial.println(response); + re = IOLtecommand("AT+GSN","OK","yy",IOLTE_CMD_TIMEOUT, 2, &response); + //Serial.println(response); response = response.substring(2, 18); - Serial.println(response); response.toCharArray(DataBuf,16,0); - Serial.println(DataBuf); + //Serial.println(DataBuf); return re; } // Get the real time from the modem. Time will be returned as yy/MM/dd,hh:mm:ss+XX -String EC20xlib::getRealTimeClock() { +String EC20xlib::Read_TimeClock() { String response = ""; // Issue the command and wait for the response. - EC20xcommand("AT+CCLK?", "OK", "yy", EC20x_CMD_TIMEOUT, 1, &response); + IOLtecommand("AT+CCLK?", "OK", "yy", IOLTE_CMD_TIMEOUT, 1, &response); int respStart = response.indexOf("+CCLK: \"") + 8; response.setCharAt(respStart - 1, '-'); return response.substring(respStart, response.indexOf("\"")); } - -///////////////////////////////////////////// -// Private methods. -// - -// Read some data from the EC20x in a non-blocking manner. -String EC20xlib::read() { - String reply = ""; - if (EC20xconn->available()) { - reply = EC20xconn->readString(); - } - - // XXX: Replace NULs with \xff so we can match on them. - for (int x = 0; x < reply.length(); x++) { - if (reply.charAt(x) == 0) { - reply.setCharAt(x, 255); - } - } - return reply; -} - -// 发送AT命令 -// Issue a command. -byte EC20xlib::EC20xcommand(const char *command, const char *resp1, const char *resp2, int timeout, int repetitions, String *response) { - byte returnValue = EC20x_NOTOK; - byte count = 0; - - // Get rid of any buffered output. - EC20xconn->flush(); - //loop(); - while (count < repetitions && returnValue != EC20x_OK) { - log("Issuing command: "); - logln(command); - - EC20xconn->write(command); - EC20xconn->write('\r'); - - if (EC20xwaitFor(resp1, resp2, timeout, response) == EC20x_OK) { - returnValue = EC20x_OK; - } else { - returnValue = EC20x_NOTOK; - } - count++; - } - return returnValue; -} -/**************************************** -* 函数名:EC20_SendCommand -* 描述 :向GPRS模块发送命令 -* CommandStr 命令字符串 -* Check 检测的命令回复 -* retry 重试次数 -* 每次等待命令回复的时间 单位100mS -* 输入 :无 -* 输出 :无 -****************************************/ -byte EC20xlib::EC20xcommand(const char *command,uint16_t commandlen, const char *resp1, const char *resp2, int timeout, int repetitions, String *response) -{ - byte returnValue = EC20x_NOTOK; - byte count = 0; - resend_state = STSTE_SEND; - // Get rid of any buffered output. - EC20xconn->flush(); - - while (count < repetitions && returnValue != EC20x_OK) { - log("Issuing command: "); - logln(command); - - EC20xconn->write(command,commandlen); - EC20xconn->write('\r'); - - if (EC20xwaitFor(resp1, resp2, timeout, response) == EC20x_OK) { - returnValue = EC20x_OK; - } else { - returnValue = EC20x_NOTOK; - } - count++; - } - resend_state = STSTE_RE; - return returnValue; -} - -// Wait for responses. -byte EC20xlib::EC20xwaitFor(const char *resp1, const char *resp2, int timeout, String *response) { - unsigned long entry = millis(); - String reply = ""; - String reply2 = ""; - byte retVal = 99; - do { - reply += read(); -#ifdef ESP8266 - yield(); -#endif - } while (((reply.indexOf(resp1) + reply.indexOf(resp2)) == -2) && ((millis() - entry) < timeout)); - - if (reply != "") { - log("Reply in "); - log((millis() - entry)); - log(" ms: "); - logln(reply); - } - - if (response != NULL) { - *response = reply; - } - - if ((millis() - entry) >= timeout) { - retVal = EC20x_TIMEOUT; - logln("Timed out."); - } else { - if (reply.indexOf(resp1) + reply.indexOf(resp2) > -2) { - logln("Reply OK."); - retVal = EC20x_OK; - } else { - logln("Reply NOT OK."); - retVal = EC20x_NOTOK; - } - } - reply2 = reply; - if(reply2.indexOf("+QIURC: \"recv\",") != -1)//接收到数据 - { - logln("recv data"); - uint8_t connectID = 0; - uint16_t datalen = 0; - char sendbuf[1000] = ""; - int commaPosition = 0; - commaPosition = reply2.indexOf("+QIURC: \"recv\","); - reply2 = reply2.substring(commaPosition,reply2.length());//截取有效数据 - - for(uint8_t i =0;i<3;i++){ - commaPosition = reply2.indexOf(","); - if(commaPosition != -1){//检测到“,” - if(i == 1){ - connectID = atoi(reply2.substring(0,commaPosition).c_str()); - log("connectID:"); - logln(connectID); - datalen = atoi(reply2.substring(commaPosition+1,reply2.indexOf("\r")).c_str()); - logln("datalen---------------------------------"); - logln(datalen); - break; - } - reply2 = reply2.substring(commaPosition+1,reply2.length()); - } - } - commaPosition = reply2.indexOf("HTTP/1.1"); - if(commaPosition != -1){//检测到“HTTP/1.1” - reply2 = reply2.substring(commaPosition,reply2.length()); - reply2.toCharArray(sendbuf,reply2.length()+1,0); - getdatecallback(sendbuf,datalen,connectID); - } - } - return retVal; -} - -void EC20xlib::attachsubcallback(subcallbackFunction newFunction){ - subdatecallback = newFunction; -} -void EC20xlib::attachtcpcallback(recallbackFunction newFunction){ - getdatecallback = newFunction; -} - // Read some data from the EC20x in a non-blocking manner. -byte EC20xlib::connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password) { +byte EC20xlib::connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password,uint8_t keepalive,uint8_t will_qos,char *will,char *will_payload) { char sendbuf[100]; String response = ""; @@ -359,65 +112,101 @@ byte EC20xlib::connectMqttServer(char *clientID,char *ip,char *port,char *userna int client_idx = 0, error = 0,result = 0; for(int i = 0; i<2;i++) { + //配置MQTT接收模式 + //AT+QMTCFG="recv/mode",[,[,]] + /* + + 整型。配置 MQTT 消息接收模式。 + 0 从服务器接收的 MQTT 消息以 URC 的形式上报 + 1 从服务器接收的 MQTT 消息不以 URC 的形式上报 + + + 整型。配置 URC 中是否包含从服务器接收的 MQTT 消息长度。 + 0 不包含 + 1 包含 + */ + sprintf(sendbuf,"AT+QMTCFG=\"recv/mode\",%d,%d,%d",0,0,1); + if (IOLtecommand(sendbuf, "OK", "yy", IOLTE_CMD_TIMEOUT+3000, 3, &response) <= IOLTE_OK) return IOLTE_FAILURE; + + //根据需要设置遗嘱消息 + /* + AT+QMTCFG="will",[,[,,,,]] + */ + if((will !=NULL)&&(will_payload != NULL)){ + memset(sendbuf,0,sizeof(sendbuf)); + sprintf(sendbuf,"AT+QMTCFG=\"will\",0,1,1,1,\"%s\",\"%s\"",will,will_payload); + if (IOLtecommand(sendbuf, "yy", "OK", IOLTE_CMD_TIMEOUT+3000, 3, &response) <= IOLTE_OK) return IOLTE_FAILURE; + log4g("%s","set will OK."); + } + /* + AT+QMTCFG="keepalive",[,] + */ + memset(sendbuf,0,sizeof(sendbuf)); + sprintf(sendbuf,"AT+QMTCFG=\"keepalive\",0,%d",keepalive); + if (IOLtecommand(sendbuf, "yy", "OK", IOLTE_CMD_TIMEOUT+3000, 3, &response) <= IOLTE_OK) return IOLTE_FAILURE; + log4g("%s","set keepalive OK."); sprintf(sendbuf,"AT+QMTOPEN=0,\"%s\",%s",ip,port); - if (EC20x_OK == EC20xcommand(sendbuf, "+QMTOPEN:", "yy", EC20x_CMD_TIMEOUT+3000, 3, &response)) - { + if (IOLtecommand(sendbuf, "+QMTOPEN:", "yy", IOLTE_CMD_TIMEOUT+3000, 3, &response) > IOLTE_OK) { respStart = response.indexOf("+QMTOPEN:"); if (respStart < 0) { - return EC20x_FAILURE; + return IOLTE_FAILURE; } - sscanf(response.substring(respStart).c_str(), "+QMTOPEN: %d,%d", - &client_idx, &error); - - if(error == EC20x_OK)//连接成功 - { - sprintf(sendbuf,"AT+QMTCONN=0,\"%s\",\"%s\",\"%s\"",clientID,username,password); - if (EC20x_OK == EC20xcommand(sendbuf, "+QMTCONN:", "yy", EC20x_CMD_TIMEOUT+3000, 3, &response)) - { + response = response.substring(respStart,response.length()); + respStart = response.indexOf(","); + error = atoi(response.substring(respStart+1,response.length()).c_str()); + //sscanf(DataBuf, "+QMTOPEN: %d,%d",&client_idx, &error);//未解决负数问题 + + log4g("error: %d",error); + if(error == 0){//连接成功 + if((username != NULL)&&(password != NULL)) + sprintf(sendbuf,"AT+QMTCONN=0,\"%s\",\"%s\",\"%s\"",clientID,username,password); + else sprintf(sendbuf,"AT+QMTCONN=0,\"%s\"",clientID); + if (IOLtecommand(sendbuf, "+QMTCONN:", "yy", IOLTE_CMD_TIMEOUT+3000, 3, &response) > IOLTE_OK){ respStart = response.indexOf("+QMTCONN:"); if (respStart < 0) { - return EC20x_FAILURE; + return IOLTE_FAILURE; } sscanf(response.substring(respStart).c_str(), "+QMTCONN: %d,%d,%d", &client_idx,&result, &error); - if(error == EC20x_OK) - { - return EC20x_OK; - logln("connect OK."); + if(error == 0){ + connected_state = true; + if(eventcallback != NULL) + eventcallback(MQTT_CONNECTED); + log4g("%s","connect OK."); + return IOLTE_OK; } - else return EC20x_FAILURE; + else return IOLTE_FAILURE; } - else return EC20x_FAILURE; + else return IOLTE_FAILURE; + } + else if(error == 2){//MQTT 标识符被占用,需要先断开重新连接 + connected_state = false; + if (IOLtecommand("AT+QMTCLOSE=0", "+QMTCLOSE: 0,0", "yy", IOLTE_CMD_TIMEOUT+3000, 3, &response) > IOLTE_OK) + return IOLTE_FAILURE; + if(eventcallback != NULL) + eventcallback(MQTT_DISCONNECT); } - else if(error == 2)//MQTT 标识符被占用,需要先断开重新连接 - { - if (EC20x_OK != EC20xcommand("AT+QMTCLOSE=0", "+QMTCLOSE: 0,0", "yy", EC20x_CMD_TIMEOUT+3000, 3, &response)) - return EC20x_FAILURE; + else if(error == -1){//MQTT 连接失败 + connected_state = false; + if(eventcallback != NULL) + eventcallback(MQTT_DISCONNECT); } - else return EC20x_FAILURE; + else return IOLTE_FAILURE; } - else return EC20x_FAILURE; - } - return EC20x_FAILURE; - /* - sprintf(sendbuf,"AT+QMTOPEN=0,\"%s\",%s",ip,port); - if (EC20x_OK != EC20xcommand(sendbuf, "+QMTOPEN: 0,0", "yy", EC20x_CMD_TIMEOUT+3000, 3, &response)) - { - if (EC20x_OK != EC20xcommand("AT+QMTCLOSE=0", "+QMTCLOSE: 0,0", "yy", EC20x_CMD_TIMEOUT+3000, 3, &response)) - return EC20x_FAILURE; - if (EC20x_OK != EC20xcommand(sendbuf, "+QMTOPEN: 0,0", "yy", EC20x_CMD_TIMEOUT+3000, 3, &response)) - return EC20x_FAILURE; + else return IOLTE_FAILURE; } - - memset(sendbuf,0,sizeof(sendbuf)); - sprintf(sendbuf,"AT+QMTCONN=0,\"%s\",\"%s\",\"%s\"",clientID,username,password); - if (EC20x_OK != EC20xcommand(sendbuf, "+QMTCONN: 0,0,0", "yy", EC20x_CMD_TIMEOUT, 3, &response)) return EC20x_FAILURE; - logln("connect OK."); - - return EC20x_OK; - */ + return IOLTE_FAILURE; } +byte EC20xlib::connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password,uint8_t keepalive) { + return connectMqttServer(clientID,ip,port,username,password,keepalive,1,NULL,NULL); +} +byte EC20xlib::connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password) { + return connectMqttServer(clientID,ip,port,username,password,60,1,NULL,NULL); +} +byte EC20xlib::connectMqttServer(char *clientID,char *ip,char *port) { + return connectMqttServer(clientID,ip,port,NULL,NULL,60,1,NULL,NULL); +} /* 取消订阅主题 */ @@ -425,11 +214,11 @@ byte EC20xlib::UnSubTopic(char *topic,uint8_t qos1) { char data_buff[300];//格式化AT指令时使用 - logln("UnSubTopic"); + log4g("%s","UnSubTopic"); //订阅需要的主题 sprintf(data_buff,"AT+QMTUNS=0,%d,%s",1,topic); - if (EC20x_OK != EC20xcommand(data_buff,"OK","yy",5000,5,NULL)) return EC20x_FAILURE; - return EC20x_OK; + if (IOLtecommand(data_buff,"OK","yy",5000,5,NULL) <= IOLTE_OK) return IOLTE_FAILURE; + return IOLTE_OK; } /* 订阅主题 @@ -438,11 +227,11 @@ byte EC20xlib::SubTopic(char *topic,uint8_t qos1) { char data_buff[300];//格式化AT指令时使用 - logln("SubTopic"); + log4g("%s","SubTopic"); //订阅需要的主题 sprintf(data_buff,"AT+QMTSUB=0,%d,\"%s\",%d",1,topic,qos1); - if (EC20x_OK != EC20xcommand(data_buff,"OK","yy",5000,5,NULL)) return EC20x_FAILURE; - return EC20x_OK; + if (IOLtecommand(data_buff,"+QMTSUB: 0,1,0,0","yy",5000,5,NULL) <= IOLTE_OK) return IOLTE_FAILURE; + return IOLTE_OK; } /* 发布主题 @@ -450,119 +239,44 @@ byte EC20xlib::SubTopic(char *topic,uint8_t qos1) byte EC20xlib::PubTopic(char *topic,char* msg,uint8_t qos1)//typ_NEC_AQData *AQData_Send, { char data_buff[300];//格式化AT指令时使用 - - logln("PubTopic"); + String response = ""; + String reply = ""; + + log4g("%s","PubTopic"); + pubsub_f = 1; sprintf(data_buff,"AT+QMTPUBEX=0,0,%d,0,\"%s\",%d",qos1,topic,strlen(msg)); - if (EC20x_OK != EC20xcommand(data_buff,">","yy",5000,5,NULL)) return EC20x_FAILURE; - if (EC20x_OK != EC20xcommand(msg,"OK","yy",5000,5,NULL)) return EC20x_FAILURE; - return EC20x_OK; + if (IOLtecommand(data_buff,">","yy",IOLTE_CMD_TIMEOUT+3000,5,&response) <= IOLTE_OK) return IOLTE_FAILURE; + reply+=response; + if (IOLtecommand(msg,"OK","yy",IOLTE_CMD_TIMEOUT+3000,5,&response) <= IOLTE_OK) return IOLTE_FAILURE; + reply+=response; + pubsub_f = 0; + ParseMsg(reply); + + return IOLTE_OK; } - /* -发布主题 +MQTT是否连接 */ -byte EC20xlib::getMessage(char *topic,char* msg,uint8_t qos1)//typ_NEC_AQData *AQData_Send, -{ - unsigned long entry = millis(); - int commaPosition,last_commaPosition;//存储还没有分离出来的字符串 - String reply = ""; - byte retVal = 99; - reply += read(); -#ifdef ESP32 - yield(); -#endif - if (reply != "") { - log("Reply in "); - log((millis() - entry)); - log(" ms: "); - logln(reply); - if(reply.indexOf("+QIURC:") != -1)//接收到服务器返回 - { - logln("recv data"); - uint8_t i = 0; - for(i = 0;i<3;i++) - { - commaPosition = reply.indexOf(',');//检测字符串中的逗号 - - if(commaPosition != -1)//如果有逗号存在就向下执行 - { - if(i == 2)//获取topic - { - logln( reply.substring(1,commaPosition-1));//打印出第一个逗号位置的字符串 - reply.substring(1,commaPosition-1).toCharArray(topic,reply.substring(1,commaPosition-1).length()+1); - } - - reply = reply.substring(commaPosition+1, reply.length());//打印字符串,从当前位置+1开始 - } - } - if(1)//获取topic的内容 - { - commaPosition = reply.indexOf('"'); - last_commaPosition = reply.lastIndexOf('"'); - logln( reply.substring(commaPosition+1,last_commaPosition));//打印出第一个逗号位置的字符串 - reply.substring(commaPosition+1,last_commaPosition).toCharArray(msg,reply.substring(commaPosition+1,last_commaPosition).length()+1); - } - retVal = EC20x_OK; - } - if(reply.indexOf("+QMTSTAT:") != -1) - { - logln("mqtt disconnect"); - /* - 整型。MQTT 客户端标识符。 - 整型。错误代码。详细信息请参考下表。 - - 1 连接被服务器断开或者重置 执行 AT+QMTOPEN 重建 MQTT 连接 - 2 发送 PINGREQ 包超时或者失败 首先反激活 PDP,然后再激活 PDP 并重建MQTT 连接。 - 3 发送 CONNECT 包超时或者失败 1. 查看输入的用户名和密码是否正确。 - 2. 确保客户端 ID 未被占用。 - 3. 重建 MQTT 连接,并尝试再次发送CONNECT 包到服务器。 - 4 接收 CONNACK 包超时或者失败 1. 查看输入的用户名和密码是否正确。 - 2. 确保客户端 ID 未被占用。 - 3. 重建 MQTT 连接,并尝试再次发送CONNECT 包到服务器。 - 5 客户端向服务器发送 DISCONNECT 包,但 - 是服务器主动断开 MQTT 连接 正常流程。 - 6 因为发送数据包总是失败,客户端主动断开MQTT 连接 1. 确保数据正确。 - 2. 可能因为网络拥堵或者其他错误,尝试重建 MQTT 连接。 - 7 链路不工作或者服务器不可用 确保当前链路或者服务器可用。 - 8 客户端主动断开 MQTT 连接 尝试重建连接。 - 9~255 留作将来使用 - */ - //重新连接 - retVal = EC20x_DISCONNECT; - } - if(reply.indexOf("+QMTPUBEX:") != -1) - { - logln("rev the result of public message"); - /* - 整型。MQTT 客户端标识符。范围:0~5。 - 整型。数据包消息标识符。范围:0~65535。只有当=0 时,该参数值为 0。 - 整型。命令执行结果。 - 0 数据包发送成功且接收到服务器的 ACK(当=0 时发布了数据,则无需 ACK) - 1 数据包重传 - 2 数据包发送失败 - */ - //接收到发送返回 - retVal = EC20x_DISCONNECT; - } - - } - return retVal; +boolean EC20xlib::connected() { + + return connected_state; } /* -TCP连接服务器 +TCP断开连接服务器 */ int EC20xlib::disconnect(uint8_t connectID) { char sdata[30];//格式化AT指令时使用 - uint8_t retVal = EC20x_NOTOK; + uint8_t retVal = IOLTE_NOTOK; sprintf(sdata,"AT+QICLOSE=%d",connectID); - if(EC20xcommand(sdata,"OK","yy",EC20x_CMD_TIMEOUT,2,NULL)!=0) - retVal = EC20x_FAILURE; - else retVal = EC20x_OK; + if(IOLtecommand(sdata,"OK","yy",IOLTE_CMD_TIMEOUT,2,NULL)!=0) + retVal = IOLTE_FAILURE; + else retVal = IOLTE_OK; return retVal; } + /* TCP连接服务器 */ @@ -570,33 +284,33 @@ int EC20xlib::connect(char *ip, char *port,uint8_t connectID) { char data_buff[100];//格式化AT指令时使用 char sdata[30];//格式化AT指令时使用 - uint8_t retVal = EC20x_NOTOK; + uint8_t retVal = IOLTE_NOTOK; String reply = ""; - logln("creat a socket"); + log4g("%s","creat a socket"); //判断是否连接服务器 sprintf(data_buff,"AT+QIOPEN=1,%d,\"TCP\",\"%s\",%s,0,1",connectID,ip,port); sprintf(sdata,"+QIOPEN: %d,",connectID); - if(EC20xcommand(data_buff,sdata,"yy",EC20x_CMD_TIMEOUT+3000,1,&reply) == 0) + if(IOLtecommand(data_buff,sdata,"yy",IOLTE_CMD_TIMEOUT+3000,1,&reply) == 0) { memset(sdata,0,sizeof(sdata)); sprintf(sdata,"+QIOPEN: %d,0",connectID); if(reply.indexOf(sdata) != -1) { - logln("connected server successfully"); - return EC20x_OK; + log4g("%s","connected server successfully"); + return IOLTE_OK; } else //连接失败 { - logln("send failed,close the socket!!"); + log4g("%s","send failed,close the socket!!"); if(disconnect(connectID)!=0) - retVal = EC20x_FAILURE; + retVal = IOLTE_FAILURE; memset(sdata,0,sizeof(sdata)); sprintf(sdata,"+QIOPEN: %d,0",connectID); - if(EC20xcommand(data_buff,sdata,"yy",EC20x_CMD_TIMEOUT,2,NULL) != 0) - retVal = EC20x_FAILURE; + if(IOLtecommand(data_buff,sdata,"yy",IOLTE_CMD_TIMEOUT,2,NULL) != 0) + retVal = IOLTE_FAILURE; else - retVal = EC20x_OK; + retVal = IOLTE_OK; } } return retVal; @@ -606,13 +320,13 @@ int EC20xlib::connect(char *ip, char *port,uint8_t connectID) *函数名: EC20_senddata *功能 : 发送AQ *参数 : buf 数据 -* buflen 数据包长度 +* buflen 数据包长度 *输入 : 无 *输出 : 无 ************************************************/ int EC20xlib::postdata(char* buf, int buflen,uint8_t connectID) { - uint8_t retVal = EC20x_NOTOK; + uint8_t retVal = IOLTE_NOTOK; char data_buff[30];//格式化AT指令时使用 // 喂狗操作,防止信号问题引起死机 @@ -621,84 +335,109 @@ int EC20xlib::postdata(char* buf, int buflen,uint8_t connectID) //获取数据包长度,合并成字符串 sprintf(data_buff,"AT+QISEND=%d,%d\r\n",connectID,buflen); - if(EC20x_OK == EC20xcommand(data_buff,strlen(data_buff),">","yy",EC20x_CMD_TIMEOUT+3000,1,NULL)){ + if(IOLTE_OK == IOLtecommand(data_buff,strlen(data_buff),">","yy",IOLTE_CMD_TIMEOUT+3000,1,NULL)){ //发送数据 - if(EC20x_OK == EC20xcommand(buf,buflen,"SEND OK","yy",EC20x_CMD_TIMEOUT+3000,1,NULL)) - retVal = EC20x_OK; - else retVal = EC20x_FAILURE; - }else retVal = EC20x_FAILURE; + if(IOLTE_OK == IOLtecommand(buf,buflen,"SEND OK","yy",IOLTE_CMD_TIMEOUT+3000,1,NULL)) + retVal = IOLTE_OK; + else retVal = IOLTE_FAILURE; + }else retVal = IOLTE_FAILURE; return retVal; } /* -发布主题 +MQTT解析收到的数据 */ -void EC20xlib::loop()//typ_NEC_AQData *AQData_Send, -{ - // uint16_t i=0; - uint32_t entry = millis(); - int commaPosition = 0;//存储还没有分离出来的字符串 - String reply = ""; - - if(resend_state == STSTE_RE){//发送完成,可以接收 - reply = read(); -#ifdef ESP32 - yield(); -#endif - if(reply != "") { - log("Reply in "); - log((millis() - entry)); - log(" ms: "); - logln(reply); - /*示例 - +QIURC: "recv",0,256 - HTTP/1.1 200 - Content-Type: application/json;charset=UTF-8 - Transfer-Encoding: chunked - Vary: Accept-Encoding - Date: Wed, 05 Jan 2022 02:27:40 GMT - - 63 - {"pulseAll":4997520,"dateStr":"2022-01-05 10:27:40","pulseMoney":4997520,"state":"1","pulseFree":0} - */ - if(reply.indexOf("+QIURC: \"recv\",") != -1)//接收到数据 - { - logln("recv data"); - uint8_t connectID = 0; - uint16_t datalen = 0; - char sendbuf[1000] = ""; - commaPosition = reply.indexOf("+QIURC: \"recv\","); - reply = reply.substring(commaPosition,reply.length());//截取有效数据 - - for(uint8_t i =0;i<3;i++){ - commaPosition = reply.indexOf(","); - if(commaPosition != -1){//检测到“,” - if(i == 1){ - connectID = atoi(reply.substring(0,commaPosition).c_str()); - log("connectID:"); - logln(connectID); - datalen = atoi(reply.substring(commaPosition+1,reply.indexOf("\r")).c_str()); - logln("datalen---------------------------------"); - logln(datalen); - break; +byte EC20xlib::ParseMsg(String data){ + String reply = data; + if(pubsub_f == 0){ + if(data.indexOf("+QMTRECV:")>=0){ + //+QMTRECV: 0,0,"/server/123/17091089680/7619748","{"command_type": 2,"command_id":5,"dzcount":160}" + log4g("%s","*******************************"); + log4g("%d",reply.length()); + log4g("%s",reply.c_str()); + log4g("%s","*******************************"); + char topic[TOPIC_MAX_LEN]; + char msg[PAYLOAD_MAX_LEN]; + char payload_len[4]; + do{ + memset(topic,0,sizeof(topic)); + memset(msg,0,sizeof(msg)); + memset(payload_len,0,sizeof(payload_len)); + int commaPosition = 0;//存储还没有分离出来的字符串 + commaPosition = reply.indexOf("+QMTRECV:"); + if(commaPosition>=0){ + reply = reply.substring(commaPosition,reply.length()); + for(uint8_t i = 0;i<4;i++){ + commaPosition = reply.indexOf(',');//检测字符串中的逗号 + + if(commaPosition != -1){//如果有逗号存在就向下执行 + if(i == 2){//获取topic + log4g("%s", reply.substring(1,commaPosition-1).c_str());//打印出第一个逗号位置的字符串 + reply.substring(1,commaPosition-1).toCharArray(topic,reply.substring(1,commaPosition-1).length()+1); + } + else if(i == 3){//获取msg长度 + log4g("%s", reply.substring(0,commaPosition));//打印出第一个逗号位置的字符串 + reply.substring(0,commaPosition).toCharArray(payload_len,reply.substring(0,commaPosition).length()+1); + } + reply = reply.substring(commaPosition+1, reply.length());//打印字符串,从当前位置+1开始 } - reply = reply.substring(commaPosition+1,reply.length()); } + //log4g("%s",reply.c_str()); + if(atoi(payload_len) != 0){//获取topic的内容 + reply.substring(1,reply.length()).toCharArray(msg,atoi(payload_len)+1); + log4g("%s",msg); + if(subdatecallback != NULL) + subdatecallback(topic,msg,atoi(payload_len),0); + //log4g("===================================="); + } + //log4g("%s",reply.c_str()); } - commaPosition = reply.indexOf("HTTP/1.1"); - if(commaPosition != -1){//检测到“HTTP/1.1” - reply = reply.substring(commaPosition,reply.length()); - reply.toCharArray(sendbuf,reply.length()+1,0); - getdatecallback(sendbuf,datalen,connectID); - } - } - if(reply.indexOf("+QIURC: \"closed\",") != -1)//断开连接 - { - logln("断开连接"); - } - if(reply.indexOf("+QMTRECV:") != -1) - { - } + }while(reply.indexOf("+QMTRECV:")>=0); + } + /* + 整型。MQTT 客户端标识符。 + 整型。错误代码。详细信息请参考下表。 + 1 连接被服务器断开或者重置 执行 AT+QMTOPEN 重建 MQTT 连接 + 2 发送 PINGREQ 包超时或者失败 首先反激活 PDP,然后再激活 PDP 并重建MQTT 连接。 + 3 发送 CONNECT 包超时或者失败 1. 查看输入的用户名和密码是否正确。 + 2. 确保客户端 ID 未被占用。 + 3. 重建 MQTT 连接,并尝试再次发送CONNECT 包到服务器。 + 4 接收 CONNACK 包超时或者失败 1. 查看输入的用户名和密码是否正确。 + 2. 确保客户端 ID 未被占用。 + 3. 重建 MQTT 连接,并尝试再次发送CONNECT 包到服务器。 + 5 客户端向服务器发送 DISCONNECT 包,但 + 是服务器主动断开 MQTT 连接 正常流程。 + 6 因为发送数据包总是失败,客户端主动断开MQTT 连接 1. 确保数据正确。 + 2. 可能因为网络拥堵或者其他错误,尝试重建 MQTT 连接。 + 7 链路不工作或者服务器不可用 确保当前链路或者服务器可用。 + 8 客户端主动断开 MQTT 连接 尝试重建连接。 + 9~255 留作将来使用 + */ + if(data.indexOf("+QMTSTAT: 0,1") >= 0){//连接被服务器断开或者重置 + connected_state = false; + if(eventcallback != NULL) eventcallback(MQTT_DISCONNECT); + } + if(data.indexOf("+QMTSTAT: 0,2") >= 0){//发送 PINGREQ 包超时或者失败 + connected_state = false; + if(eventcallback != NULL) eventcallback(MQTT_DISCONNECT); + } + if(data.indexOf("+QMTSTAT: 0,3") >= 0){//发送 CONNECT 包超时或者失败 + connected_state = false; + if(eventcallback != NULL) eventcallback(MQTT_DISCONNECT); + } + if(data.indexOf("+QMTSTAT: 0,8") >= 0){//客户端主动断开 MQTT 连接 + connected_state = false; + if(eventcallback != NULL) eventcallback(MQTT_DISCONNECT); } } + //没有返回 +} + +/* +判断模块返回 +*/ +void EC20xlib::loop() +{ + String reply = ""; + IOLtewaitFor(NULL,NULL,0, &reply); } \ No newline at end of file diff --git a/EC20xlib.h b/EC20xlib.h index ec3dff6..95fa172 100644 --- a/EC20xlib.h +++ b/EC20xlib.h @@ -2,80 +2,58 @@ #define EC20xlib_h #include +#include -//#define DEBUG -#ifdef DEBUG -#define log(msg) Serial.print(msg) -#define logln(msg) Serial.println(msg) -#else -#define log(msg) -#define logln(msg) +#ifndef TOPIC_MAX_LEN +#define TOPIC_MAX_LEN 128 +#endif + +#ifndef PAYLOAD_MAX_LEN +#define PAYLOAD_MAX_LEN 1024 #endif #define USE_HTTP 1 #define USE_MQTT 1 #define USE_TCP 1 -#define countof(a) (sizeof(a) / sizeof(a[0])) - -#define EC20x_OK 0 -#define EC20x_NOTOK 1 -#define EC20x_TIMEOUT 2 -#define EC20x_FAILURE 3 -#define EC20x_DISCONNECT 4 - -#define EC20x_CMD_TIMEOUT 2000 - -#define STSTE_SEND 1 -#define STSTE_RE 0 - -extern "C" { -typedef byte (*recallbackFunction)(char* buf, int buflen,uint8_t connectID); -typedef byte (*subcallbackFunction)(char *topic,char* msg,int buflen,uint8_t qos1); -} //#define EC20xconn Serial2 -class EC20xlib { +class EC20xlib : public IOLTE{ public: EC20xlib(Stream &serial); ~EC20xlib(); - byte begin(); - byte blockUntilReady(long baudRate); - - void powerCycle(int pin); - void powerOn(int pin); - void powerOff(int pin); - - int getSignalStrength(); - String getRealTimeClock(); - int connect(char *ip, char *port,uint8_t connectID); int disconnect(uint8_t connectID); - uint8_t connected(); + boolean connected(); + byte ParseMsg(String data); int postdata(char* buf, int buflen,uint8_t connectID); + byte connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password,uint8_t keepalive,uint8_t will_qos,char *will,char *will_payload); + byte connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password,uint8_t keepalive); byte connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password); + byte connectMqttServer(char *clientID,char *ip,char *port); + byte SubTopic(char *topic,uint8_t qos1); byte PubTopic(char *topic,char* msg,uint8_t qos1); byte UnSubTopic(char *topic,uint8_t qos1); byte getMessage(char *topic,char* msg,uint8_t qos1); - byte Read_CCID(char *DataBuf); - byte Read_IMEI(char *DataBuf); + void attachsubcallback(subcallbackFunction newFunction); void attachtcpcallback(recallbackFunction newFunction); + void attacheventcallback(eventcallbackFunction newFunction); void loop(); + String Read_TimeClock(); + int Read_Signal(); + byte Read_CCID(char *DataBuf); + byte Read_IMEI(char *DataBuf); private: Stream* EC20xconn; uint8_t resend_state; char *ip; char *port; - String read(); - byte EC20xcommand(const char *command, const char *resp1, const char *resp2, int timeout, int repetitions, String *response); - byte EC20xcommand(const char *command,uint16_t commandlen, const char *resp1, const char *resp2, int timeout, int repetitions, String *response); - byte EC20xwaitFor(const char *resp1, const char *resp2, int timeout, String *response); - long detectRate(); - char setRate(long baudRate); + bool connected_state = false; recallbackFunction getdatecallback = NULL; subcallbackFunction subdatecallback = NULL; + eventcallbackFunction eventcallback = NULL; }; #endif diff --git a/N716lib.cpp b/N716lib.cpp new file mode 100644 index 0000000..e18aeb7 --- /dev/null +++ b/N716lib.cpp @@ -0,0 +1,361 @@ +#include +#include "N716lib.h" +#include "iolog.h" + +#define min _min +#define max _max + +uint8_t pubsub_f = 0; + +///////////////////////////////////////////// +// Public methods. +// +void N716lib::attachsubcallback(subcallbackFunction newFunction){ + subdatecallback = newFunction; +} +void N716lib::attachtcpcallback(recallbackFunction newFunction){ + getdatecallback = newFunction; +} +void N716lib::attacheventcallback(eventcallbackFunction newFunction){ + eventcallback = newFunction; +} + +N716lib::N716lib(Stream &serial) : IOLTE(serial){ + +} + + +N716lib::~N716lib() { + + //delete N716conn; +} +byte N716lib::begin() { + int returnValue = IOLTE_NOTOK; + returnValue = initcom(); + log4g("%d",returnValue); + if(returnValue == IOLTE_OK){//激活网络 + returnValue = IOLtecommand("AT+XIIC=1", "yy", "OK", IOLTE_CMD_TIMEOUT+3000, 3, NULL); + if ( returnValue <= IOLTE_OK) return returnValue; + } + return returnValue; +} + +// Get the strength of the GSM signal. +int N716lib::Read_Signal() { + String response = ""; + uint32_t respStart = 0; + int strength, error = 0; + + // Issue the command and wait for the response. + IOLtecommand("AT+CSQ", "OK", "+CSQ", IOLTE_CMD_TIMEOUT, 2, &response); + + respStart = response.indexOf("+CSQ"); + if (respStart < 0) { + return 0; + } + + sscanf(response.substring(respStart).c_str(), "+CSQ: %d,%d", + &strength, &error); + + // Bring value range 0..31 to 0..100%, don't mind rounding.. + strength = (strength * 100) / 31; + return strength; +} + +/************************************************ +*函数名:ReadCCID +*功能 :读取SIM卡CCID号 +*参数 :DataBuf(输出) CCID号码缓存 应大于20 +*返回 : 0 成功 1 失败 +************************************************/ +byte N716lib::Read_CCID(char *DataBuf) +{ + uint8_t re = 0; + uint32_t respStart = 0; + String response = ""; + //发送读取IMSI命令 + re=IOLtecommand("AT+CCID\r\n","+CCID","yy",IOLTE_CMD_TIMEOUT, 2, &response); + //Serial.println(response); + respStart = response.indexOf("+CCID"); + if (respStart < 0) { + return 0; + } + sscanf(response.substring(respStart).c_str(), "+CCID: %s",DataBuf); + + return re; +} +/************************************************ +*函数名:EC20_Read_IMEI +*功能 :读取模块IMEI号 +*参数 :DataBuf(输出) IMEI号码缓存 应大于20 +*返回 : 0 成功 1 失败 +************************************************/ +byte N716lib::Read_IMEI(char *DataBuf) +{ + uint8_t re = 0; + uint32_t respStart = 0; + String response = ""; + //发送读取IMSI命令 + re = IOLtecommand("AT+GSN","OK","yy",IOLTE_CMD_TIMEOUT, 2, &response); + //Serial.println(response); + + //Serial.println(response); + respStart = response.indexOf("+GSN:"); + if (respStart < 0) { + return 0; + } + sscanf(response.substring(respStart).c_str(), "+GSN: %s",DataBuf); + return re; +} + +// Get the real time from the modem. Time will be returned as yy/MM/dd,hh:mm:ss+XX +String N716lib::Read_TimeClock() { + String response = ""; + + // Issue the command and wait for the response. + IOLtecommand("AT+CCLK?", "OK", "yy", IOLTE_CMD_TIMEOUT, 1, &response); + int respStart = response.indexOf("+CCLK: \"") + 8; + response.setCharAt(respStart - 1, '-'); + + return response.substring(respStart, response.indexOf("\"")); +} + +// Read some data from the N716 in a non-blocking manner. +byte N716lib::connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password,uint8_t keepalive,uint8_t will_qos,char *will,char *will_payload) { + char sendbuf[100]; + String response = ""; + + for(int i = 0; i<2;i++) + { + if((username != NULL)&&(password != NULL)) + sprintf(sendbuf,"AT+MQTTCONNPARAM=\"%s\",\"%s\",\"%s\"",clientID,username,password); + else sprintf(sendbuf,"AT+MQTTCONNPARAM=\"%s\",\"%s\",\"%s\"",clientID,username,password); + if (IOLtecommand(sendbuf, "yy", "OK", IOLTE_CMD_TIMEOUT+3000, 3, &response) > IOLTE_OK) + { + //根据需要设置遗嘱消息 + if((will !=NULL)&&(will_payload != NULL)){ + memset(sendbuf,0,sizeof(sendbuf)); + sprintf(sendbuf,"AT+MQTTWILLPARAM=0,1,\"%s\",\"%s\"",will,will_payload); + if (IOLtecommand(sendbuf, "yy", "OK", IOLTE_CMD_TIMEOUT+3000, 3, &response) <= IOLTE_OK) return IOLTE_FAILURE; + log4g("%s","set will OK."); + } + memset(sendbuf,0,sizeof(sendbuf)); + sprintf(sendbuf,"AT+MQTTCONN=\"%s:%s\",%d,%d",ip,port,0,keepalive); + if (IOLtecommand(sendbuf, "yy", "OK", IOLTE_CMD_TIMEOUT+3000, 3, &response) > IOLTE_OK) + { + log4g("%s","connect OK."); + connected_state = true; + return IOLTE_OK; + } + else return IOLTE_FAILURE; + } + else return IOLTE_FAILURE; + } + return IOLTE_FAILURE; + /* + sprintf(sendbuf,"AT+QMTOPEN=0,\"%s\",%s",ip,port); + if (IOLTE_OK != IOLtecommand(sendbuf, "+QMTOPEN: 0,0", "yy", IOLTE_CMD_TIMEOUT+3000, 3, &response)) + { + if (IOLTE_OK != IOLtecommand("AT+QMTCLOSE=0", "+QMTCLOSE: 0,0", "yy", IOLTE_CMD_TIMEOUT+3000, 3, &response)) + return IOLTE_FAILURE; + if (IOLTE_OK != IOLtecommand(sendbuf, "+QMTOPEN: 0,0", "yy", IOLTE_CMD_TIMEOUT+3000, 3, &response)) + return IOLTE_FAILURE; + } + + memset(sendbuf,0,sizeof(sendbuf)); + sprintf(sendbuf,"AT+QMTCONN=0,\"%s\",\"%s\",\"%s\"",clientID,username,password); + if (IOLTE_OK != IOLtecommand(sendbuf, "+QMTCONN: 0,0,0", "yy", IOLTE_CMD_TIMEOUT, 3, &response)) return IOLTE_FAILURE; + log4g("%s","connect OK."); + + return IOLTE_OK; + */ +} +byte N716lib::connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password,uint8_t keepalive) { + return connectMqttServer(clientID,ip,port,username,password,keepalive,1,NULL,NULL); +} +byte N716lib::connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password) { + return connectMqttServer(clientID,ip,port,username,password,60,1,NULL,NULL); +} +byte N716lib::connectMqttServer(char *clientID,char *ip,char *port) { + return connectMqttServer(clientID,ip,port,NULL,NULL,60,1,NULL,NULL); +} +/* +取消订阅主题 +*/ +byte N716lib::UnSubTopic(char *topic,uint8_t qos1) +{ + char data_buff[300];//格式化AT指令时使用 + String response = ""; + log4g("%s","UnSubTopic"); + if (IOLTE_OK == IOLtecommand("AT+MQTTSTATE?","+MQTTSTATE: 1","yy",5000,5,NULL)) + { + //订阅需要的主题 + sprintf(data_buff,"AT+MQTTUNSUB=\"%s\"",topic); + if (IOLtecommand(data_buff,"OK","yy",5000,5,&response) <= IOLTE_OK) return IOLTE_FAILURE; + return IOLTE_OK; + } + return IOLTE_FAILURE; +} +/* +订阅主题 +AT+MQTTSUB=<"topicname">, + +响应时间 + 最大响应时间 30s。 +参数 + <"topicname"> 订阅的主题,最大长度 128。 + 服务质量,目前支持 Qos=0,1 和 2 + +*/ +byte N716lib::SubTopic(char *topic,uint8_t qos1) +{ + char data_buff[300];//格式化AT指令时使用 + + log("SubTopic: "); + log4g("%s",topic); + if (IOLtecommand("AT+MQTTSTATE?","+MQTTSTATE: 1","yy",5000,5,NULL) > IOLTE_OK) + { + //订阅需要的主题 + sprintf(data_buff,"AT+MQTTSUB=\"%s\",%d",topic,qos1); + if (IOLtecommand(data_buff,"OK","yy",5000,5,NULL) <= IOLTE_OK) return IOLTE_FAILURE; + return IOLTE_OK; + } + return IOLTE_FAILURE; +} +/* +发布主题 + + AT+MQTTPUB=,,<"topicname">,<"message"> + 响应时间 + 最大响应时间 30s。 + 参数 + 保留标志,数字类型,0 和 1。 + 服务质量,目前仅支持 Qos=0,1 和 2。 + <"topicname"> 发布的主题,最大长度 128。 + <"message"> 发布的消息,最大长度 1024。 +*/ +byte N716lib::PubTopic(char *topic,char* msg,uint8_t qos1)//typ_NEC_AQData *AQData_Send, +{ + char data_buff[300];//格式化AT指令时使用 + String response = ""; + String reply = ""; + + log4g("%s","PubTopic"); + pubsub_f = 1; + if (IOLtecommand("AT+MQTTSTATE?","+MQTTSTATE: 1","yy",5000,5,NULL) > IOLTE_OK) { + sprintf(data_buff,"AT+MQTTPUBS=%d,%d,\"%s\",%d",1,qos1,topic,strlen(msg)); + if (IOLtecommand(data_buff,">","yy",5000,5,&response) <= IOLTE_OK) return IOLTE_FAILURE; + reply+=response; + if (IOLtecommand(msg,"OK","yy",5000,5,&response) <= IOLTE_OK) return IOLTE_FAILURE; + reply+=response; + pubsub_f = 0; + ParseMsg(reply); + return IOLTE_OK; + } + return IOLTE_FAILURE; +} +bool N716lib::connected(){ + return connected_state; +} +byte N716lib::ParseMsg(String data){ + String reply = data; + if(pubsub_f == 0){ + if(data.indexOf("+MQTTSUB:") >= 0){ + log4g("%s","*******************************"); + log4g("%s",reply.c_str()); + log4g("%s","*******************************"); + char topic[TOPIC_MAX_LEN]; + char msg[PAYLOAD_MAX_LEN]; + char payload_len[4]; + do{ + memset(topic,0,sizeof(topic)); + memset(msg,0,sizeof(msg)); + memset(payload_len,0,sizeof(payload_len)); + int commaPosition = 0;//存储还没有分离出来的字符串 + commaPosition = reply.indexOf("+MQTTSUB:"); + if(commaPosition>=0){ + reply = reply.substring(commaPosition,reply.length()); + for(uint8_t i = 0;i<3;i++){ + commaPosition = reply.indexOf(',');//检测字符串中的逗号 + + if(commaPosition != -1){//如果有逗号存在就向下执行 + if(i == 1){//获取topic + log4g("%s", reply.substring(1,commaPosition-1).c_str());//打印出第一个逗号位置的字符串 + reply.substring(1,commaPosition-1).toCharArray(topic,reply.substring(1,commaPosition-1).length()+1); + } + else if(i == 2){//获取msg长度 + log4g("%s", reply.substring(0,commaPosition));//打印出第一个逗号位置的字符串 + reply.substring(0,commaPosition).toCharArray(payload_len,reply.substring(0,commaPosition).length()+1); + } + reply = reply.substring(commaPosition+1, reply.length());//打印字符串,从当前位置+1开始 + log4g("%s",reply.c_str()); + } + } + //log4g("%s",reply.c_str()); + if(atoi(payload_len) != 0){//获取topic的内容 + reply.substring(0,reply.length()).toCharArray(msg,atoi(payload_len)+1); + log4g("%s",msg); + if(subdatecallback != NULL) + subdatecallback(topic,msg,atoi(payload_len),0); + //log4g("===================================="); + } + //log4g("%s",reply.c_str()); + } + }while(reply.indexOf("+MQTTSUB:")>=0); + } + if(data.indexOf("+MQTTDISCONNED: Link Closed")>=0){ + connected_state = false; + if(eventcallback != NULL) eventcallback(MQTT_DISCONNECT); + } + } + return 0; +} +void N716lib::loop()//typ_NEC_AQData *AQData_Send, +{ + String reply = ""; + IOLtewaitFor(NULL,NULL,0, &reply); +} +byte N716lib::http_send(String url,String port,String api,byte mode,String data,uint32_t datalen,byte data_type,uint32_t timeout, int repetitions, String *response,uint32_t offsize,uint32_t size){ + char data_buff[300];//格式化AT指令时使用 + String reply = ""; + String urlapi = url+api; + log4g("%s",urlapi); + sprintf(data_buff,"AT+HTTPPARA=url,%s",urlapi.c_str()); + if (IOLtecommand(data_buff,"OK","yy",5000,5,NULL) <= IOLTE_OK) return IOLTE_FAILURE; + else{ + memset(data_buff,0,sizeof(data_buff)); + sprintf(data_buff,"AT+HTTPPARA=port,%s",port.c_str()); + if (IOLtecommand(data_buff,"OK","yy",5000,5,NULL) <= IOLTE_OK) return IOLTE_FAILURE; + else{//设置http连接 + if (IOLtecommand("AT+HTTPSETUP","OK","yy",5000,5,NULL) <= IOLTE_OK) return IOLTE_FAILURE; + else{ + memset(data_buff,0,sizeof(data_buff)); + sprintf(data_buff,"AT+HTTPACTION=%d,%d,%d",mode,datalen,data_type); + if (IOLtecommand(data_buff,">","yy",5000,5,NULL) <= IOLTE_OK) return IOLTE_FAILURE; + else{ + if (IOLtecommand(data.c_str(),"OK","yy",5000,5,NULL) <= IOLTE_OK) return IOLTE_FAILURE; + else{ + if(IOLtewaitFor("+HTTPRECV","+HTTPCLOSED",timeout, &reply) > IOLTE_OK){ + if(reply.indexOf("+HTTPRECV")>=0){ + log4g("%s","*******************************"); + log4g("%s",reply); + log4g("%s","*******************************"); + *response = reply; + return IOLTE_OK+1; + } + } + else return IOLTE_FAILURE;//等待数据返回 + } + } + } + } + } + return IOLTE_FAILURE; +} +byte N716lib::http_post(String url,String port,String api,String data,uint32_t datalen,byte data_type,String *response){ + return http_send(url,port,api,POST,data,datalen,data_type,30000, 0, response,0,0); +} +byte N716lib::http_post(String url,String port,uint32_t datalen,byte data_type){ + return 0; +} + diff --git a/N716lib.h b/N716lib.h new file mode 100644 index 0000000..baa2370 --- /dev/null +++ b/N716lib.h @@ -0,0 +1,58 @@ +#ifndef N716lib_h +#define N716lib_h + +#include +#include + +#ifndef TOPIC_MAX_LEN +#define TOPIC_MAX_LEN 128 +#endif + +#ifndef PAYLOAD_MAX_LEN +#define PAYLOAD_MAX_LEN 1024 +#endif + +#define GET 0 +#define HEAD 1 +#define POST 2 +#define OPEN_MODE 99 + +#define http_x_www_form_urlencoded 0 +#define http_text 1 +#define http_json 2 +#define http_xml 3 +#define http_html 4 + +class N716lib : public IOLTE{ +public: + N716lib(Stream &serial); + ~N716lib(); + byte ParseMsg(String data); + byte begin(); + byte connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password,uint8_t keepalive,uint8_t will_qos,char *will,char *will_payload); + byte connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password,uint8_t keepalive); + byte connectMqttServer(char *clientID,char *ip,char *port,char *username,char *password); + byte connectMqttServer(char *clientID,char *ip,char *port); + byte SubTopic(char *topic,uint8_t qos1); + byte PubTopic(char *topic,char* msg,uint8_t qos1); + byte UnSubTopic(char *topic,uint8_t qos1); + void attachsubcallback(subcallbackFunction newFunction); + void attachtcpcallback(recallbackFunction newFunction); + void attacheventcallback(eventcallbackFunction newFunction); + bool connected(); + void loop(); + byte http_send(String url,String port,String api,byte mode,String data,uint32_t datalen,byte data_type,uint32_t timeout, int repetitions, String *response,uint32_t offsize,uint32_t size); + byte http_post(String url,String port,String api,String data,uint32_t datalen,byte data_type,String *response); + byte http_post(String url,String port,uint32_t datalen,byte data_type); + + String Read_TimeClock(); + int Read_Signal(); + byte Read_CCID(char *DataBuf); + byte Read_IMEI(char *DataBuf); +private: + recallbackFunction getdatecallback = NULL; + subcallbackFunction subdatecallback = NULL; + eventcallbackFunction eventcallback = NULL; + bool connected_state = false; +}; +#endif diff --git a/iolte_base.cpp b/iolte_base.cpp new file mode 100644 index 0000000..361ceb9 --- /dev/null +++ b/iolte_base.cpp @@ -0,0 +1,349 @@ +#include +#include "iolte_base.h" + +#define min _min +#define max _max + +///////////////////////////////////////////// +// Public methods. +// + +IOLTE::IOLTE(Stream &serial) { +#ifndef ESP32 +#ifdef ESP8266 + iolte_conn = new SoftwareSerial(receivePin, transmitPin, false, 1024); +#else + iolte_conn = new SoftwareSerial(receivePin, transmitPin, false); + +#endif + iolte_conn->setTimeout(100); +#else + iolte_conn = &serial; + + iolte_conn->setTimeout(50);//阻塞时间 +#endif +} + + +IOLTE::~IOLTE() { + + //delete iolte_conn; +} + + +// Block until the module is ready. +byte IOLTE::blockUntilReady(long baudRate) { + + byte response = IOLTE_NOTOK; + while (IOLTE_OK != response) { + response = initcom(); + // This means the modem has failed to initialize and we need to reboot + // it. + if (IOLTE_FAILURE == response) { + return IOLTE_FAILURE; + } + delay(1000); + logln("Waiting for module to be ready..."); + } + return IOLTE_OK; +} + + +// Initialize the software serial connection and change the baud rate from the +// default (autodetected) to the desired speed. +byte IOLTE::initcom() { + + iolte_conn->flush(); + + // Factory reset. + if (IOLtecommand("AT", "OK", "yy", IOLTE_CMD_TIMEOUT, 20, NULL) <= IOLTE_OK) return IOLTE_FAILURE; + + // Echo off. + if (IOLtecommand("ATE0", "OK", "yy", IOLTE_CMD_TIMEOUT, 2, NULL) <= IOLTE_OK) return IOLTE_FAILURE; + + // Echo off. + if (IOLtecommand("ATI", "OK", "yy", IOLTE_CMD_TIMEOUT, 2, NULL) <= IOLTE_OK) return IOLTE_FAILURE; + + // 检查SIM卡是否在位 + if (IOLtecommand("AT+CPIN?", "+CPIN: READY", "yy", IOLTE_CMD_TIMEOUT, 2, NULL) <= IOLTE_OK) return IOLTE_FAILURE; + + // 检查CSQ + if (IOLtecommand("AT+CSQ", "OK", "yy", IOLTE_CMD_TIMEOUT, 2, NULL) <= IOLTE_OK) return IOLTE_FAILURE; + + // 查看是否注册GSM网络 + if (IOLtecommand("AT+CREG?", "+CREG: 0,1", "+CREG: 0,5", IOLTE_CMD_TIMEOUT, 20, NULL) <= IOLTE_OK) return IOLTE_FAILURE; + + // 查看是否注册GPRS网络 + if (IOLtecommand("AT+CGREG?", "+CGREG: 0,1", "+CGREG: 0,5", IOLTE_CMD_TIMEOUT, 20, NULL) <= IOLTE_OK) return IOLTE_FAILURE; + + return IOLTE_OK; +} + + +// Reboot the module by setting the specified pin HIGH, then LOW. The pin should +// be connected to a P-MOSFET, not the EC20x's POWER pin. +void IOLTE::powerCycle(int pin) { + logln("Power-cycling module..."); + + powerOff(pin); + + delay(2000); + + powerOn(pin); + + // Give the module some time to settle. + logln("Done, waiting for the module to initialize..."); + delay(20000); + logln("Done."); + + iolte_conn->flush(); +} + + +// Turn the modem power completely off. +void IOLTE::powerOff(int pin) { + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); +} + + +// Turn the modem power on. +void IOLTE::powerOn(int pin) { + pinMode(pin, OUTPUT); + digitalWrite(pin, HIGH); +} + +///////////////////////////////////////////// +// Private methods. +// + +// Read some data from the EC20x in a non-blocking manner. +String IOLTE::read() { + String reply = ""; + if (iolte_conn->available()) { + reply = iolte_conn->readString(); + } + + // XXX: Replace NULs with \xff so we can match on them. + for (int x = 0; x < reply.length(); x++) { + if (reply.charAt(x) == 0) { + reply.setCharAt(x, 255); + } + } + return reply; +} + +// 发送AT命令 +// Issue a command. +byte IOLTE::IOLtecommand(const char *command, const char *resp1, const char *resp2, int timeout, int repetitions, String *response) { + int returnValue = IOLTE_NOTOK; + byte count = 0; + + // Get rid of any buffered output. + iolte_conn->flush(); + + //loop(); + while ((count < repetitions) && (returnValue < IOLTE_OK)) { + log("Issuing command: "); + logln(command); + + iolte_conn->write(command); + iolte_conn->write('\r'); + returnValue = IOLtewaitFor(resp1, resp2, timeout, response); + count++; + } + + return returnValue; +} +/**************************************** +* 函数名:EC20_SendCommand +* 描述 :向GPRS模块发送命令 +* CommandStr 命令字符串 +* Check 检测的命令回复 +* retry 重试次数 +* 每次等待命令回复的时间 单位100mS +* 输入 :无 +* 输出 :无 +****************************************/ +byte IOLTE::IOLtecommand(const char *command,uint16_t commandlen, const char *resp1, const char *resp2, int timeout, int repetitions, String *response) +{ + byte returnValue = IOLTE_NOTOK; + byte count = 0; + resend_state = STSTE_SEND; + // Get rid of any buffered output. + iolte_conn->flush(); + + while (count < repetitions && returnValue < IOLTE_OK) { + log("Issuing command: "); + logln(command); + + iolte_conn->write(command,commandlen); + iolte_conn->write('\r'); + returnValue = IOLtewaitFor(resp1, resp2, timeout, response); + count++; + } + resend_state = STSTE_RE; + return returnValue; +} + + +// Wait for responses. +byte IOLTE::IOLtewaitFor(const char *resp1,const char *resp2,const char *resp3,const char *resp4, const char *resp5, int timeout, String *response) { + unsigned long entry = millis(); + String reply = ""; + byte retVal = 0; + + do { + reply += read(); + yield(); +#ifdef ESP8266 + yield(); +#endif + if(resp1 && (reply.indexOf(resp1)>=0)){//接收到需要的数据 + retVal = IOLTE_OK+1; + break; + } + else if(resp2 && (reply.indexOf(resp2)>=0)){//接收到需要的数据 + retVal = IOLTE_OK+2; + break; + } + else if(resp3 && (reply.indexOf(resp3)>=0)){//接收到需要的数据 + retVal = IOLTE_OK+3; + break; + } + else if(resp4 && (reply.indexOf(resp4)>=0)){//接收到需要的数据 + retVal = IOLTE_OK+4; + break; + } + else if(resp5 && (reply.indexOf(resp5)>=0)){//接收到需要的数据 + retVal = IOLTE_OK+5; + break; + } + delay(1); + } while ((millis() - entry) < timeout); + + if (reply != "") { + log("Reply in "); + log((millis() - entry)); + log(" ms: "); + logln(reply); + if (response != NULL) { + *response = reply; + } + this->ParseMsg(reply); + } + if (retVal == 0) retVal = IOLTE_TIMEOUT; + + /* reply2 = reply; + if(reply2.indexOf("+QIURC: \"recv\",") != -1)//接收到数据 + { + logln("recv data"); + uint8_t connectID = 0; + uint16_t datalen = 0; + char sendbuf[1000] = ""; + int commaPosition = 0; + commaPosition = reply2.indexOf("+QIURC: \"recv\","); + reply2 = reply2.substring(commaPosition,reply2.length());//截取有效数据 + + for(uint8_t i =0;i<3;i++){ + commaPosition = reply2.indexOf(","); + if(commaPosition != -1){//检测到“,” + if(i == 1){ + connectID = atoi(reply2.substring(0,commaPosition).c_str()); + log("connectID:"); + logln(connectID); + datalen = atoi(reply2.substring(commaPosition+1,reply2.indexOf("\r")).c_str()); + logln("datalen---------------------------------"); + logln(datalen); + break; + } + reply2 = reply2.substring(commaPosition+1,reply2.length()); + } + } + commaPosition = reply2.indexOf("HTTP/1.1"); + if(commaPosition != -1){//检测到“HTTP/1.1” + reply2 = reply2.substring(commaPosition,reply2.length()); + reply2.toCharArray(sendbuf,reply2.length()+1,0); + getdatecallback(sendbuf,datalen,connectID); + } + } */ + return retVal; +} + +byte IOLTE::IOLtewaitFor(const char *resp1,const char *resp2, int timeout, String *response) { + return IOLtewaitFor(resp1,resp2,NULL,NULL,NULL, timeout, response); +} +void IOLTE::attachcallback(callbackFunction newFunction){ + datecallback = newFunction; +} + +// void IOLTE::loop() +// { +// // uint16_t i=0; +// uint32_t entry = millis(); +// int commaPosition = 0;//存储还没有分离出来的字符串 +// String reply = ""; + +// if(resend_state == STSTE_RE){//发送完成,可以接收 +// reply = read(); +// #ifdef ESP32 +// yield(); +// #endif +// if(reply != "") { +// log("Reply in "); +// log((millis() - entry)); +// log(" ms: "); +// logln(reply); +// /*示例 +// +QIURC: "recv",0,256 +// HTTP/1.1 200 +// Content-Type: application/json;charset=UTF-8 +// Transfer-Encoding: chunked +// Vary: Accept-Encoding +// Date: Wed, 05 Jan 2022 02:27:40 GMT + +// 63 +// {"pulseAll":4997520,"dateStr":"2022-01-05 10:27:40","pulseMoney":4997520,"state":"1","pulseFree":0} +// */ +// if(reply.indexOf("+QIURC: \"recv\",") != -1)//接收到数据 +// { +// logln("recv data"); +// uint8_t connectID = 0; +// uint16_t datalen = 0; +// char sendbuf[1000] = ""; +// commaPosition = reply.indexOf("+QIURC: \"recv\","); +// reply = reply.substring(commaPosition,reply.length());//截取有效数据 + +// for(uint8_t i =0;i<3;i++){ +// commaPosition = reply.indexOf(","); +// if(commaPosition != -1){//检测到“,” +// if(i == 1){ +// connectID = atoi(reply.substring(0,commaPosition).c_str()); +// log("connectID:"); +// logln(connectID); +// datalen = atoi(reply.substring(commaPosition+1,reply.indexOf("\r")).c_str()); +// logln("datalen---------------------------------"); +// logln(datalen); +// break; +// } +// reply = reply.substring(commaPosition+1,reply.length()); +// } +// } +// commaPosition = reply.indexOf("HTTP/1.1"); +// if(commaPosition != -1){//检测到“HTTP/1.1” +// reply = reply.substring(commaPosition,reply.length()); +// reply.toCharArray(sendbuf,reply.length()+1,0); +// //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1datecallback(sendbuf,datalen,connectID); +// } +// } +// if(reply.indexOf("+QIURC: \"closed\",") != -1)//断开连接 +// { +// logln("断开连接"); +// } +// if(reply.indexOf("+QMTRECV:") != -1) +// { +// } + +// } +// } +// } \ No newline at end of file diff --git a/iolte_base.h b/iolte_base.h new file mode 100644 index 0000000..9498e28 --- /dev/null +++ b/iolte_base.h @@ -0,0 +1,90 @@ +#ifndef IOLTE_BASE_h +#define IOLTE_BASE_h + +#include + +#define DEBUG +#ifdef DEBUG +#define log(msg) Serial.print(msg) +#define logln(msg) Serial.println(msg) +#else +#define log(msg) +#define logln(msg) +#endif + +#define countof(a) (sizeof(a) / sizeof(a[0])) + +#define IOLTE_NOTOK 0 +#define IOLTE_TIMEOUT 1 +#define IOLTE_FAILURE 2 +#define IOLTE_DISCONNECT 3 +//保持OK是最大值且不超过120 +#define IOLTE_OK 4 + +#define IOLTE_CMD_TIMEOUT 2000 + +#define STSTE_SEND 1 +#define STSTE_RE 0 + +enum EVENT +{ + MQTT_CONNECTED, + MQTT_DISCONNECT, + MQTT_CONNECT_TIMEOUT, + TCP_CONNECTED, + TCP_DISCONNECT, + TCP_CONNECT_TIMEOUT, + HTTP_CONNECT_TIMEOUT +}; + +typedef struct +{ + char ip[20]; //服务器Ip地址 + char port[10]; //端口号 +} httpType; + +//extern "C" { +typedef byte (*redatacheckFunction)(String msg); +typedef byte (*callbackFunction)(char *topic,char* msg,int buflen,uint8_t qos1); +typedef byte (*recallbackFunction)(char* buf, int buflen,uint8_t connectID); +typedef byte (*subcallbackFunction)(char *topic,char* msg,int buflen,uint8_t qos1); +typedef byte (*eventcallbackFunction)(uint8_t event); +//} +//#define EC20xconn Serial2 + +class IOLTE { +public: + IOLTE(Stream &serial); + ~IOLTE(); + virtual byte ParseMsg(String data) = 0; + byte initcom(); + byte blockUntilReady(long baudRate); + + void powerCycle(int pin); + void powerOn(int pin); + void powerOff(int pin); + + byte IOLtecommand(const char *command, const char *resp1, const char *resp2, int timeout, int repetitions, String *response); + byte IOLtecommand(const char *command,uint16_t commandlen, const char *resp1, const char *resp2, int timeout, int repetitions, String *response); + byte IOLtewaitFor(const char *resp1,const char *resp2,const char *resp3,const char *resp4, const char *resp5, int timeout, String *response); + byte IOLtewaitFor(const char *resp1, const char *resp2, int timeout, String *response); + int connect(char *ip, char *port,uint8_t connectID); + int disconnect(uint8_t connectID); + uint8_t connected(); + + void attachredatacallback(redatacheckFunction newFunction); + void attachcallback(callbackFunction newFunction); + void loop(); +private: + Stream* iolte_conn; + uint8_t resend_state; + char *ip; + char *port; + String read(); + + long detectRate(); + char setRate(long baudRate); + callbackFunction datecallback = NULL; + redatacheckFunction datecallback2 = NULL; +}; +#endif diff --git a/library.json b/library.json index c0246f5..3c80bac 100644 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "repository": { "type": "git", - "url": "https://github.com/luyang14/EC20x_lib.git" + "url": "https://github.com/luyang14/IOLTE_Lib.git" }, "frameworks": "arduino", "platforms": "*" diff --git a/library.properties b/library.properties index a895bbf..4f6490a 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=EC20xlib version=0.1.0 -author=Stavros Korokithakis +author=iotts maintainer=iotts sentence=An ESP8266/ESP32/Arduino library for communicating with the EC20 CAT1 module. paragraph= category=Communication -url=https://github.com/luyang14/EC20x_lib.git +url=https://github.com/luyang14/IOLTE_Lib.git architectures=*