diff --git a/examples/AllFunctions/AllFunctions.ino b/examples/AllFunctions/AllFunctions.ino index 98a0adc..934bac9 100644 --- a/examples/AllFunctions/AllFunctions.ino +++ b/examples/AllFunctions/AllFunctions.ino @@ -61,16 +61,17 @@ SoftwareSerial SerialAT(2, 3); // RX, TX #define TINY_GSM_TEST_WIFI false #define TINY_GSM_TEST_TCP true #define TINY_GSM_TEST_SSL true -// #define TINY_GSM_TEST_CALL true -// #define TINY_GSM_TEST_SMS true -// #define TINY_GSM_TEST_USSD true +#define TINY_GSM_TEST_CALL false +#define TINY_GSM_TEST_SMS false +#define TINY_GSM_TEST_USSD false #define TINY_GSM_TEST_BATTERY true #define TINY_GSM_TEST_TEMPERATURE true #define TINY_GSM_TEST_GSM_LOCATION true +#define TINY_GSM_TEST_NTP true #define TINY_GSM_TEST_TIME true -#define TINY_GSM_TEST_GPS false -// powerdown modem after tests -#define TINY_GSM_POWERDOWN true +#define TINY_GSM_TEST_GPS true +// disconnect and power down modem after tests +#define TINY_GSM_POWERDOWN false // set GSM PIN, if any #define GSM_PIN "" @@ -183,7 +184,7 @@ void loop() { #endif DBG("Waiting for network..."); - if (!modem.waitForNetwork(600000L)) { + if (!modem.waitForNetwork(600000L, true)) { delay(10000); return; } @@ -230,7 +231,7 @@ void loop() { #if TINY_GSM_TEST_TCP && defined TINY_GSM_MODEM_HAS_TCP TinyGsmClient client(modem, 0); const int port = 80; - DBG("Connecting to ", server); + DBG("Connecting to", server); if (!client.connect(server, port)) { DBG("... failed"); } else { @@ -248,12 +249,16 @@ void loop() { // Read data start = millis(); + char logo[634]; + int read_chars = 0; while (client.connected() && millis() - start < 10000L) { while (client.available()) { - SerialMon.write(client.read()); + logo[read_chars] = client.read(); + read_chars++; start = millis(); } } + SerialMon.println(logo); client.stop(); } #endif @@ -261,7 +266,7 @@ void loop() { #if TINY_GSM_TEST_SSL && defined TINY_GSM_MODEM_HAS_SSL TinyGsmClientSecure secureClient(modem, 1); const int securePort = 443; - DBG("Connecting to ", server); + DBG("Connecting securely to", server); if (!secureClient.connect(server, securePort)) { DBG("... failed"); } else { @@ -279,12 +284,16 @@ void loop() { // Read data startS = millis(); - while (secureClient.connected() && millis() - startS < 5000L) { + char logoS[634]; + int read_charsS = 0; + while (secureClient.connected() && millis() - startS < 10000L) { while (secureClient.available()) { - SerialMon.write(secureClient.read()); + logoS[read_charsS] = secureClient.read(); + read_charsS++; startS = millis(); } } + SerialMon.println(logoS); secureClient.stop(); } #endif @@ -398,6 +407,11 @@ void loop() { modem.disableGPS(); #endif +#if TINY_GSM_TEST_NTP && defined TINY_GSM_MODEM_HAS_NTP + DBG("Asking modem to sync with NTP"); + modem.NTPServerSync("132.163.96.5", 20); +#endif + #if TINY_GSM_TEST_TIME && defined TINY_GSM_MODEM_HAS_TIME int year3 = 0; int month3 = 0; @@ -424,21 +438,6 @@ void loop() { DBG("Current Network Time:", time); #endif -#if TINY_GSM_TEST_GPRS - modem.gprsDisconnect(); - delay(5000L); - if (!modem.isGprsConnected()) { - DBG("GPRS disconnected"); - } else { - DBG("GPRS disconnect: Failed."); - } -#endif - -#if TINY_GSM_TEST_WIFI - modem.networkDisconnect(); - DBG("WiFi disconnected"); -#endif - #if TINY_GSM_TEST_BATTERY && defined TINY_GSM_MODEM_HAS_BATTERY uint8_t chargeState = -99; int8_t percent = -99; @@ -455,6 +454,22 @@ void loop() { #endif #if TINY_GSM_POWERDOWN + +#if TINY_GSM_TEST_GPRS + modem.gprsDisconnect(); + delay(5000L); + if (!modem.isGprsConnected()) { + DBG("GPRS disconnected"); + } else { + DBG("GPRS disconnect: Failed."); + } +#endif + +#if TINY_GSM_TEST_WIFI + modem.networkDisconnect(); + DBG("WiFi disconnected"); +#endif + // Try to power-off (modem may decide to restart automatically) // To turn off modem completely, please use Reset/Enable pins modem.poweroff(); diff --git a/src/TinyGsmClient.h b/src/TinyGsmClient.h index 6eb9d2d..a8ae763 100644 --- a/src/TinyGsmClient.h +++ b/src/TinyGsmClient.h @@ -39,10 +39,10 @@ typedef TinyGsmSim7000SSL::GsmClientSecureSIM7000SSL TinyGsmClientSecure; #elif defined(TINY_GSM_MODEM_SIM7070) || defined(TINY_GSM_MODEM_SIM7080) || \ defined(TINY_GSM_MODEM_SIM7090) -#include "TinyGsmClientSIM70x0.h" -typedef TinyGsmSim70x0 TinyGsm; -typedef TinyGsmSim70x0::GsmClientSim70x0 TinyGsmClient; -typedef TinyGsmSim70x0::GsmClientSecureSIM70x0 TinyGsmClientSecure; +#include "TinyGsmClientSIM7080.h" +typedef TinyGsmSim7080 TinyGsm; +typedef TinyGsmSim7080::GsmClientSim7080 TinyGsmClient; +typedef TinyGsmSim7080::GsmClientSecureSIM7080 TinyGsmClientSecure; #elif defined(TINY_GSM_MODEM_SIM5320) || defined(TINY_GSM_MODEM_SIM5360) || \ defined(TINY_GSM_MODEM_SIM5300) || defined(TINY_GSM_MODEM_SIM7100) diff --git a/src/TinyGsmClientSIM7000SSL.h b/src/TinyGsmClientSIM7000SSL.h index 5646f26..512dcb6 100644 --- a/src/TinyGsmClientSIM7000SSL.h +++ b/src/TinyGsmClientSIM7000SSL.h @@ -163,6 +163,23 @@ class TinyGsmSim7000SSL } } + 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 */ @@ -228,9 +245,9 @@ class TinyGsmSim7000SSL sendAT(GF("+CGATT=1")); if (waitResponse(60000L) != 1) { return false; } - // Activate the PDP context - sendAT(GF("+CGACT=1,1")); - waitResponse(60000L); + // NOTE: **DO NOT** activate the PDP context + // For who only knows what reason, doing so screws up the rest of the + // process // Open the definied GPRS bearer context sendAT(GF("+SAPBR=1,1")); @@ -279,8 +296,7 @@ class TinyGsmSim7000SSL ntries++; } - // return res == 1; - return true; + return res == 1; } bool gprsDisconnectImpl() { @@ -352,12 +368,24 @@ class TinyGsmSim7000SSL // 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=,"SSL", + // Application connection ID (set with AT+CACID above) + // 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", // 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 @@ -372,30 +400,24 @@ class TinyGsmSim7000SSL "\""); if (waitResponse(5000L) != 1) return false; } - } - // enable or disable ssl - // AT+CASSLCFG=,"SSL", - // Application connection ID (set with AT+CACID above) - // 0: Not support SSL - // 1: Support SSL - sendAT(GF("+CASSLCFG="), mux, ',', GF("ssl,"), ssl); - waitResponse(); - - // set the protocol - // 0: TCP; 1: UDP - sendAT(GF("+CASSLCFG="), mux, ',', GF("protocol,0")); - waitResponse(); + // set the protocol + // 0: TCP; 1: UDP + sendAT(GF("+CASSLCFG="), mux, ',', GF("protocol,0")); + waitResponse(); - // set the SSL SNI (server name indication) - sendAT(GF("+CSSLCFG=\"sni\","), mux, ',', GF("\""), host, GF("\"")); - 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=[,],, // TCP/UDP identifier // "TCP" or "UDP" - sendAT(GF("+CAOPEN="), mux, GF(",\"TCP\",\""), host, GF("\","), port); + // 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: , // 0: Success @@ -424,12 +446,15 @@ class TinyGsmSim7000SSL } 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(buff), len); stream.flush(); + // after posting data, module responds with: + //+CASEND: ,, if (waitResponse(GF(GSM_NL "+CASEND:")) != 1) { return 0; } streamSkipUntil(','); // Skip mux if (streamGetIntBefore(',') != 0) { return 0; } // If result != success @@ -437,26 +462,32 @@ class TinyGsmSim7000SSL } size_t modemRead(size_t size, uint8_t mux) { - if (!sockets[mux]) return 0; + if (!sockets[mux]) { return 0; } sendAT(GF("+CARECV="), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CARECV:")) != 1) { - sockets[mux]->sock_available = 0; - return 0; - } + if (waitResponse(GF("+CARECV:")) != 1) { return 0; } - stream.read(); - if (stream.peek() == '0') { - waitResponse(); - sockets[mux]->sock_available = 0; - 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; + // } - const int16_t len_confirmed = streamGetIntBefore(','); + // 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) { - sockets[mux]->sock_available = 0; waitResponse(); + sockets[mux]->sock_available = modemGetAvailable(mux); return 0; } @@ -469,54 +500,106 @@ class TinyGsmSim7000SSL char c = stream.read(); sockets[mux]->rx.put(c); } - // DBG("### READ:", len_requested, "from", mux); - // sockets[mux]->sock_available = modemGetAvailable(mux); - auto diff = int64_t(size) - int64_t(len_confirmed); - if (diff < 0) diff = 0; - sockets[mux]->sock_available = diff; 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) { - // NOTE: This gets how many characters are available on all connections + // 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++) { - if (waitResponse(3000, GF(GSM_NL "+CARECV: ")) != 1) { break; } - size_t result = 0; - // if (streamGetIntBefore(',') != muxNo) { // check the mux no - // DBG("### Warning: misaligned mux numbers!"); - // } - streamSkipUntil(','); // skip mux [use muxNo] - result = streamGetIntBefore('\n'); - GsmClientSim7000SSL* sock = sockets[mux]; - if (sock && muxNo == mux) { sock->sock_available = result; } + // 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* sock = sockets[extra_mux]; + if (sock) { sock->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* sock = sockets[extra_mux]; + if (sock) { sock->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(); } } - waitResponse(); // Should be an OK at the end - modemGetConnected(mux); - if (!sockets[mux]) return 0; + 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 + // 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++) { - if (waitResponse(3000, GF(GSM_NL "+CASTATE: ")) != 1) { break; } - uint8_t status = 0; - // if (streamGetIntBefore(',') != muxNo) { // check the mux no - // DBG("### Warning: misaligned mux numbers!"); - // } - streamSkipUntil(','); // skip mux [use muxNo] - status = stream.parseInt(); // Read the status - // 0: Closed by remote server or internal error - // 1: Connected to remote server - // 2: Listening (server mode) - GsmClientSim7000SSL* sock = sockets[mux]; - if (sock && muxNo == mux) { sock->sock_connected = (status == 1); } + // 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* sock = sockets[extra_mux]; + if (sock) { sock->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* sock = sockets[extra_mux]; + if (sock) { sock->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(); } } - waitResponse(); // Should be an OK at the end return sockets[mux]->sock_connected; } @@ -571,21 +654,23 @@ class TinyGsmSim7000SSL } else if (r5 && data.endsWith(r5)) { index = 5; goto finish; - } else if (data.endsWith(GF(GSM_NL "+CARECV:"))) { - int8_t mux = streamGetIntBefore(','); + } 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:", mux); - } else if (data.endsWith(GF(GSM_NL "+CADATAIND:"))) { + 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(GSM_NL "+CASTATE:"))) { + } 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]) { @@ -625,6 +710,7 @@ class TinyGsmSim7000SSL data = ""; DBG("### Unexpected module reset!"); init(); + data = ""; } } } while (millis() - startMillis < timeout_ms); diff --git a/src/TinyGsmClientSIM70x0.h b/src/TinyGsmClientSIM7080.h similarity index 65% rename from src/TinyGsmClientSIM70x0.h rename to src/TinyGsmClientSIM7080.h index 1fc8a63..394bddc 100644 --- a/src/TinyGsmClientSIM70x0.h +++ b/src/TinyGsmClientSIM7080.h @@ -1,13 +1,13 @@ /** - * @file TinyGsmClientSim70x0.h + * @file TinyGsmClientSim7080.h * @author Volodymyr Shymanskyy * @license LGPL-3.0 * @copyright Copyright (c) 2016 Volodymyr Shymanskyy * @date Nov 2016 */ -#ifndef SRC_TINYGSMCLIENTSIM70X0_H_ -#define SRC_TINYGSMCLIENTSIM70X0_H_ +#ifndef SRC_TINYGSMCLIENTSIM7080_H_ +#define SRC_TINYGSMCLIENTSIM7080_H_ // #define TINY_GSM_DEBUG Serial // #define TINY_GSM_USE_HEX @@ -19,28 +19,28 @@ #include "TinyGsmTCP.tpp" #include "TinyGsmSSL.tpp" -class TinyGsmSim70x0 : public TinyGsmSim70xx, - public TinyGsmTCP, - public TinyGsmSSL { - friend class TinyGsmSim70xx; - friend class TinyGsmTCP; - friend class TinyGsmSSL; +class TinyGsmSim7080 : public TinyGsmSim70xx, + public TinyGsmTCP, + public TinyGsmSSL { + friend class TinyGsmSim70xx; + friend class TinyGsmTCP; + friend class TinyGsmSSL; /* * Inner Client */ public: - class GsmClientSim70x0 : public GsmClient { - friend class TinyGsmSim70x0; + class GsmClientSim7080 : public GsmClient { + friend class TinyGsmSim7080; public: - GsmClientSim70x0() {} + GsmClientSim7080() {} - explicit GsmClientSim70x0(TinyGsmSim70x0& modem, uint8_t mux = 0) { + explicit GsmClientSim7080(TinyGsmSim7080& modem, uint8_t mux = 0) { init(&modem, mux); } - bool init(TinyGsmSim70x0* modem, uint8_t mux = 0) { + bool init(TinyGsmSim7080* modem, uint8_t mux = 0) { this->at = modem; sock_available = 0; prev_check = 0; @@ -88,12 +88,12 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, * Inner Secure Client */ - class GsmClientSecureSIM70x0 : public GsmClientSim70x0 { + class GsmClientSecureSIM7080 : public GsmClientSim7080 { public: - GsmClientSecureSIM70x0() {} + GsmClientSecureSIM7080() {} - GsmClientSecureSIM70x0(TinyGsmSim70x0& modem, uint8_t mux = 0) - : GsmClientSim70x0(modem, mux) {} + GsmClientSecureSIM7080(TinyGsmSim7080& modem, uint8_t mux = 0) + : GsmClientSim7080(modem, mux) {} public: bool setCertificate(const String& certificateName) { @@ -114,8 +114,8 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, * Constructor */ public: - explicit TinyGsmSim70x0(Stream& stream) - : TinyGsmSim70xx(stream), + explicit TinyGsmSim7080(Stream& stream) + : TinyGsmSim70xx(stream), certificates() { memset(sockets, 0, sizeof(sockets)); } @@ -126,7 +126,7 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, protected: bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM70x0")); + DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM7080")); if (!testAT()) { return false; } @@ -162,6 +162,23 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, } } + 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++) { + GsmClientSim7080* 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 */ @@ -199,26 +216,6 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, const char* pwd = NULL) { gprsDisconnect(); - // Bearer settings for applications based on IP - // Set the connection type to GPRS - sendAT(GF("+SAPBR=3,1,\"Contype\",\"GPRS\"")); - waitResponse(); - - // Set the APN - sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"'); - waitResponse(); - - // Set the user name - if (user && strlen(user) > 0) { - sendAT(GF("+SAPBR=3,1,\"USER\",\""), user, '"'); - waitResponse(); - } - // Set the password - if (pwd && strlen(pwd) > 0) { - sendAT(GF("+SAPBR=3,1,\"PWD\",\""), pwd, '"'); - waitResponse(); - } - // Define the PDP context sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"'); waitResponse(); @@ -227,21 +224,20 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, sendAT(GF("+CGATT=1")); if (waitResponse(60000L) != 1) { return false; } - // Activate the PDP context - sendAT(GF("+CGACT=1,1")); - waitResponse(60000L); + // NOTE: **DO NOT** activate the PDP context + // For who only knows what reason, doing so screws up the rest of the + // process - // Open the definied GPRS bearer context - sendAT(GF("+SAPBR=1,1")); - waitResponse(85000L); - // Query the GPRS bearer context status - sendAT(GF("+SAPBR=2,1")); - if (waitResponse(30000L) != 1) { return false; } + // Check the APN returned by the server + // not sure why, but the connection is more consistent with this + sendAT(GF("+CGNAPN")); + waitResponse(); // Bearer settings for applications based on IP // Set the user name and password // AT+CNCFG=,,[,[,,[]]] - // PDP Context Identifier (1 is setup above) + // PDP Context Identifier - for reasons not understood by me, + // use PDP context identifier of 0 for what we defined as 1 above // 0: Dual PDN Stack // 1: Internet Protocol Version 4 // 2: Internet Protocol Version 6 @@ -250,44 +246,42 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, // 2: CHAP // 3: PAP or CHAP if (pwd && strlen(pwd) > 0 && user && strlen(user) > 0) { - sendAT(GF("+CNCFG=1,1,\""), apn, "\",\"", "\",\"", user, pwd, '"'); + sendAT(GF("+CNCFG=0,1,\""), apn, "\",\"", "\",\"", user, pwd, '"'); waitResponse(); } else if (user && strlen(user) > 0) { // Set the user name only - sendAT(GF("+CNCFG=1,1,\""), apn, "\",\"", user, '"'); + sendAT(GF("+CNCFG=0,1,\""), apn, "\",\"", user, '"'); waitResponse(); } else { // Set the APN only - sendAT(GF("+CNCFG=1,1,\""), apn, '"'); + sendAT(GF("+CNCFG=0,1,\""), apn, '"'); waitResponse(); } // Activate application network connection - // This is for most other supported applications outside of the - // TCP application toolkit (ie, SSL) // AT+CNACT=, - // PDP Context Identifier (1 is setup above) + // PDP Context Identifier - for reasons not understood by me, + // use PDP context identifier of 0 for what we defined as 1 above // 0: Deactive // 1: Active // 2: Auto Active int res = 0; int ntries = 0; while (res != 1 && ntries < 5) { - sendAT(GF("+CNACT=1,1,\""), apn, GF("\"")); - res = waitResponse(60000L, GF(GSM_NL "+APP PDP: ACTIVE"), - GF(GSM_NL "+APP PDP: DEACTIVE")); + sendAT(GF("+CNACT=0,1")); + res = waitResponse(60000L, GF(GSM_NL "+APP PDP: 0,ACTIVE"), + GF(GSM_NL "+APP PDP: 0,DEACTIVE")); waitResponse(); ntries++; } - // return res == 1; - return true; + return res == 1; } bool gprsDisconnectImpl() { // Shut down the general application TCP/IP connection // CNACT will close *all* open application connections - sendAT(GF("+CNACT=1,0")); + sendAT(GF("+CNACT=0,0")); if (waitResponse(60000L) != 1) { return false; } sendAT(GF("+CGATT=0")); // Deactivate the bearer context @@ -353,13 +347,26 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, // 3: QAPI_NET_SSL_PROTOCOL_TLS_1_2 // 4: QAPI_NET_SSL_PROTOCOL_DTLS_1_0 // 5: QAPI_NET_SSL_PROTOCOL_DTLS_1_2 - sendAT(GF("+CSSLCFG=\"SSLVERSION\",0,3")); // TLS 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=,"SSL", + // Application connection ID (set with AT+CACID above) + // 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", // PDP context identifier - sendAT(GF("+CSSLCFG=\"CTXINDEX\",0")); + // NOTE: despite docs using "CRINDEX" in all caps, the module only + // accepts the command "ctxindex" and it 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(); @@ -373,19 +380,12 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, "\""); if (waitResponse(5000L) != 1) return false; } - } - - // enable or disable ssl - // AT+CASSLCFG=,"SSL", - // Application connection ID (set with AT+CACID above) - // 0: Not support SSL - // 1: Support SSL - sendAT(GF("+CASSLCFG="), mux, ',', GF("SSL,"), ssl); - waitResponse(); - // set the SSL SNI (server name indication) - sendAT(GF("+CSSLCFG=\"SNI\","), mux, ',', GF("\""), host, GF("\"")); - 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=,,,,[,] @@ -398,8 +398,8 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, // URC: // +CAURC: // "recv",,,, - sendAT(GF("+CAOPEN="), mux, GF(",0,\"TCP\",\""), host, GF("\","), port, ',', - 0); + // NOTE: including the fails + sendAT(GF("+CAOPEN="), mux, GF(",0,\"TCP\",\""), host, GF("\","), port); if (waitResponse(timeout_ms, GF(GSM_NL "+CAOPEN:")) != 1) { return 0; } // returns OK/r/n/r/n+CAOPEN: , // 0: Success @@ -428,39 +428,46 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, } 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(buff), len); stream.flush(); - if (waitResponse(GF(GSM_NL "+CASEND:")) != 1) { return 0; } - streamSkipUntil(','); // Skip mux - if (streamGetIntBefore(',') != 0) { return 0; } // If result != success - return streamGetIntBefore('\n'); + // OK after posting data + if (waitResponse() != 1) { return 0; } + + return len; } size_t modemRead(size_t size, uint8_t mux) { - if (!sockets[mux]) return 0; + if (!sockets[mux]) { return 0; } sendAT(GF("+CARECV="), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CARECV:")) != 1) { - sockets[mux]->sock_available = 0; - return 0; - } + if (waitResponse(GF("+CARECV:")) != 1) { return 0; } - stream.read(); - if (stream.peek() == '0') { - waitResponse(); - sockets[mux]->sock_available = 0; - return 0; - } + // uint8_t ret_mux = stream.parseInt(); + // streamSkipUntil(','); + // const int16_t len_confirmed = streamGetIntBefore('\n'); + // DBG("### READING:", len_confirmed, "from", ret_mux); - const int16_t len_confirmed = streamGetIntBefore(','); + // 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) { - sockets[mux]->sock_available = 0; waitResponse(); + sockets[mux]->sock_available = modemGetAvailable(mux); return 0; } @@ -473,54 +480,103 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, char c = stream.read(); sockets[mux]->rx.put(c); } - // DBG("### READ:", len_requested, "from", mux); - // sockets[mux]->sock_available = modemGetAvailable(mux); - auto diff = int64_t(size) - int64_t(len_confirmed); - if (diff < 0) diff = 0; - sockets[mux]->sock_available = diff; waitResponse(); + // make sure the sock available number is accurate again + sockets[mux]->sock_available = modemGetAvailable(mux); return len_confirmed; } size_t modemGetAvailable(uint8_t mux) { - // NOTE: This gets how many characters are available on all connections + // 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++) { - if (waitResponse(3000, GF(GSM_NL "+CARECV: ")) != 1) { break; } - size_t result = 0; - // if (streamGetIntBefore(',') != muxNo) { // check the mux no - // DBG("### Warning: misaligned mux numbers!"); - // } - streamSkipUntil(','); // skip mux [use muxNo] - result = streamGetIntBefore('\n'); - GsmClientSim70x0* sock = sockets[mux]; - if (sock && muxNo == mux) { sock->sock_available = result; } + // 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'); + GsmClientSim7080* 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++) { + GsmClientSim7080* sock = sockets[extra_mux]; + if (sock) { sock->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++) { + GsmClientSim7080* sock = sockets[extra_mux]; + if (sock) { sock->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(); } } - waitResponse(); // Should be an OK at the end - modemGetConnected(mux); - if (!sockets[mux]) return 0; + 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 + // 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++) { - if (waitResponse(3000, GF(GSM_NL "+CASTATE: ")) != 1) { break; } - uint8_t status = 0; - // if (streamGetIntBefore(',') != muxNo) { // check the mux no - // DBG("### Warning: misaligned mux numbers!"); - // } - streamSkipUntil(','); // skip mux [use muxNo] - status = stream.parseInt(); // Read the status - // 0: Closed by remote server or internal error - // 1: Connected to remote server - // 2: Listening (server mode) - GsmClientSim70x0* sock = sockets[mux]; - if (sock && muxNo == mux) { sock->sock_connected = (status == 1); } + // 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) + GsmClientSim7080* 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++) { + GsmClientSim7080* sock = sockets[extra_mux]; + if (sock) { sock->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++) { + GsmClientSim7080* sock = sockets[extra_mux]; + if (sock) { sock->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(); } } - waitResponse(); // Should be an OK at the end return sockets[mux]->sock_connected; } @@ -575,21 +631,23 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, } else if (r5 && data.endsWith(r5)) { index = 5; goto finish; - } else if (data.endsWith(GF(GSM_NL "+CARECV:"))) { - int8_t mux = streamGetIntBefore(','); + } 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:", mux); - } else if (data.endsWith(GF(GSM_NL "+CADATAIND:"))) { + 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(GSM_NL "+CASTATE:"))) { + } 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]) { @@ -629,6 +687,7 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, data = ""; DBG("### Unexpected module reset!"); init(); + data = ""; } } } while (millis() - startMillis < timeout_ms); @@ -669,8 +728,8 @@ class TinyGsmSim70x0 : public TinyGsmSim70xx, } protected: - GsmClientSim70x0* sockets[TINY_GSM_MUX_COUNT]; + GsmClientSim7080* sockets[TINY_GSM_MUX_COUNT]; String certificates[TINY_GSM_MUX_COUNT]; }; -#endif // SRC_TINYGSMCLIENTSIM70X0_H_ +#endif // SRC_TINYGSMCLIENTSIM7080_H_ diff --git a/src/TinyGsmClientSIM70xx.h b/src/TinyGsmClientSIM70xx.h index fe043d5..1f0792e 100644 --- a/src/TinyGsmClientSIM70xx.h +++ b/src/TinyGsmClientSIM70xx.h @@ -128,8 +128,8 @@ class TinyGsmSim70xx : public TinyGsmModem>, // During sleep, the SIM70xx module has its serial communication disabled. // In order to reestablish communication pull the DRT-pin of the SIM70xx - // module LOW for at least 50ms. Then use this function to disable sleep mode. - // The DTR-pin can then be released again. + // module LOW for at least 50ms. Then use this function to disable sleep + // mode. The DTR-pin can then be released again. bool sleepEnableImpl(bool enable = true) { thisModem().sendAT(GF("+CSCLK="), enable); return thisModem().waitResponse() == 1; diff --git a/src/TinyGsmClientSequansMonarch.h b/src/TinyGsmClientSequansMonarch.h index dc782ee..01eb966 100644 --- a/src/TinyGsmClientSequansMonarch.h +++ b/src/TinyGsmClientSequansMonarch.h @@ -596,8 +596,8 @@ class TinyGsmSequansMonarch // SOCK_LISTENING = 4, // SOCK_INCOMING = 5, // SOCK_OPENING = 6, - GsmClientSequansMonarch* sock = sockets[mux % TINY_GSM_MUX_COUNT]; - if (sock && muxNo == mux) { + GsmClientSequansMonarch* sock = sockets[muxNo % TINY_GSM_MUX_COUNT]; + if (sock) { sock->sock_connected = ((status != SOCK_CLOSED) && (status != SOCK_INCOMING) && (status != SOCK_OPENING)); diff --git a/src/TinyGsmClientXBee.h b/src/TinyGsmClientXBee.h index 10dccc0..6ab77d7 100644 --- a/src/TinyGsmClientXBee.h +++ b/src/TinyGsmClientXBee.h @@ -391,9 +391,7 @@ class TinyGsmXBee : public TinyGsmModem, case 115200: changesMade |= changeSettingIfNeeded(GF("BD"), 0x7); break; case 230400: changesMade |= changeSettingIfNeeded(GF("BD"), 0x8); break; case 460800: changesMade |= changeSettingIfNeeded(GF("BD"), 0x9); break; - case 921600: - changesMade |= changeSettingIfNeeded(GF("BD"), 0xA); - break; + case 921600: changesMade |= changeSettingIfNeeded(GF("BD"), 0xA); break; default: { DBG(GF("Specified baud rate is unsupported! Setting to 9600 baud.")); changesMade |= changeSettingIfNeeded(GF("BD"), @@ -735,10 +733,12 @@ class TinyGsmXBee : public TinyGsmModem, } } - bool waitForNetworkImpl(uint32_t timeout_ms = 60000L) { + bool waitForNetworkImpl(uint32_t timeout_ms = 60000L, + bool check_signal = false) { bool retVal = false; XBEE_COMMAND_START_DECORATOR(5, false) for (uint32_t start = millis(); millis() - start < timeout_ms;) { + if (check_signal) { getSignalQuality(); } if (isNetworkConnected()) { retVal = true; break; diff --git a/src/TinyGsmModem.tpp b/src/TinyGsmModem.tpp index 4089c6e..c15b1ec 100644 --- a/src/TinyGsmModem.tpp +++ b/src/TinyGsmModem.tpp @@ -77,8 +77,8 @@ class TinyGsmModem { return thisModem().isNetworkConnectedImpl(); } // Waits for network attachment - bool waitForNetwork(uint32_t timeout_ms = 60000L) { - return thisModem().waitForNetworkImpl(timeout_ms); + bool waitForNetwork(uint32_t timeout_ms = 60000L, bool check_signal = false) { + return thisModem().waitForNetworkImpl(timeout_ms, check_signal); } // Gets signal quality report int16_t getSignalQuality() { @@ -197,8 +197,10 @@ class TinyGsmModem { return status; } - bool waitForNetworkImpl(uint32_t timeout_ms = 60000L) { + bool waitForNetworkImpl(uint32_t timeout_ms = 60000L, + bool check_signal = false) { for (uint32_t start = millis(); millis() - start < timeout_ms;) { + if (check_signal) { thisModem().getSignalQuality(); } if (thisModem().isNetworkConnected()) { return true; } delay(250); } diff --git a/src/TinyGsmNTP.tpp b/src/TinyGsmNTP.tpp index 502c88b..be8ff4e 100644 --- a/src/TinyGsmNTP.tpp +++ b/src/TinyGsmNTP.tpp @@ -61,7 +61,7 @@ class TinyGsmNTP { thisModem().waitResponse(10000L); // Set NTP server and timezone - thisModem().sendAT(GF("+CNTP="), server, ',', String(TimeZone)); + thisModem().sendAT(GF("+CNTP=\""), server, "\",", String(TimeZone)); if (thisModem().waitResponse(10000L) != 1) { return -1; } // Request network synchronization diff --git a/src/TinyGsmTCP.tpp b/src/TinyGsmTCP.tpp index 57d3d96..9b770ef 100644 --- a/src/TinyGsmTCP.tpp +++ b/src/TinyGsmTCP.tpp @@ -141,6 +141,8 @@ class TinyGsmTCP { // with the modem to see if anything has arrived without a UURC. if (!rx.size()) { if (millis() - prev_check > 500) { + // setting got_data to true will tell maintain to run + // modemGetAvailable(mux) got_data = true; prev_check = millis(); } @@ -211,6 +213,8 @@ class TinyGsmTCP { } // Workaround: Some modules "forget" to notify about data arrival if (millis() - prev_check > 500) { + // setting got_data to true will tell maintain to run + // modemGetAvailable() got_data = true; prev_check = millis(); } @@ -250,8 +254,8 @@ class TinyGsmTCP { if (available()) { return true; } #if defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE // If the modem is one where we can read and check the size of the buffer, - // then the 'available()' function will call a check of the current size of - // the buffer and state of the connection. [available calls maintain, + // then the 'available()' function will call a check of the current size + // of the buffer and state of the connection. [available calls maintain, // maintain calls modemGetAvailable, modemGetAvailable calls // modemGetConnected] This cascade means that the sock_connected value // should be correct and all we need