731 lines
24 KiB
C++
731 lines
24 KiB
C++
/**
|
||
* @file TinyGsmClientSim7000SSL.h
|
||
* @author Volodymyr Shymanskyy
|
||
* @license LGPL-3.0
|
||
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||
* @date Nov 2016
|
||
*/
|
||
|
||
#ifndef SRC_TINYGSMCLIENTSIM7000SSL_H_
|
||
#define SRC_TINYGSMCLIENTSIM7000SSL_H_
|
||
|
||
// #define TINY_GSM_DEBUG Serial
|
||
// #define TINY_GSM_USE_HEX
|
||
|
||
#define TINY_GSM_MUX_COUNT 2
|
||
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
|
||
|
||
#include "TinyGsmClientSIM70xx.h"
|
||
#include "TinyGsmTCP.tpp"
|
||
#include "TinyGsmSSL.tpp"
|
||
|
||
class TinyGsmSim7000SSL
|
||
: public TinyGsmSim70xx<TinyGsmSim7000SSL>,
|
||
public TinyGsmTCP<TinyGsmSim7000SSL, TINY_GSM_MUX_COUNT>,
|
||
public TinyGsmSSL<TinyGsmSim7000SSL> {
|
||
friend class TinyGsmSim70xx<TinyGsmSim7000SSL>;
|
||
friend class TinyGsmTCP<TinyGsmSim7000SSL, TINY_GSM_MUX_COUNT>;
|
||
friend class TinyGsmSSL<TinyGsmSim7000SSL>;
|
||
|
||
/*
|
||
* Inner Client
|
||
*/
|
||
public:
|
||
class GsmClientSim7000SSL : public GsmClient {
|
||
friend class TinyGsmSim7000SSL;
|
||
|
||
public:
|
||
GsmClientSim7000SSL() {}
|
||
|
||
explicit GsmClientSim7000SSL(TinyGsmSim7000SSL& modem, uint8_t mux = 0) {
|
||
init(&modem, mux);
|
||
}
|
||
|
||
bool init(TinyGsmSim7000SSL* modem, uint8_t mux = 0) {
|
||
this->at = modem;
|
||
sock_available = 0;
|
||
prev_check = 0;
|
||
sock_connected = false;
|
||
got_data = false;
|
||
|
||
if (mux < TINY_GSM_MUX_COUNT) {
|
||
this->mux = mux;
|
||
} else {
|
||
this->mux = (mux % TINY_GSM_MUX_COUNT);
|
||
}
|
||
at->sockets[this->mux] = this;
|
||
|
||
return true;
|
||
}
|
||
|
||
public:
|
||
virtual int connect(const char* host, uint16_t port, int timeout_s) {
|
||
stop();
|
||
TINY_GSM_YIELD();
|
||
rx.clear();
|
||
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
|
||
return sock_connected;
|
||
}
|
||
TINY_GSM_CLIENT_CONNECT_OVERRIDES
|
||
|
||
void stop(uint32_t maxWaitMs) {
|
||
dumpModemBuffer(maxWaitMs);
|
||
at->sendAT(GF("+CACLOSE="), mux);
|
||
sock_connected = false;
|
||
at->waitResponse(3000);
|
||
}
|
||
void stop() override {
|
||
stop(15000L);
|
||
}
|
||
|
||
/*
|
||
* Extended API
|
||
*/
|
||
|
||
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||
};
|
||
|
||
/*
|
||
* Inner Secure Client
|
||
*/
|
||
|
||
class GsmClientSecureSIM7000SSL : public GsmClientSim7000SSL {
|
||
public:
|
||
GsmClientSecureSIM7000SSL() {}
|
||
|
||
explicit GsmClientSecureSIM7000SSL(TinyGsmSim7000SSL& modem,
|
||
uint8_t mux = 0)
|
||
: GsmClientSim7000SSL(modem, mux) {}
|
||
|
||
public:
|
||
bool setCertificate(const String& certificateName) {
|
||
return at->setCertificate(certificateName, mux);
|
||
}
|
||
|
||
virtual int connect(const char* host, uint16_t port,
|
||
int timeout_s) override {
|
||
stop();
|
||
TINY_GSM_YIELD();
|
||
rx.clear();
|
||
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
|
||
return sock_connected;
|
||
}
|
||
TINY_GSM_CLIENT_CONNECT_OVERRIDES
|
||
};
|
||
|
||
/*
|
||
* Constructor
|
||
*/
|
||
public:
|
||
explicit TinyGsmSim7000SSL(Stream& stream)
|
||
: TinyGsmSim70xx<TinyGsmSim7000SSL>(stream),
|
||
certificates() {
|
||
memset(sockets, 0, sizeof(sockets));
|
||
}
|
||
|
||
/*
|
||
* Basic functions
|
||
*/
|
||
protected:
|
||
bool initImpl(const char* pin = NULL) {
|
||
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
|
||
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM7000SSL"));
|
||
|
||
if (!testAT()) { return false; }
|
||
|
||
sendAT(GF("E0")); // Echo Off
|
||
if (waitResponse() != 1) { return false; }
|
||
|
||
#ifdef TINY_GSM_DEBUG
|
||
sendAT(GF("+CMEE=2")); // turn on verbose error codes
|
||
#else
|
||
sendAT(GF("+CMEE=0")); // turn off error codes
|
||
#endif
|
||
waitResponse();
|
||
|
||
DBG(GF("### Modem:"), getModemName());
|
||
|
||
// Enable Local Time Stamp for getting network time
|
||
sendAT(GF("+CLTS=1"));
|
||
if (waitResponse(10000L) != 1) { return false; }
|
||
|
||
// Enable battery checks
|
||
sendAT(GF("+CBATCHK=1"));
|
||
if (waitResponse() != 1) { return false; }
|
||
|
||
SimStatus ret = getSimStatus();
|
||
// if the sim isn't ready and a pin has been provided, try to unlock the sim
|
||
if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
|
||
simUnlock(pin);
|
||
return (getSimStatus() == SIM_READY);
|
||
} else {
|
||
// if the sim is ready, or it's locked but no pin has been provided,
|
||
// return true
|
||
return (ret == SIM_READY || ret == SIM_LOCKED);
|
||
}
|
||
}
|
||
|
||
void maintainImpl() {
|
||
// Keep listening for modem URC's and proactively iterate through
|
||
// sockets asking if any data is avaiable
|
||
bool check_socks = false;
|
||
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
|
||
GsmClientSim7000SSL* sock = sockets[mux];
|
||
if (sock && sock->got_data) {
|
||
sock->got_data = false;
|
||
check_socks = true;
|
||
}
|
||
}
|
||
// modemGetAvailable checks all socks, so we only want to do it once
|
||
// modemGetAvailable calls modemGetConnected(), which also checks allf
|
||
if (check_socks) { modemGetAvailable(0); }
|
||
while (stream.available()) { waitResponse(15, NULL, NULL); }
|
||
}
|
||
|
||
/*
|
||
* Power functions
|
||
*/
|
||
protected:
|
||
// Follows the SIM70xx template
|
||
|
||
/*
|
||
* Generic network functions
|
||
*/
|
||
protected:
|
||
String getLocalIPImpl() {
|
||
sendAT(GF("+CNACT?"));
|
||
if (waitResponse(GF(GSM_NL "+CNACT:")) != 1) { return ""; }
|
||
streamSkipUntil('\"');
|
||
String res = stream.readStringUntil('\"');
|
||
waitResponse();
|
||
return res;
|
||
}
|
||
|
||
/*
|
||
* Secure socket layer functions
|
||
*/
|
||
protected:
|
||
bool setCertificate(const String& certificateName, const uint8_t mux = 0) {
|
||
if (mux >= TINY_GSM_MUX_COUNT) return false;
|
||
certificates[mux] = certificateName;
|
||
return true;
|
||
}
|
||
|
||
/*
|
||
* GPRS functions
|
||
*/
|
||
protected:
|
||
bool gprsConnectImpl(const char* apn, const char* user = NULL,
|
||
const char* pwd = NULL) {
|
||
gprsDisconnect();
|
||
|
||
// Define the PDP context
|
||
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
|
||
waitResponse();
|
||
|
||
// Attach to GPRS
|
||
sendAT(GF("+CGATT=1"));
|
||
if (waitResponse(60000L) != 1) { return false; }
|
||
|
||
// NOTE: **DO NOT** activate the PDP context
|
||
// For who only knows what reason, doing so screws up the rest of the
|
||
// process
|
||
|
||
// Bearer settings for applications based on IP
|
||
// Set the user name and password
|
||
// AT+CNCFG=<ip_type>[,<APN>[,<usename>,<password>[,<authentication>]]]
|
||
//<ip_type> 0: Dual PDN Stack
|
||
// 1: Internet Protocol Version 4
|
||
// 2: Internet Protocol Version 6
|
||
//<authentication> 0: NONE
|
||
// 1: PAP
|
||
// 2: CHAP
|
||
// 3: PAP or CHAP
|
||
if (pwd && strlen(pwd) > 0 && user && strlen(user) > 0) {
|
||
sendAT(GF("+CNCFG=1,\""), apn, "\",\"", "\",\"", user, pwd, '"');
|
||
waitResponse();
|
||
} else if (user && strlen(user) > 0) {
|
||
// Set the user name only
|
||
sendAT(GF("+CNCFG=1,\""), apn, "\",\"", user, '"');
|
||
waitResponse();
|
||
} else {
|
||
// Set the APN only
|
||
sendAT(GF("+CNCFG=1,\""), apn, '"');
|
||
waitResponse();
|
||
}
|
||
|
||
// Activate application network connection
|
||
// This is for most other supported applications outside of the
|
||
// TCP application toolkit (ie, SSL)
|
||
// AT+CNACT=<mode>,<action>
|
||
// <mode> 0: Deactive
|
||
// 1: Active
|
||
// 2: Auto Active
|
||
bool res = false;
|
||
int ntries = 0;
|
||
while (!res && ntries < 5) {
|
||
sendAT(GF("+CNACT=1,\""), apn, GF("\""));
|
||
res = waitResponse(60000L, GF(GSM_NL "+APP PDP: ACTIVE"),
|
||
GF(GSM_NL "+APP PDP: DEACTIVE")) == 1;
|
||
waitResponse();
|
||
ntries++;
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
bool gprsDisconnectImpl() {
|
||
// Shut down the general application TCP/IP connection
|
||
// CNACT will close *all* open application connections
|
||
sendAT(GF("+CNACT=0"));
|
||
if (waitResponse(60000L) != 1) { return false; }
|
||
|
||
sendAT(GF("+CGATT=0")); // Deactivate the bearer context
|
||
if (waitResponse(60000L) != 1) { return false; }
|
||
|
||
return true;
|
||
}
|
||
|
||
/*
|
||
* SIM card functions
|
||
*/
|
||
protected:
|
||
// Follows the SIM70xx template
|
||
|
||
/*
|
||
* Messaging functions
|
||
*/
|
||
protected:
|
||
// Follows all messaging functions per template
|
||
|
||
/*
|
||
* GPS/GNSS/GLONASS location functions
|
||
*/
|
||
protected:
|
||
// Follows the SIM70xx template
|
||
|
||
/*
|
||
* Time functions
|
||
*/
|
||
// Can follow CCLK as per template
|
||
|
||
/*
|
||
* NTP server functions
|
||
*/
|
||
// Can sync with server using CNTP as per template
|
||
|
||
/*
|
||
* Battery functions
|
||
*/
|
||
protected:
|
||
// Follows all battery functions per template
|
||
|
||
/*
|
||
* Client related functions
|
||
*/
|
||
protected:
|
||
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
|
||
bool ssl = false, int timeout_s = 75) {
|
||
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
|
||
|
||
// set the connection (mux) identifier to use
|
||
sendAT(GF("+CACID="), mux);
|
||
if (waitResponse(timeout_ms) != 1) return false;
|
||
|
||
|
||
if (ssl) {
|
||
// set the ssl version
|
||
// AT+CSSLCFG="SSLVERSION",<ctxindex>,<sslversion>
|
||
// <ctxindex> PDP context identifier
|
||
// <sslversion> 0: QAPI_NET_SSL_PROTOCOL_UNKNOWN
|
||
// 1: QAPI_NET_SSL_PROTOCOL_TLS_1_0
|
||
// 2: QAPI_NET_SSL_PROTOCOL_TLS_1_1
|
||
// 3: QAPI_NET_SSL_PROTOCOL_TLS_1_2
|
||
// 4: QAPI_NET_SSL_PROTOCOL_DTLS_1_0
|
||
// 5: QAPI_NET_SSL_PROTOCOL_DTLS_1_2
|
||
// NOTE: despite docs using caps, "sslversion" must be in lower case
|
||
sendAT(GF("+CSSLCFG=\"sslversion\",0,3")); // TLS 1.2
|
||
if (waitResponse(5000L) != 1) return false;
|
||
}
|
||
|
||
// enable or disable ssl
|
||
// AT+CASSLCFG=<cid>,"SSL",<sslFlag>
|
||
// <cid> Application connection ID (set with AT+CACID above)
|
||
// <sslFlag> 0: Not support SSL
|
||
// 1: Support SSL
|
||
sendAT(GF("+CASSLCFG="), mux, ',', GF("ssl,"), ssl);
|
||
waitResponse();
|
||
|
||
if (ssl) {
|
||
// set the PDP context to apply SSL to
|
||
// AT+CSSLCFG="CTXINDEX",<ctxindex>
|
||
// <ctxindex> PDP context identifier
|
||
// NOTE: despite docs using caps, "ctxindex" must be in lower case
|
||
sendAT(GF("+CSSLCFG=\"ctxindex\",0"));
|
||
if (waitResponse(5000L, GF("+CSSLCFG:")) != 1) return false;
|
||
streamSkipUntil('\n'); // read out the certificate information
|
||
waitResponse();
|
||
|
||
if (certificates[mux] != "") {
|
||
// apply the correct certificate to the connection
|
||
// AT+CASSLCFG=<cid>,"CACERT",<caname>
|
||
// <cid> Application connection ID (set with AT+CACID above)
|
||
// <certname> certificate name
|
||
sendAT(GF("+CASSLCFG="), mux, ",CACERT,\"", certificates[mux].c_str(),
|
||
"\"");
|
||
if (waitResponse(5000L) != 1) return false;
|
||
}
|
||
|
||
// set the protocol
|
||
// 0: TCP; 1: UDP
|
||
sendAT(GF("+CASSLCFG="), mux, ',', GF("protocol,0"));
|
||
waitResponse();
|
||
|
||
// set the SSL SNI (server name indication)
|
||
// NOTE: despite docs using caps, "sni" must be in lower case
|
||
sendAT(GF("+CSSLCFG=\"sni\","), mux, ',', GF("\""), host, GF("\""));
|
||
waitResponse();
|
||
}
|
||
|
||
// actually open the connection
|
||
// AT+CAOPEN=<cid>[,<conn_type>],<server>,<port>
|
||
// <cid> TCP/UDP identifier
|
||
// <conn_type> "TCP" or "UDP"
|
||
// NOTE: the "TCP" can't be included
|
||
sendAT(GF("+CAOPEN="), mux, GF(",\""), host, GF("\","), port);
|
||
if (waitResponse(timeout_ms, GF(GSM_NL "+CAOPEN:")) != 1) { return 0; }
|
||
// returns OK/r/n/r/n+CAOPEN: <cid>,<result>
|
||
// <result> 0: Success
|
||
// 1: Socket error
|
||
// 2: No memory
|
||
// 3: Connection limit
|
||
// 4: Parameter invalid
|
||
// 6: Invalid IP address
|
||
// 7: Not support the function
|
||
// 12: Can’t bind the port
|
||
// 13: Can’t listen the port
|
||
// 20: Can’t resolve the host
|
||
// 21: Network not active
|
||
// 23: Remote refuse
|
||
// 24: Certificate’s time expired
|
||
// 25: Certificate’s common name does not match
|
||
// 26: Certificate’s common name does not match and time expired
|
||
// 27: Connect failed
|
||
streamSkipUntil(','); // Skip mux
|
||
|
||
// make sure the connection really opened
|
||
int8_t res = streamGetIntBefore('\n');
|
||
waitResponse();
|
||
|
||
return 0 == res;
|
||
}
|
||
|
||
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
|
||
// send data on prompt
|
||
sendAT(GF("+CASEND="), mux, ',', (uint16_t)len);
|
||
if (waitResponse(GF(">")) != 1) { return 0; }
|
||
|
||
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
|
||
stream.flush();
|
||
|
||
// after posting data, module responds with:
|
||
//+CASEND: <cid>,<result>,<sendlen>
|
||
if (waitResponse(GF(GSM_NL "+CASEND:")) != 1) { return 0; }
|
||
streamSkipUntil(','); // Skip mux
|
||
if (streamGetIntBefore(',') != 0) { return 0; } // If result != success
|
||
return streamGetIntBefore('\n');
|
||
}
|
||
|
||
size_t modemRead(size_t size, uint8_t mux) {
|
||
if (!sockets[mux]) { return 0; }
|
||
|
||
sendAT(GF("+CARECV="), mux, ',', (uint16_t)size);
|
||
|
||
if (waitResponse(GF("+CARECV:")) != 1) { return 0; }
|
||
|
||
// uint8_t ret_mux = stream.parseInt();
|
||
// streamSkipUntil(',');
|
||
// const int16_t len_confirmed = streamGetIntBefore('\n');
|
||
// DBG("### READING:", len_confirmed, "from", ret_mux);
|
||
|
||
// if (ret_mux != mux) {
|
||
// DBG("### Data from wrong mux! Got", ret_mux, "expected", mux);
|
||
// waitResponse();
|
||
// sockets[mux]->sock_available = modemGetAvailable(mux);
|
||
// return 0;
|
||
// }
|
||
|
||
// NOTE: manual says the mux number is returned before the number of
|
||
// characters available, but in tests only the number is returned
|
||
|
||
int16_t len_confirmed = stream.parseInt();
|
||
streamSkipUntil(','); // skip the comma
|
||
if (len_confirmed <= 0) {
|
||
waitResponse();
|
||
sockets[mux]->sock_available = modemGetAvailable(mux);
|
||
return 0;
|
||
}
|
||
|
||
for (int i = 0; i < len_confirmed; i++) {
|
||
uint32_t startMillis = millis();
|
||
while (!stream.available() &&
|
||
(millis() - startMillis < sockets[mux]->_timeout)) {
|
||
TINY_GSM_YIELD();
|
||
}
|
||
char c = stream.read();
|
||
sockets[mux]->rx.put(c);
|
||
}
|
||
waitResponse();
|
||
// DBG("### READ:", len_confirmed, "from", mux);
|
||
// make sure the sock available number is accurate again
|
||
// the module is **EXTREMELY** testy about being asked to read more from
|
||
// the buffer than exits; it will freeze until a hard reset or power cycle!
|
||
sockets[mux]->sock_available = modemGetAvailable(mux);
|
||
return len_confirmed;
|
||
}
|
||
|
||
size_t modemGetAvailable(uint8_t mux) {
|
||
// If the socket doesn't exist, just return
|
||
if (!sockets[mux]) { return 0; }
|
||
// We need to check if there are any connections open *before* checking for
|
||
// available characters. The SIM7000 *will crash* if you ask about data
|
||
// when there are no open connections.
|
||
if (!modemGetConnected(mux)) { return 0; }
|
||
// NOTE: This gets how many characters are available on all connections that
|
||
// have data. It does not return all the connections, just those with data.
|
||
sendAT(GF("+CARECV?"));
|
||
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
|
||
// after the last connection, there's an ok, so we catch it right away
|
||
int res = waitResponse(3000, GF("+CARECV:"), GFP(GSM_OK), GFP(GSM_ERROR));
|
||
// if we get the +CARECV: response, read the mux number and the number of
|
||
// characters available
|
||
if (res == 1) {
|
||
int ret_mux = streamGetIntBefore(',');
|
||
size_t result = streamGetIntBefore('\n');
|
||
GsmClientSim7000SSL* sock = sockets[ret_mux];
|
||
if (sock) { sock->sock_available = result; }
|
||
// if the first returned mux isn't 0 (or is higher than expected)
|
||
// we need to fill in the missing muxes
|
||
if (ret_mux > muxNo) {
|
||
for (int extra_mux = muxNo; extra_mux < ret_mux; extra_mux++) {
|
||
GsmClientSim7000SSL* isock = sockets[extra_mux];
|
||
if (isock) { isock->sock_available = 0; }
|
||
}
|
||
muxNo = ret_mux;
|
||
}
|
||
} else if (res == 2) {
|
||
// if we get an OK, we've reached the last socket with available data
|
||
// so we set any we haven't gotten to yet to 0
|
||
for (int extra_mux = muxNo; extra_mux < TINY_GSM_MUX_COUNT;
|
||
extra_mux++) {
|
||
GsmClientSim7000SSL* isock = sockets[extra_mux];
|
||
if (isock) { isock->sock_available = 0; }
|
||
}
|
||
break;
|
||
} else {
|
||
// if we got an error, give up
|
||
break;
|
||
}
|
||
// Should be a final OK at the end.
|
||
// If every connection was returned, catch the OK here.
|
||
// If only a portion were returned, catch it above.
|
||
if (muxNo == TINY_GSM_MUX_COUNT - 1) { waitResponse(); }
|
||
}
|
||
modemGetConnected(mux); // check the state of all connections
|
||
if (!sockets[mux]) { return 0; }
|
||
return sockets[mux]->sock_available;
|
||
}
|
||
|
||
bool modemGetConnected(uint8_t mux) {
|
||
// NOTE: This gets the state of all connections that have been opened
|
||
// since the last connection
|
||
sendAT(GF("+CASTATE?"));
|
||
|
||
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
|
||
// after the last connection, there's an ok, so we catch it right away
|
||
int res = waitResponse(3000, GF("+CASTATE:"), GFP(GSM_OK),
|
||
GFP(GSM_ERROR));
|
||
// if we get the +CASTATE: response, read the mux number and the status
|
||
if (res == 1) {
|
||
int ret_mux = streamGetIntBefore(',');
|
||
size_t status = streamGetIntBefore('\n');
|
||
// 0: Closed by remote server or internal error
|
||
// 1: Connected to remote server
|
||
// 2: Listening (server mode)
|
||
GsmClientSim7000SSL* sock = sockets[ret_mux];
|
||
if (sock) { sock->sock_connected = (status == 1); }
|
||
// if the first returned mux isn't 0 (or is higher than expected)
|
||
// we need to fill in the missing muxes
|
||
if (ret_mux > muxNo) {
|
||
for (int extra_mux = muxNo; extra_mux < ret_mux; extra_mux++) {
|
||
GsmClientSim7000SSL* isock = sockets[extra_mux];
|
||
if (isock) { isock->sock_connected = false; }
|
||
}
|
||
muxNo = ret_mux;
|
||
}
|
||
} else if (res == 2) {
|
||
// if we get an OK, we've reached the last socket with available data
|
||
// so we set any we haven't gotten to yet to 0
|
||
for (int extra_mux = muxNo; extra_mux < TINY_GSM_MUX_COUNT;
|
||
extra_mux++) {
|
||
GsmClientSim7000SSL* isock = sockets[extra_mux];
|
||
if (isock) { isock->sock_connected = false; }
|
||
}
|
||
break;
|
||
} else {
|
||
// if we got an error, give up
|
||
break;
|
||
}
|
||
// Should be a final OK at the end.
|
||
// If every connection was returned, catch the OK here.
|
||
// If only a portion were returned, catch it above.
|
||
if (muxNo == TINY_GSM_MUX_COUNT - 1) { waitResponse(); }
|
||
}
|
||
return sockets[mux]->sock_connected;
|
||
}
|
||
|
||
/*
|
||
* Utilities
|
||
*/
|
||
public:
|
||
// TODO(vshymanskyy): Optimize this!
|
||
int8_t waitResponse(uint32_t timeout_ms, String& data,
|
||
GsmConstStr r1 = GFP(GSM_OK),
|
||
GsmConstStr r2 = GFP(GSM_ERROR),
|
||
#if defined TINY_GSM_DEBUG
|
||
GsmConstStr r3 = GFP(GSM_CME_ERROR),
|
||
GsmConstStr r4 = GFP(GSM_CMS_ERROR),
|
||
#else
|
||
GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
|
||
#endif
|
||
GsmConstStr r5 = NULL) {
|
||
/*String r1s(r1); r1s.trim();
|
||
String r2s(r2); r2s.trim();
|
||
String r3s(r3); r3s.trim();
|
||
String r4s(r4); r4s.trim();
|
||
String r5s(r5); r5s.trim();
|
||
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
|
||
data.reserve(64);
|
||
uint8_t index = 0;
|
||
uint32_t startMillis = millis();
|
||
do {
|
||
TINY_GSM_YIELD();
|
||
while (stream.available() > 0) {
|
||
TINY_GSM_YIELD();
|
||
int8_t a = stream.read();
|
||
if (a <= 0) continue; // Skip 0x00 bytes, just in case
|
||
data += static_cast<char>(a);
|
||
if (r1 && data.endsWith(r1)) {
|
||
index = 1;
|
||
goto finish;
|
||
} else if (r2 && data.endsWith(r2)) {
|
||
index = 2;
|
||
goto finish;
|
||
} else if (r3 && data.endsWith(r3)) {
|
||
#if defined TINY_GSM_DEBUG
|
||
if (r3 == GFP(GSM_CME_ERROR)) {
|
||
streamSkipUntil('\n'); // Read out the error
|
||
}
|
||
#endif
|
||
index = 3;
|
||
goto finish;
|
||
} else if (r4 && data.endsWith(r4)) {
|
||
index = 4;
|
||
goto finish;
|
||
} else if (r5 && data.endsWith(r5)) {
|
||
index = 5;
|
||
goto finish;
|
||
} else if (data.endsWith(GF("+CARECV:"))) {
|
||
int8_t mux = streamGetIntBefore(',');
|
||
int16_t len = streamGetIntBefore('\n');
|
||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||
sockets[mux]->got_data = true;
|
||
if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
|
||
}
|
||
data = "";
|
||
DBG("### Got Data:", len, "on", mux);
|
||
} else if (data.endsWith(GF("+CADATAIND:"))) {
|
||
int8_t mux = streamGetIntBefore('\n');
|
||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||
sockets[mux]->got_data = true;
|
||
}
|
||
data = "";
|
||
DBG("### Got Data:", mux);
|
||
} else if (data.endsWith(GF("+CASTATE:"))) {
|
||
int8_t mux = streamGetIntBefore(',');
|
||
int8_t state = streamGetIntBefore('\n');
|
||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||
if (state != 1) {
|
||
sockets[mux]->sock_connected = false;
|
||
DBG("### Closed: ", mux);
|
||
}
|
||
}
|
||
data = "";
|
||
} else if (data.endsWith(GF("*PSNWID:"))) {
|
||
streamSkipUntil('\n'); // Refresh network name by network
|
||
data = "";
|
||
DBG("### Network name updated.");
|
||
} else if (data.endsWith(GF("*PSUTTZ:"))) {
|
||
streamSkipUntil('\n'); // Refresh time and time zone by network
|
||
data = "";
|
||
DBG("### Network time and time zone updated.");
|
||
} else if (data.endsWith(GF("+CTZV:"))) {
|
||
streamSkipUntil('\n'); // Refresh network time zone by network
|
||
data = "";
|
||
DBG("### Network time zone updated.");
|
||
} else if (data.endsWith(GF("DST: "))) {
|
||
streamSkipUntil(
|
||
'\n'); // Refresh Network Daylight Saving Time by network
|
||
data = "";
|
||
DBG("### Daylight savings time state updated.");
|
||
} else if (data.endsWith(GF(GSM_NL "SMS Ready" GSM_NL))) {
|
||
data = "";
|
||
DBG("### Unexpected module reset!");
|
||
init();
|
||
data = "";
|
||
}
|
||
}
|
||
} while (millis() - startMillis < timeout_ms);
|
||
finish:
|
||
if (!index) {
|
||
data.trim();
|
||
if (data.length()) { DBG("### Unhandled:", data); }
|
||
data = "";
|
||
}
|
||
// data.replace(GSM_NL, "/");
|
||
// DBG('<', index, '>', data);
|
||
return index;
|
||
}
|
||
|
||
int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
|
||
GsmConstStr r2 = GFP(GSM_ERROR),
|
||
#if defined TINY_GSM_DEBUG
|
||
GsmConstStr r3 = GFP(GSM_CME_ERROR),
|
||
GsmConstStr r4 = GFP(GSM_CMS_ERROR),
|
||
#else
|
||
GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
|
||
#endif
|
||
GsmConstStr r5 = NULL) {
|
||
String data;
|
||
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
|
||
}
|
||
|
||
int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
|
||
GsmConstStr r2 = GFP(GSM_ERROR),
|
||
#if defined TINY_GSM_DEBUG
|
||
GsmConstStr r3 = GFP(GSM_CME_ERROR),
|
||
GsmConstStr r4 = GFP(GSM_CMS_ERROR),
|
||
#else
|
||
GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
|
||
#endif
|
||
GsmConstStr r5 = NULL) {
|
||
return waitResponse(1000, r1, r2, r3, r4, r5);
|
||
}
|
||
|
||
protected:
|
||
GsmClientSim7000SSL* sockets[TINY_GSM_MUX_COUNT];
|
||
String certificates[TINY_GSM_MUX_COUNT];
|
||
};
|
||
|
||
#endif // SRC_TINYGSMCLIENTSIM7000SSL_H_
|