diff --git a/.gitattributes b/.gitattributes index af7e5e0..7a76c7a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,5 @@ /.gitattributes export-ignore /.gitignore export-ignore /.travis.yml export-ignore + +*.ino filter=updateUsersAndPasses \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index ce46eb8..aeccd82 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -23,7 +23,7 @@ with your board before submitting any issues. Main processor board: Modem: -TinyGSM version: +TinyGSM version: Code: ### Scenario, steps to reproduce diff --git a/.gitignore b/.gitignore index 11753c7..051117d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,9 @@ platformio.ini lib/readme.txt include/readme.txt +.atomrc.cson + +# VSCode .vscode/* .vscode/.browse.c_cpp.db* .vscode/c_cpp_properties.json @@ -36,3 +39,7 @@ extras/Module Datasheets/* extras/Older Command Manuals/* extras/Older Command Manuals/* extras/At Command Manuals - Unsupported/* + +# Filters +.gitconfig +filters/* diff --git a/README.md b/README.md index 867fdf8..5739b95 100644 --- a/README.md +++ b/README.md @@ -79,9 +79,9 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - UDP - Not yet supported on any module, though it may be some day - SSL/TLS (HTTPS) - - Supported on¹: + - Supported on: - SIM800, u-Blox, XBee _cellular_, ESP8266, and Sequans Monarch - ¹ - only some device models or firmware revisions have this feature (SIM8xx R14.18, A7, etc.) + - Note: only some device models or firmware revisions have this feature (SIM8xx R14.18, A7, etc.) - Not yet supported on: - Quectel modems, SIM7000, SIM5360/5320/7100/7500/7600 - Not possible on: diff --git a/examples/AllFunctions/AllFunctions.ino b/examples/AllFunctions/AllFunctions.ino index f6df8a6..7a8fb99 100644 --- a/examples/AllFunctions/AllFunctions.ino +++ b/examples/AllFunctions/AllFunctions.ino @@ -60,6 +60,7 @@ #define TINY_GSM_TEST_SMS true #define TINY_GSM_TEST_USSD true #define TINY_GSM_TEST_BATTERY true +#define TINY_GSM_TEST_GPS false // powerdown modem after tests #define TINY_GSM_POWERDOWN false @@ -70,16 +71,40 @@ //#define SMS_TARGET "+380xxxxxxxxx" //#define CALL_TARGET "+380xxxxxxxxx" -// Your GPRS credentials -// Leave empty, if missing user or pass +// Your GPRS credentials, if any const char apn[] = "YourAPN"; const char gprsUser[] = ""; const char gprsPass[] = ""; + +// Your WiFi connection credentials, if applicable const char wifiSSID[] = "YourSSID"; const char wifiPass[] = "YourWiFiPass"; #include +#if TINY_GSM_TEST_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS +#undef TINY_GSM_TEST_GPRS +#undef TINY_GSM_TEST_CALL +#undef TINY_GSM_TEST_SMS +#undef TINY_GSM_TEST_USSD +#undef TINY_GSM_TEST_WIFI +#define TINY_GSM_TEST_GPRS false +#define TINY_GSM_TEST_CALL false +#define TINY_GSM_TEST_SMS false +#define TINY_GSM_TEST_USSD false +#define TINY_GSM_TEST_WIFI true +#endif +#if TINY_GSM_TEST_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS true +#define TINY_GSM_USE_WIFI false +#endif +#if TINY_GSM_TEST_GPS && not defined TINY_GSM_MODEM_HAS_GPS +#undef TINY_GSM_TEST_GPS +#define TINY_GSM_TEST_GPS false +#endif + #ifdef DUMP_AT_COMMANDS #include StreamDebugger debugger(SerialAT, SerialMon); @@ -202,7 +227,7 @@ void loop() { DBG("Phone number (USSD):", ussd_phone_num); #endif -#if defined(TINY_GSM_MODEM_HAS_GPS) +#if TINY_GSM_TEST_GPS modem.enableGPS(); String gps_raw = modem.getGPSraw(); modem.disableGPS(); diff --git a/examples/BlynkClient/BlynkClient.ino b/examples/BlynkClient/BlynkClient.ino index afaed31..0ae92ee 100644 --- a/examples/BlynkClient/BlynkClient.ino +++ b/examples/BlynkClient/BlynkClient.ino @@ -63,8 +63,7 @@ //SoftwareSerial SerialAT(2, 3); // RX, TX -// Your GPRS credentials -// Leave empty, if missing user or pass +// Your GPRS credentials, if any const char apn[] = "YourAPN"; const char user[] = ""; const char pass[] = ""; @@ -91,7 +90,7 @@ void setup() modem.restart(); String modemInfo = modem.getModemInfo(); - SerialMon.print("Modem: "); + SerialMon.print("Modem Info: "); SerialMon.println(modemInfo); // Unlock your SIM card with a PIN diff --git a/examples/FileDownload/FileDownload.ino b/examples/FileDownload/FileDownload.ino index 5d935bc..cfab6a7 100644 --- a/examples/FileDownload/FileDownload.ino +++ b/examples/FileDownload/FileDownload.ino @@ -61,17 +61,19 @@ // Add a reception delay - may be needed for a fast processor at a slow baud rate //#define TINY_GSM_YIELD() { delay(2); } +// Define how you're planning to connect to the internet #define TINY_GSM_USE_GPRS true #define TINY_GSM_USE_WIFI false // set GSM PIN, if any #define GSM_PIN "" -// Your GPRS credentials -// Leave empty, if missing user or pass +// Your GPRS credentials, if any const char apn[] = "YourAPN"; const char gprsUser[] = ""; const char gprsPass[] = ""; + +// Your WiFi connection credentials, if applicable const char wifiSSID[] = "YourSSID"; const char wifiPass[] = "YourWiFiPass"; @@ -82,6 +84,20 @@ const int port = 80; #include #include +// Just in case someone defined the wrong thing.. +#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS false +#define TINY_GSM_USE_WIFI true +#endif +#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS true +#define TINY_GSM_USE_WIFI false +#endif + const char resource[] = "/TinyGSM/test_1k.bin"; uint32_t knownCRC32 = 0x6f50d767; uint32_t knownFileSize = 1024; // In case server does not send it @@ -101,6 +117,12 @@ void setup() { SerialMon.begin(115200); delay(10); + // !!!!!!!!!!! + // Set your reset, enable, power pins here + // !!!!!!!!!!! + + SerialMon.println("Wait..."); + // Set GSM module baud rate SerialAT.begin(115200); delay(3000); @@ -109,9 +131,10 @@ void setup() { // To skip it, call init() instead of restart() SerialMon.println("Initializing modem..."); modem.restart(); + // modem.init(); String modemInfo = modem.getModemInfo(); - SerialMon.print("Modem: "); + SerialMon.print("Modem Info: "); SerialMon.println(modemInfo); #if TINY_GSM_USE_GPRS @@ -135,7 +158,8 @@ void printPercent(uint32_t readLength, uint32_t contentLength) { void loop() { -#if defined TINY_GSM_USE_WIFI && defined TINY_GSM_MODEM_HAS_WIFI +#if TINY_GSM_USE_WIFI + // Wifi connection parameters must be set before waiting for the network SerialMon.print(F("Setting SSID/password...")); if (!modem.networkConnect(wifiSSID, wifiPass)) { SerialMon.println(" fail"); @@ -162,7 +186,8 @@ void loop() { SerialMon.println("Network connected"); } -#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_HAS_GPRS +#if TINY_GSM_USE_GPRS + // GPRS connection parameters are usually set after network registration SerialMon.print(F("Connecting to ")); SerialMon.print(apn); if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { @@ -171,6 +196,10 @@ void loop() { return; } SerialMon.println(" success"); + + if (modem.isGprsConnected()) { + SerialMon.println("GPRS connected"); + } #endif SerialMon.print(F("Connecting to ")); @@ -292,8 +321,14 @@ void loop() { client.stop(); SerialMon.println(F("Server disconnected")); +#if TINY_GSM_USE_WIFI + modem.networkDisconnect(); + SerialMon.println(F("WiFi disconnected")); +#endif +#if TINY_GSM_USE_GPRS modem.gprsDisconnect(); SerialMon.println(F("GPRS disconnected")); +#endif float duration = float(timeElapsed) / 1000; diff --git a/examples/HttpClient/HttpClient.ino b/examples/HttpClient/HttpClient.ino index 37bd6f2..d77f1de 100644 --- a/examples/HttpClient/HttpClient.ino +++ b/examples/HttpClient/HttpClient.ino @@ -63,20 +63,26 @@ #define TINY_GSM_DEBUG SerialMon //#define LOGGING // <- Logging is for the HTTP library +// Range to attempt to autobaud +#define GSM_AUTOBAUD_MIN 9600 +#define GSM_AUTOBAUD_MAX 115200 + // Add a reception delay - may be needed for a fast processor at a slow baud rate //#define TINY_GSM_YIELD() { delay(2); } +// Define how you're planning to connect to the internet #define TINY_GSM_USE_GPRS true #define TINY_GSM_USE_WIFI false // set GSM PIN, if any #define GSM_PIN "" -// Your GPRS credentials -// Leave empty, if missing user or pass +// Your GPRS credentials, if any const char apn[] = "YourAPN"; const char gprsUser[] = ""; const char gprsPass[] = ""; + +// Your WiFi connection credentials, if applicable const char wifiSSID[] = "YourSSID"; const char wifiPass[] = "YourWiFiPass"; @@ -88,6 +94,20 @@ const int port = 80; #include #include +// Just in case someone defined the wrong thing.. +#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS false +#define TINY_GSM_USE_WIFI true +#endif +#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS true +#define TINY_GSM_USE_WIFI false +#endif + #ifdef DUMP_AT_COMMANDS #include StreamDebugger debugger(SerialAT, SerialMon); @@ -111,7 +131,8 @@ void setup() { SerialMon.println("Wait..."); // Set GSM module baud rate - SerialAT.begin(115200); + // TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); + SerialAT.begin(9600); delay(3000); // Restart takes quite some time @@ -121,7 +142,7 @@ void setup() { // modem.init(); String modemInfo = modem.getModemInfo(); - SerialMon.print("Modem: "); + SerialMon.print("Modem Info: "); SerialMon.println(modemInfo); #if TINY_GSM_USE_GPRS @@ -134,7 +155,8 @@ void setup() { void loop() { -#if defined TINY_GSM_USE_WIFI && defined TINY_GSM_MODEM_HAS_WIFI +#if TINY_GSM_USE_WIFI + // Wifi connection parameters must be set before waiting for the network SerialMon.print(F("Setting SSID/password...")); if (!modem.networkConnect(wifiSSID, wifiPass)) { SerialMon.println(" fail"); @@ -161,7 +183,8 @@ void loop() { SerialMon.println("Network connected"); } -#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_HAS_GPRS +#if TINY_GSM_USE_GPRS + // GPRS connection parameters are usually set after network registration SerialMon.print(F("Connecting to ")); SerialMon.print(apn); if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { @@ -170,6 +193,10 @@ void loop() { return; } SerialMon.println(" success"); + + if (modem.isGprsConnected()) { + SerialMon.println("GPRS connected"); + } #endif SerialMon.print(F("Performing HTTP GET request... ")); diff --git a/examples/HttpsClient/HttpsClient.ino b/examples/HttpsClient/HttpsClient.ino index 04874dd..6330ea9 100644 --- a/examples/HttpsClient/HttpsClient.ino +++ b/examples/HttpsClient/HttpsClient.ino @@ -55,20 +55,26 @@ #define TINY_GSM_DEBUG SerialMon //#define LOGGING // <- Logging is for the HTTP library +// Range to attempt to autobaud +#define GSM_AUTOBAUD_MIN 9600 +#define GSM_AUTOBAUD_MAX 115200 + // Add a reception delay - may be needed for a fast processor at a slow baud rate //#define TINY_GSM_YIELD() { delay(2); } +// Define how you're planning to connect to the internet #define TINY_GSM_USE_GPRS true #define TINY_GSM_USE_WIFI false // set GSM PIN, if any #define GSM_PIN "" -// Your GPRS credentials -// Leave empty, if missing user or pass +// Your GPRS credentials, if any const char apn[] = "YourAPN"; const char gprsUser[] = ""; const char gprsPass[] = ""; + +// Your WiFi connection credentials, if applicable const char wifiSSID[] = "YourSSID"; const char wifiPass[] = "YourWiFiPass"; @@ -80,6 +86,20 @@ const int port = 443; #include #include +// Just in case someone defined the wrong thing.. +#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS false +#define TINY_GSM_USE_WIFI true +#endif +#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS true +#define TINY_GSM_USE_WIFI false +#endif + #ifdef DUMP_AT_COMMANDS #include StreamDebugger debugger(SerialAT, SerialMon); @@ -103,7 +123,8 @@ void setup() { SerialMon.println("Wait..."); // Set GSM module baud rate - SerialAT.begin(115200); + // TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); + SerialAT.begin(9600); delay(3000); // Restart takes quite some time @@ -113,7 +134,7 @@ void setup() { // modem.init(); String modemInfo = modem.getModemInfo(); - SerialMon.print("Modem: "); + SerialMon.print("Modem Info: "); SerialMon.println(modemInfo); #if TINY_GSM_USE_GPRS @@ -131,7 +152,8 @@ void setup() { void loop() { -#if defined TINY_GSM_USE_WIFI && defined TINY_GSM_MODEM_HAS_WIFI +#if TINY_GSM_USE_WIFI + // Wifi connection parameters must be set before waiting for the network SerialMon.print(F("Setting SSID/password...")); if (!modem.networkConnect(wifiSSID, wifiPass)) { SerialMon.println(" fail"); @@ -158,7 +180,8 @@ void loop() { SerialMon.println("Network connected"); } -#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_HAS_GPRS +#if TINY_GSM_USE_GPRS + // GPRS connection parameters are usually set after network registration SerialMon.print(F("Connecting to ")); SerialMon.print(apn); if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { @@ -167,10 +190,14 @@ void loop() { return; } SerialMon.println(" success"); + + if (modem.isGprsConnected()) { + SerialMon.println("GPRS connected"); + } #endif SerialMon.print(F("Performing HTTPS GET request... ")); - http.connectionKeepAlive(); // Currently, this is needed for HTTPS + http.connectionKeepAlive(); // Currently, this is needed for HTTPS int err = http.get(resource); if (err != 0) { SerialMon.println(F("failed to connect")); diff --git a/examples/MqttClient/MqttClient.ino b/examples/MqttClient/MqttClient.ino index 4b3878b..1e269d8 100644 --- a/examples/MqttClient/MqttClient.ino +++ b/examples/MqttClient/MqttClient.ino @@ -67,27 +67,29 @@ // Range to attempt to autobaud #define GSM_AUTOBAUD_MIN 9600 -#define GSM_AUTOBAUD_MAX 38400 +#define GSM_AUTOBAUD_MAX 115200 // Add a reception delay - may be needed for a fast processor at a slow baud rate //#define TINY_GSM_YIELD() { delay(2); } +// Define how you're planning to connect to the internet #define TINY_GSM_USE_GPRS true #define TINY_GSM_USE_WIFI false // set GSM PIN, if any #define GSM_PIN "" -// Your GPRS credentials -// Leave empty, if missing user or pass -const char apn[] = "YourAPN"; +// Your GPRS credentials, if any +const char apn[] = "YourAPN"; const char gprsUser[] = ""; const char gprsPass[] = ""; -const char wifiSSID[] = "YourSSID"; + +// Your WiFi connection credentials, if applicable +const char wifiSSID[] = "YourSSID"; const char wifiPass[] = "YourWiFiPass"; // MQTT details -const char* broker = "test.mosquitto.org"; +const char* broker = "broker.hivemq.com"; const char* topicLed = "GsmClientTest/led"; const char* topicInit = "GsmClientTest/init"; @@ -96,6 +98,20 @@ const char* topicLedStatus = "GsmClientTest/ledStatus"; #include #include +// Just in case someone defined the wrong thing.. +#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS false +#define TINY_GSM_USE_WIFI true +#endif +#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS true +#define TINY_GSM_USE_WIFI false +#endif + #ifdef DUMP_AT_COMMANDS #include StreamDebugger debugger(SerialAT, SerialMon); @@ -111,6 +127,42 @@ int ledStatus = LOW; long lastReconnectAttempt = 0; +void mqttCallback(char* topic, byte* payload, unsigned int len) { + SerialMon.print("Message arrived ["); + SerialMon.print(topic); + SerialMon.print("]: "); + SerialMon.write(payload, len); + SerialMon.println(); + + // Only proceed if incoming message's topic matches + if (String(topic) == topicLed) { + ledStatus = !ledStatus; + digitalWrite(LED_PIN, ledStatus); + mqtt.publish(topicLedStatus, ledStatus ? "1" : "0"); + } +} + +boolean mqttConnect() { + SerialMon.print("Connecting to "); + SerialMon.print(broker); + + // Connect to MQTT Broker + boolean status = mqtt.connect("GsmClientTest"); + + // Or, if you want to authenticate MQTT: + //boolean status = mqtt.connect("GsmClientName", "mqtt_user", "mqtt_pass"); + + if (status == false) { + SerialMon.println(" fail"); + return false; + } + SerialMon.println(" success"); + mqtt.publish(topicInit, "GsmClientTest started"); + mqtt.subscribe(topicLed); + return mqtt.connected(); +} + + void setup() { // Set console baud rate SerialMon.begin(115200); @@ -125,7 +177,8 @@ void setup() { SerialMon.println("Wait..."); // Set GSM module baud rate - SerialAT.begin(115200); + // TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); + SerialAT.begin(9600); delay(3000); // Restart takes quite some time @@ -135,7 +188,7 @@ void setup() { // modem.init(); String modemInfo = modem.getModemInfo(); - SerialMon.print("Modem: "); + SerialMon.print("Modem Info: "); SerialMon.println(modemInfo); #if TINY_GSM_USE_GPRS @@ -145,7 +198,8 @@ void setup() { } #endif -#if defined TINY_GSM_USE_WIFI && defined TINY_GSM_MODEM_HAS_WIFI +#if TINY_GSM_USE_WIFI + // Wifi connection parameters must be set before waiting for the network SerialMon.print(F("Setting SSID/password...")); if (!modem.networkConnect(wifiSSID, wifiPass)) { SerialMon.println(" fail"); @@ -172,7 +226,8 @@ void setup() { SerialMon.println("Network connected"); } -#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_HAS_GPRS +#if TINY_GSM_USE_GPRS + // GPRS connection parameters are usually set after network registration SerialMon.print(F("Connecting to ")); SerialMon.print(apn); if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { @@ -181,6 +236,10 @@ void setup() { return; } SerialMon.println(" success"); + + if (modem.isGprsConnected()) { + SerialMon.println("GPRS connected"); + } #endif // MQTT Broker setup @@ -188,26 +247,6 @@ void setup() { mqtt.setCallback(mqttCallback); } -boolean mqttConnect() { - SerialMon.print("Connecting to "); - SerialMon.print(broker); - - // Connect to MQTT Broker - boolean status = mqtt.connect("GsmClientTest"); - - // Or, if you want to authenticate MQTT: - //boolean status = mqtt.connect("GsmClientName", "mqtt_user", "mqtt_pass"); - - if (status == false) { - SerialMon.println(" fail"); - return false; - } - SerialMon.println(" success"); - mqtt.publish(topicInit, "GsmClientTest started"); - mqtt.subscribe(topicLed); - return mqtt.connected(); -} - void loop() { if (!mqtt.connected()) { @@ -226,18 +265,3 @@ void loop() { mqtt.loop(); } - -void mqttCallback(char* topic, byte* payload, unsigned int len) { - SerialMon.print("Message arrived ["); - SerialMon.print(topic); - SerialMon.print("]: "); - SerialMon.write(payload, len); - SerialMon.println(); - - // Only proceed if incoming message's topic matches - if (String(topic) == topicLed) { - ledStatus = !ledStatus; - digitalWrite(LED_PIN, ledStatus); - mqtt.publish(topicLedStatus, ledStatus ? "1" : "0"); - } -} diff --git a/examples/WebClient/WebClient.ino b/examples/WebClient/WebClient.ino index 9c63c62..8207c06 100644 --- a/examples/WebClient/WebClient.ino +++ b/examples/WebClient/WebClient.ino @@ -62,17 +62,20 @@ // Uncomment this if you want to use SSL //#define USE_SSL +// Define how you're planning to connect to the internet #define TINY_GSM_USE_GPRS true #define TINY_GSM_USE_WIFI false // set GSM PIN, if any #define GSM_PIN "" -// Your GPRS credentials -// Leave empty, if missing user or pass +// Your GPRS credentials, if any const char apn[] = "YourAPN"; const char gprsUser[] = ""; const char gprsPass[] = ""; + +// Your WiFi connection credentials, if applicable +const char wifiSSID[] = "YourSSID"; const char wifiPass[] = "YourWiFiPass"; // Server details @@ -81,6 +84,20 @@ const char resource[] = "/TinyGSM/logo.txt"; #include +// Just in case someone defined the wrong thing.. +#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS false +#define TINY_GSM_USE_WIFI true +#endif +#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS true +#define TINY_GSM_USE_WIFI false +#endif + #ifdef DUMP_AT_COMMANDS #include StreamDebugger debugger(SerialAT, SerialMon); @@ -120,7 +137,7 @@ void setup() { // modem.init(); String modemInfo = modem.getModemInfo(); - SerialMon.print("Modem: "); + SerialMon.print("Modem Info: "); SerialMon.println(modemInfo); #if TINY_GSM_USE_GPRS @@ -133,7 +150,8 @@ void setup() { void loop() { -#if defined TINY_GSM_USE_WIFI && defined TINY_GSM_MODEM_HAS_WIFI +#if TINY_GSM_USE_WIFI + // Wifi connection parameters must be set before waiting for the network SerialMon.print(F("Setting SSID/password...")); if (!modem.networkConnect(wifiSSID, wifiPass)) { SerialMon.println(" fail"); @@ -160,7 +178,8 @@ void loop() { SerialMon.println("Network connected"); } -#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_HAS_GPRS +#if TINY_GSM_USE_GPRS + // GPRS connection parameters are usually set after network registration SerialMon.print(F("Connecting to ")); SerialMon.print(apn); if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { @@ -169,6 +188,10 @@ void loop() { return; } SerialMon.println(" success"); + + if (modem.isGprsConnected()) { + SerialMon.println("GPRS connected"); + } #endif SerialMon.print("Connecting to "); diff --git a/library.json b/library.json index a01c544..4fec615 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TinyGSM", - "version": "0.9.7", + "version": "0.9.17", "description": "A small Arduino library for GPRS modules, that just works. Includes examples for Blynk, MQTT, File Download, and Web Client. Supports many GSM, LTE, and WiFi modules with AT command interfaces.", "keywords": "GSM, AT commands, AT, SIM800, SIM900, A6, A7, M590, ESP8266, SIM7000, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900A, SIM900D, SIM908, SIM968, M95, MC60, MC60E, BG96, ublox, Quectel, SIMCOM, AI Thinker, LTE, LTE-M", "authors": diff --git a/library.properties b/library.properties index 46aa51e..549761f 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TinyGSM -version=0.9.7 +version=0.9.17 author=Volodymyr Shymanskyy maintainer=Volodymyr Shymanskyy sentence=A small Arduino library for GPRS modules, that just works. diff --git a/src/TinyGsmClientA6.h b/src/TinyGsmClientA6.h index e20c973..449d330 100644 --- a/src/TinyGsmClientA6.h +++ b/src/TinyGsmClientA6.h @@ -122,7 +122,7 @@ public: { memset(sockets, 0, sizeof(sockets)); } - + virtual ~TinyGsmA6() {} /* @@ -135,20 +135,38 @@ public: bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } + sendAT(GF("&FZE0")); // Factory + Reset + Echo Off if (waitResponse() != 1) { return false; } - sendAT(GF("+CMEE=0")); // Turn off verbose errors + +#ifdef TINY_GSM_DEBUG + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes +#endif waitResponse(); sendAT(GF("+CMER=3,0,0,2")); // Set unsolicited result code output destination waitResponse(); + DBG(GF("### Modem:"), getModemName()); - getSimStatus(); - return true; + + int 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); + } + // if the sim is ready, or it's locked but no pin has been provided, return + // true + else { + return (ret == SIM_READY || ret == SIM_LOCKED); + } } String getModemName() { @@ -525,6 +543,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } chargeState = stream.readStringUntil(',').toInt(); percent = stream.readStringUntil('\n').toInt(); + milliVolts = 0; // Wait for final OK waitResponse(); return true; @@ -562,7 +581,7 @@ protected: } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+CIPSEND="), mux, ',', len); + sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len); if (waitResponse(2000L, GF(GSM_NL ">")) != 1) { return 0; } diff --git a/src/TinyGsmClientBG96.h b/src/TinyGsmClientBG96.h index c54ec08..c9f4510 100644 --- a/src/TinyGsmClientBG96.h +++ b/src/TinyGsmClientBG96.h @@ -158,16 +158,29 @@ public: bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } + sendAT(GF("&FZE0")); // Factory + Reset + Echo Off if (waitResponse() != 1) { return false; } + DBG(GF("### Modem:"), getModemName()); - getSimStatus(); - return true; + + int 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); + } + // if the sim is ready, or it's locked but no pin has been provided, return + // true + else { + return (ret == SIM_READY || ret == SIM_LOCKED); + } } String getModemName() { @@ -496,11 +509,13 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() protected: - bool modemConnect(const char* host, uint16_t port, uint8_t mux, - bool ssl = false, int timeout_s = 20) - { - int rsp; - uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; + bool modemConnect(const char* host, uint16_t port, uint8_t mux, + bool ssl = false, int timeout_s = 20) { + if (ssl) { + DBG("SSL not yet supported on this module!"); + } + int rsp; + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; // (1-16), (0-11),"TCP/UDP/TCP LISTENER/UDP SERVICE", // "/",,,(0-2 0=buffer) @@ -521,7 +536,7 @@ protected: } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+QISEND="), mux, ',', len); + sendAT(GF("+QISEND="), mux, ',', (uint16_t)len); if (waitResponse(GF(">")) != 1) { return 0; } @@ -535,13 +550,13 @@ protected: } size_t modemRead(size_t size, uint8_t mux) { - sendAT(GF("+QIRD="), mux, ',', size); + sendAT(GF("+QIRD="), mux, ',', (uint16_t)size); if (waitResponse(GF("+QIRD:")) != 1) { return 0; } - size_t len = stream.readStringUntil('\n').toInt(); + int len = stream.readStringUntil('\n').toInt(); - for (size_t i=0; i 0) { + DBG("ESP8266 modules do not use an unlock pin!"); + } sendAT(GF("E0")); // Echo Off if (waitResponse() != 1) { return false; @@ -249,7 +251,8 @@ TINY_GSM_MODEM_MAINTAIN_LISTEN() RegStatus getRegistrationStatus() { sendAT(GF("+CIPSTATUS")); if (waitResponse(3000, GF("STATUS:")) != 1) return REG_UNKNOWN; - int status = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5")); + int status = + waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5")); waitResponse(); // Returns an OK after the status return (RegStatus)status; } @@ -275,25 +278,26 @@ TINY_GSM_MODEM_MAINTAIN_LISTEN() bool isNetworkConnected() { RegStatus s = getRegistrationStatus(); - return (s == REG_OK_IP || s == REG_OK_TCP); - } - - bool waitForNetwork(unsigned long timeout_ms = 60000L) { - for (unsigned long start = millis(); millis() - start < timeout_ms; ) { - sendAT(GF("+CIPSTATUS")); - int res1 = waitResponse(3000, GF("busy p..."), GF("STATUS:")); - if (res1 == 2) { - int res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5")); - if (res2 == 2 || res2 == 3) { - waitResponse(); - return true; - } - } - delay(250); + if (s == REG_OK_IP || s == REG_OK_TCP) { + // with these, we're definitely connected + return true; + } + else if (s == REG_OK_NO_TCP) { + // with this, we may or may not be connected + if (getLocalIP() == "") { + return false; + } + else { + return true; + } + } + else { + return false; } - return false; } + TINY_GSM_MODEM_WAIT_FOR_NETWORK() + /* * WiFi functions */ @@ -370,7 +374,7 @@ protected: } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+CIPSEND="), mux, ',', len); + sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len); if (waitResponse(GF(">")) != 1) { return 0; } @@ -383,8 +387,37 @@ protected: } bool modemGetConnected(uint8_t mux) { - RegStatus s = getRegistrationStatus(); - return (s == REG_OK_IP || s == REG_OK_TCP); + sendAT(GF("+CIPSTATUS")); + if (waitResponse(3000, GF("STATUS:")) != 1) return REG_UNKNOWN; + int status = + waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5")); + if (status != 3) { + // if the status is anything but 3, there are no connections open + waitResponse(); // Returns an OK after the status + for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) { + sockets[muxNo]->sock_connected = false; + } + return false; + } + bool verified_connections[TINY_GSM_MUX_COUNT] = {0, 0, 0, 0, 0}; + for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) { + uint8_t has_status = waitResponse(GF("+CIPSTATUS:"), GFP(GSM_OK), GFP(GSM_ERROR)); + if (has_status == 1) { + int returned_mux = stream.readStringUntil(',').toInt(); + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip type + streamSkipUntil(','); // Skip remote IP + streamSkipUntil(','); // Skip remote port + streamSkipUntil(','); // Skip local port + streamSkipUntil('\n'); // Skip client/server type + verified_connections[returned_mux] = 1; + } + if (has_status == 2) break; // once we get to the ok, stop + } + for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) { + sockets[muxNo]->sock_connected = verified_connections[muxNo]; + } + return verified_connections[mux]; } public: diff --git a/src/TinyGsmClientM590.h b/src/TinyGsmClientM590.h index 42e6ca0..f312e59 100644 --- a/src/TinyGsmClientM590.h +++ b/src/TinyGsmClientM590.h @@ -133,20 +133,36 @@ public: bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } + sendAT(GF("&FZE0")); // Factory + Reset + Echo Off if (waitResponse() != 1) { return false; } + #ifdef TINY_GSM_DEBUG - sendAT(GF("+CMEE=2")); - waitResponse(); + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes #endif + waitResponse(); + DBG(GF("### Modem:"), getModemName()); - getSimStatus(); - return true; + + int 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); + } + // if the sim is ready, or it's locked but no pin has been provided, return + // true + else { + return (ret == SIM_READY || ret == SIM_LOCKED); + } } String getModemName() { @@ -443,7 +459,7 @@ protected: } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+TCPSEND="), mux, ',', len); + sendAT(GF("+TCPSEND="), mux, ',', (uint16_t)len); if (waitResponse(GF(">")) != 1) { return 0; } diff --git a/src/TinyGsmClientM95.h b/src/TinyGsmClientM95.h index 2f52589..3e108d0 100644 --- a/src/TinyGsmClientM95.h +++ b/src/TinyGsmClientM95.h @@ -64,7 +64,6 @@ public: this->mux = mux; sock_available = 0; sock_connected = false; - got_data = false; at->sockets[mux] = this; @@ -110,7 +109,6 @@ private: uint8_t mux; uint16_t sock_available; bool sock_connected; - bool got_data; RxFifo rx; }; @@ -157,20 +155,36 @@ public: bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } + sendAT(GF("&FZE0")); // Factory + Reset + Echo Off if (waitResponse() != 1) { return false; } + #ifdef TINY_GSM_DEBUG - sendAT(GF("+CMEE=2")); - waitResponse(); + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes #endif + waitResponse(); + DBG(GF("### Modem:"), getModemName()); - getSimStatus(); - return true; + + int 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); + } + // if the sim is ready, or it's locked but no pin has been provided, return + // true + else { + return (ret == SIM_READY || ret == SIM_LOCKED); + } } String getModemName() { @@ -324,36 +338,71 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() return false; } - //Start TCPIP Task and Set APN, User Name and Password - sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"" ); + // Select TCP/IP transfer mode - NOT transparent mode + sendAT(GF("+QIMODE=0")); if (waitResponse() != 1) { return false; } - //Activate GPRS/CSD Context - sendAT(GF("+QIACT")); - if (waitResponse(10000) != 1) { + // Enable multiple TCP/IP connections + sendAT(GF("+QIMUX=1")); + if (waitResponse() != 1) { return false; } - //Enable multiple TCP/IP connections - sendAT(GF("+QIMUX=1")); + //Start TCPIP Task and Set APN, User Name and Password + sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"" ); if (waitResponse() != 1) { return false; } - //Request an IP header for received data ("IPD(data length):") - sendAT(GF("+QIHEAD=1")); - if (waitResponse() != 1) { + //Activate GPRS/CSD Context + sendAT(GF("+QIACT")); + if (waitResponse(60000L) != 1) { return false; } - //Set Method to Handle Received TCP/IP Data - Retrieve Data by Command + // Check that we have a local IP address + if (localIP() == IPAddress(0,0,0,0)) { + return false; + } + + // Set Method to Handle Received TCP/IP Data + // Mode = 1 - Output a notification when data is received + // “+QIRDI: ,,” sendAT(GF("+QINDI=1")); if (waitResponse() != 1) { return false; } + // // Request an IP header for received data + // // "IPD(data length):" + // sendAT(GF("+QIHEAD=1")); + // if (waitResponse() != 1) { + // return false; + // } + // + // // Do NOT show the IP address of the sender when receiving data + // // The format to show the address is: RECV FROM: : + // sendAT(GF("+QISHOWRA=0")); + // if (waitResponse() != 1) { + // return false; + // } + // + // // Do NOT show the protocol type at the end of the header for received data + // // IPD(data length)(TCP/UDP): + // sendAT(GF("+QISHOWPT=0")); + // if (waitResponse() != 1) { + // return false; + // } + // + // // Do NOT show the destination address before receiving data + // // The format to show the address is: TO: + // sendAT(GF("+QISHOWLA=0")); + // if (waitResponse() != 1) { + // return false; + // } + return true; } @@ -556,20 +605,22 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() 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; - sendAT(GF("+QIOPEN="), mux, GF("\"TCP"), GF("\",\""), host, GF("\","), port); + bool modemConnect(const char* host, uint16_t port, uint8_t mux, + bool ssl = false, int timeout_s = 75) { + if (ssl) { + DBG("SSL not yet supported on this module!"); + } + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + sendAT(GF("+QIOPEN="), mux, GF(",\""), GF("TCP"), GF("\",\""), host, GF("\","), port); int rsp = waitResponse(timeout_ms, GF("CONNECT OK" GSM_NL), GF("CONNECT FAIL" GSM_NL), GF("ALREADY CONNECT" GSM_NL)); - return (1 == rsp); + return (1 == rsp); } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+QISEND="), mux, ',', len); + sendAT(GF("+QISEND="), mux, ',', (uint16_t)len); if (waitResponse(GF(">")) != 1) { return 0; } @@ -579,51 +630,62 @@ protected: return 0; } - bool allAcknowledged = false; - // bool failed = false; - while ( !allAcknowledged ) { - sendAT( GF("+QISACK")); - if (waitResponse(5000L, GF(GSM_NL "+QISACK:")) != 1) { - return -1; - } else { - streamSkipUntil(','); /** Skip total */ - streamSkipUntil(','); /** Skip acknowledged data size */ - if ( stream.readStringUntil('\n').toInt() == 0 ) { - allAcknowledged = true; - } - } - } - waitResponse(5000L); + // bool allAcknowledged = false; + // // bool failed = false; + // while ( !allAcknowledged ) { + // sendAT( GF("+QISACK")); + // if (waitResponse(5000L, GF(GSM_NL "+QISACK:")) != 1) { + // return -1; + // } else { + // streamSkipUntil(','); // Skip total length sent on connection + // streamSkipUntil(','); // Skip length already acknowledged by remote + // // Make sure the total length un-acknowledged is 0 + // if ( stream.readStringUntil('\n').toInt() == 0 ) { + // allAcknowledged = true; + // } + // } + // } + // waitResponse(5000L); - // streamSkipUntil(','); // Skip mux - // return stream.readStringUntil('\n').toInt(); return len; // TODO } size_t modemRead(size_t size, uint8_t mux) { // TODO: Does this work???? // AT+QIRD=,,, - // id = GPRS context number - 0, set in GPRS connect - // sc = roll in connection - 1, client of connection - // sid = index of connection - mux - // len = maximum length of data to send - sendAT(GF("+QIRD=0,1,"), mux, ',', size); - // sendAT(GF("+QIRD="), mux, ',', size); - if (waitResponse(GF("+QIRD:")) != 1) { - return 0; - } - streamSkipUntil(':'); // skip IP address - streamSkipUntil(','); // skip port - streamSkipUntil(','); // skip connection type (TCP/UDP) - size_t len = stream.readStringUntil('\n').toInt(); // read length - for (size_t i=0; isock_available--; - // ^^ One less character available after moving from modem's FIFO to our FIFO + // id = GPRS context number = 0, set in GPRS connect + // sc = role in connection = 1, client of connection + // sid = index of connection = mux + // len = maximum length of data to retrieve + sendAT(GF("+QIRD=0,1,"), mux, ',', (uint16_t)size); + // If it replies only OK for the write command, it means there is no + // received data in the buffer of the connection. + int res = waitResponse(GF("+QIRD:"), GFP(GSM_OK), GFP(GSM_ERROR)); + if (res == 1) { + streamSkipUntil(':'); // skip IP address + streamSkipUntil(','); // skip port + streamSkipUntil(','); // skip connection type (TCP/UDP) + // read the real length of the retrieved data + uint16_t len = stream.readStringUntil('\n').toInt(); + // We have no way of knowing in advance how much data will be in the buffer + // so when data is received we always assume the buffer is completely full. + // Chances are, this is not true and there's really not that much there. + // In that case, make sure we make sure we re-set the amount of data available. + if (len < size) { + sockets[mux]->sock_available = len; + } + for (uint16_t i=0; isock_available--; + // ^^ One less character available after moving from modem's FIFO to our FIFO + } + waitResponse(); // ends with an OK + DBG("### READ:", len, "from", mux); + return len; + } else { + sockets[mux]->sock_available = 0; + return 0; } - waitResponse(); // ends with an OK - DBG("### READ:", len, "from", mux); - return len; } bool modemGetConnected(uint8_t mux) { @@ -690,14 +752,17 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } else if (r5 && data.endsWith(r5)) { index = 5; goto finish; - } else if (data.endsWith(GF(GSM_NL "+QIRD:"))) { // TODO: QIRD? or QIRDI? + } else if (data.endsWith(GF(GSM_NL "+QIRDI:"))) { streamSkipUntil(','); // Skip the context streamSkipUntil(','); // Skip the role int mux = stream.readStringUntil('\n').toInt(); DBG("### Got Data:", mux); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; + // We have no way of knowing how much data actually came in, so + // we set the value to 1500, the maximum possible size. + sockets[mux]->sock_available = 1500; } + data = ""; } else if (data.endsWith(GF("CLOSED" GSM_NL))) { int nl = data.lastIndexOf(GSM_NL, data.length()-8); int coma = data.indexOf(',', nl+2); diff --git a/src/TinyGsmClientMC60.h b/src/TinyGsmClientMC60.h index adac7e9..6036e9c 100644 --- a/src/TinyGsmClientMC60.h +++ b/src/TinyGsmClientMC60.h @@ -68,7 +68,6 @@ public: this->mux = mux; sock_available = 0; sock_connected = false; - got_data = false; at->sockets[mux] = this; @@ -114,7 +113,6 @@ private: uint8_t mux; uint16_t sock_available; bool sock_connected; - bool got_data; RxFifo rx; }; @@ -161,18 +159,32 @@ public: bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } + sendAT(GF("&FZ")); // Factory + Reset waitResponse(); + sendAT(GF("E0")); // Echo Off if (waitResponse() != 1) { return false; } + DBG(GF("### Modem:"), getModemName()); - getSimStatus(); - return true; + + int 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); + } + // if the sim is ready, or it's locked but no pin has been provided, return + // true + else { + return (ret == SIM_READY || ret == SIM_LOCKED); + } } String getModemName() { @@ -338,42 +350,44 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() sendAT(GF("+CGACT=1,1")); waitResponse(60000L); - //Start TCPIP Task and Set APN, User Name and Password - sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"" ); + // Select TCP/IP transfer mode - NOT transparent mode + sendAT(GF("+QIMODE=0")); if (waitResponse() != 1) { return false; } - //Activate GPRS/CSD Context - sendAT(GF("+QIACT")); - if (waitResponse(60000L) != 1) { - return false; - } - - //Enable multiple TCP/IP connections + // Enable multiple TCP/IP connections sendAT(GF("+QIMUX=1")); if (waitResponse() != 1) { return false; } - //Request an IP header for received data ("IPD(data length):") - sendAT(GF("+QIHEAD=1")); + //Start TCPIP Task and Set APN, User Name and Password + sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"" ); if (waitResponse() != 1) { return false; } - //Set Method to Handle Received TCP/IP Data - Retrieve Data by Command - sendAT(GF("+QINDI=1")); - if (waitResponse() != 1) { + //Activate GPRS/CSD Context + sendAT(GF("+QIACT")); + if (waitResponse(60000L) != 1) { return false; } // Check that we have a local IP address - if (localIP() != IPAddress(0,0,0,0)) { - return true; + if (localIP() == IPAddress(0,0,0,0)) { + return false; } - return false; + //Set Method to Handle Received TCP/IP Data + // Mode=2 - Output a notification statement: + // “+QIRDI: ,,,,,< tlen>” + sendAT(GF("+QINDI=2")); + if (waitResponse() != 1) { + return false; + } + + return true; } bool gprsDisconnect() { @@ -563,10 +577,12 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() 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; - sendAT(GF("+QIOPEN="), mux, GF("\"TCP"), GF("\",\""), host, GF("\","), port); + bool ssl = false, int timeout_s = 75) { + if (ssl) { + DBG("SSL not yet supported on this module!"); + } + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + sendAT(GF("+QIOPEN="), mux, GF(",\""), GF("TCP"), GF("\",\""), host, GF("\","), port); int rsp = waitResponse(timeout_ms, GF("CONNECT OK" GSM_NL), GF("CONNECT FAIL" GSM_NL), @@ -575,7 +591,7 @@ protected: } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+QISEND="), mux, ',', len); + sendAT(GF("+QISEND="), mux, ',', (uint16_t)len); if (waitResponse(GF(">")) != 1) { return 0; } @@ -614,23 +630,35 @@ protected: // sc = roll in connection - 1, client of connection // sid = index of connection - mux // len = maximum length of data to send - sendAT(GF("+QIRD=0,1,"), mux, ',', size); - // sendAT(GF("+QIRD="), mux, ',', size); - if (waitResponse(GF("+QIRD:")) != 1) { - return 0; - } - streamSkipUntil(':'); // skip IP address - streamSkipUntil(','); // skip port - streamSkipUntil(','); // skip connection type (TCP/UDP) - size_t len = stream.readStringUntil('\n').toInt(); // read length - for (size_t i=0; isock_available--; - // ^^ One less character available after moving from modem's FIFO to our FIFO + sendAT(GF("+QIRD=0,1,"), mux, ',', (uint16_t)size); + // If it replies only OK for the write command, it means there is no + // received data in the buffer of the connection. + int res = waitResponse(GF("+QIRD:"), GFP(GSM_OK), GFP(GSM_ERROR)); + if (res == 1) { + streamSkipUntil(':'); // skip IP address + streamSkipUntil(','); // skip port + streamSkipUntil(','); // skip connection type (TCP/UDP) + // read the real length of the retrieved data + uint16_t len = stream.readStringUntil('\n').toInt(); + // It's possible that the real length available is less than expected + // This is quite likely if the buffer is broken into packets - which may + // be different sizes. + // If so, make sure we make sure we re-set the amount of data available. + if (len < size) { + sockets[mux]->sock_available = len; + } + for (uint16_t i=0; isock_available--; + // ^^ One less character available after moving from modem's FIFO to our FIFO + } + waitResponse(); + DBG("### READ:", len, "from", mux); + return len; + } else { + sockets[mux]->sock_available = 0; + return 0; } - waitResponse(); - DBG("### READ:", len, "from", mux); - return len; } bool modemGetConnected(uint8_t mux) { @@ -702,13 +730,20 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 6; goto finish; } else if (data.endsWith(GF(GSM_NL "+QIRD:"))) { // TODO: QIRD? or QIRDI? + // +QIRDI: ,,,,,< tlen> streamSkipUntil(','); // Skip the context streamSkipUntil(','); // Skip the role - int mux = stream.readStringUntil('\n').toInt(); - DBG("### Got Data:", mux); + // read the connection id + int mux = stream.readStringUntil(',').toInt(); + // read the number of packets in the buffer + int num_packets = stream.readStringUntil(',').toInt(); + // read the length of the current packet + int len_packet = stream.readStringUntil('\n').toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; + sockets[mux]->sock_available = len_packet*num_packets; } + data = ""; + DBG("### Got Data:", len, "on", mux); } else if (data.endsWith(GF("CLOSED" GSM_NL))) { int nl = data.lastIndexOf(GSM_NL, data.length()-8); int coma = data.indexOf(',', nl+2); diff --git a/src/TinyGsmClientSIM5360.h b/src/TinyGsmClientSIM5360.h index a19b83a..46b5eaf 100644 --- a/src/TinyGsmClientSIM5360.h +++ b/src/TinyGsmClientSIM5360.h @@ -142,16 +142,29 @@ public: bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } + sendAT(GF("E0")); // Echo Off if (waitResponse() != 1) { return false; } + DBG(GF("### Modem:"), getModemName()); - getSimStatus(); - return true; + + int 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); + } + // if the sim is ready, or it's locked but no pin has been provided, return + // true + else { + return (ret == SIM_READY || ret == SIM_LOCKED); + } } String getModemName() { @@ -640,24 +653,29 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() protected: bool modemConnect(const char* host, uint16_t port, uint8_t mux, - bool ssl = false, int timeout_s = 75) { + bool ssl = false, int timeout_s = 15) { // Make sure we'll be getting data manually on this connection sendAT(GF("+CIPRXGET=1")); if (waitResponse() != 1) { return false; } + if (ssl) { + DBG("SSL not yet supported on this module!"); + } + // Establish a connection in multi-socket mode + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; sendAT(GF("+CIPOPEN="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port); // The reply is +CIPOPEN: ## of socket created - if (waitResponse(15000L, GF(GSM_NL "+CIPOPEN:")) != 1) { + if (waitResponse(timeout_ms, GF(GSM_NL "+CIPOPEN:")) != 1) { return false; } return true; } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+CIPSEND="), mux, ',', len); + sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len); if (waitResponse(GF(">")) != 1) { return 0; } @@ -674,23 +692,23 @@ protected: size_t modemRead(size_t size, uint8_t mux) { #ifdef TINY_GSM_USE_HEX - sendAT(GF("+CIPRXGET=3,"), mux, ',', size); + sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size); if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #else - sendAT(GF("+CIPRXGET=2,"), mux, ',', size); + sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size); if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #endif streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX streamSkipUntil(','); // Skip mux/cid (connecion id) - size_t len_requested = stream.readStringUntil(',').toInt(); + int len_requested = stream.readStringUntil(',').toInt(); // ^^ Requested number of data bytes (1-1460 bytes)to be read - size_t len_confirmed = stream.readStringUntil('\n').toInt(); + int len_confirmed = stream.readStringUntil('\n').toInt(); // ^^ The data length which not read in the buffer - for (size_t i=0; i_timeout)) { TINY_GSM_YIELD(); } diff --git a/src/TinyGsmClientSIM7000.h b/src/TinyGsmClientSIM7000.h index d6339af..ad13af1 100644 --- a/src/TinyGsmClientSIM7000.h +++ b/src/TinyGsmClientSIM7000.h @@ -166,16 +166,29 @@ public: bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } + sendAT(GF("E0")); // Echo Off if (waitResponse() != 1) { return false; } + DBG(GF("### Modem:"), getModemName()); - getSimStatus(); - return true; + + int 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); + } + // if the sim is ready, or it's locked but no pin has been provided, return + // true + else { + return (ret == SIM_READY || ret == SIM_LOCKED); + } } String getModemName() { @@ -800,24 +813,26 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() protected: - bool modemConnect(const char* host, uint16_t port, uint8_t mux, - bool ssl = false, int timeout_s = 75) - { - int rsp; - uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; - sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port); - rsp = waitResponse(timeout_ms, - GF("CONNECT OK" GSM_NL), - GF("CONNECT FAIL" GSM_NL), - GF("ALREADY CONNECT" GSM_NL), - GF("ERROR" GSM_NL), - GF("CLOSE OK" GSM_NL) // Happens when HTTPS handshake fails - ); - return (1 == rsp); + bool modemConnect(const char* host, uint16_t port, uint8_t mux, + bool ssl = false, int timeout_s = 75) { + if (ssl) { + DBG("SSL not yet supported on this module!"); + } + + int rsp; + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), + port); + rsp = waitResponse( + timeout_ms, GF("CONNECT OK" GSM_NL), GF("CONNECT FAIL" GSM_NL), + GF("ALREADY CONNECT" GSM_NL), GF("ERROR" GSM_NL), + GF("CLOSE OK" GSM_NL) // Happens when HTTPS handshake fails + ); + return (1 == rsp); } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+CIPSEND="), mux, ',', len); + sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len); if (waitResponse(GF(">")) != 1) { return 0; } @@ -832,25 +847,25 @@ protected: size_t modemRead(size_t size, uint8_t mux) { #ifdef TINY_GSM_USE_HEX - sendAT(GF("+CIPRXGET=3,"), mux, ',', size); + sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size); if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #else - sendAT(GF("+CIPRXGET=2,"), mux, ',', size); + sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size); if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #endif streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX streamSkipUntil(','); // Skip mux - size_t len_requested = stream.readStringUntil(',').toInt(); + int len_requested = stream.readStringUntil(',').toInt(); // ^^ Requested number of data bytes (1-1460 bytes)to be read - size_t len_confirmed = stream.readStringUntil('\n').toInt(); + int len_confirmed = stream.readStringUntil('\n').toInt(); // ^^ Confirmed number of data bytes to be read, which may be less than requested. // 0 indicates that no data can be read. // This is actually be the number of bytes that will be remaining after the read - for (size_t i=0; i_timeout)) { TINY_GSM_YIELD(); } diff --git a/src/TinyGsmClientSIM7600.h b/src/TinyGsmClientSIM7600.h index 94026ee..b9d767a 100644 --- a/src/TinyGsmClientSIM7600.h +++ b/src/TinyGsmClientSIM7600.h @@ -142,16 +142,29 @@ public: bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } + sendAT(GF("E0")); // Echo Off if (waitResponse() != 1) { return false; } + DBG(GF("### Modem:"), getModemName()); - getSimStatus(); - return true; + + int 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); + } + // if the sim is ready, or it's locked but no pin has been provided, return + // true + else { + return (ret == SIM_READY || ret == SIM_LOCKED); + } } String getModemName() { @@ -555,7 +568,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } // get GPS informations - bool getGPS(float *lat, float *lon, float *speed=0, int *alt=0, int *vsat=0, int *usat=0) { + bool getGPS(float *lat, float *lon, float *speed=0, int *alt=0) { //String buffer = ""; bool fix = false; @@ -567,14 +580,14 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() //stream.readStringUntil(','); // mode if ( stream.readStringUntil(',').toInt() == 1 ) fix = true; stream.readStringUntil(','); //gps - stream.readStringUntil(','); // glonass - stream.readStringUntil(','); // beidu + stream.readStringUntil(','); // glonass + stream.readStringUntil(','); // beidu *lat = stream.readStringUntil(',').toFloat(); //lat - stream.readStringUntil(','); // N/S + stream.readStringUntil(','); // N/S *lon = stream.readStringUntil(',').toFloat(); //lon - stream.readStringUntil(','); // E/W - stream.readStringUntil(','); // date - stream.readStringUntil(','); // UTC time + stream.readStringUntil(','); // E/W + stream.readStringUntil(','); // date + stream.readStringUntil(','); // UTC time if (alt != NULL) *alt = stream.readStringUntil(',').toFloat(); //alt if (speed != NULL) *speed = stream.readStringUntil(',').toFloat(); //speed stream.readStringUntil(','); //course @@ -582,8 +595,6 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() stream.readStringUntil(',');//PDOP stream.readStringUntil(',');//HDOP stream.readStringUntil(',');//VDOP - //if (vsat != NULL) *vsat = stream.readStringUntil(',').toInt(); //viewed satelites - //if (usat != NULL) *usat = stream.readStringUntil(',').toInt(); //used satelites stream.readStringUntil('\n'); waitResponse(); @@ -610,7 +621,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() // Wait for final OK waitResponse(); // Return millivolts - uint16_t res = voltage*1000; + uint16_t res = voltage*1000; return res; } @@ -618,16 +629,11 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - // get voltage in VOLTS - float voltage = stream.readStringUntil('\n').toFloat(); - milliVolts = voltage*1000; - // Wait for final OK - waitResponse(); + bool getBattStats(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) { + chargeState = 0; + percent = 0; + milliVolts = getBattVoltage(); return true; } @@ -650,25 +656,30 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() protected: - bool modemConnect(const char* host, uint16_t port, uint8_t mux, - bool ssl = false, int timeout_s = 75) { - // Make sure we'll be getting data manually on this connection - sendAT(GF("+CIPRXGET=1")); - if (waitResponse() != 1) { - return false; - } - - // Establish a connection in multi-socket mode - sendAT(GF("+CIPOPEN="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port); - // The reply is +CIPOPEN: ## of socket created - if (waitResponse(15000L, GF(GSM_NL "+CIPOPEN:")) != 1) { - return false; - } - return true; + bool modemConnect(const char* host, uint16_t port, uint8_t mux, + bool ssl = false, int timeout_s = 15) { + if (ssl) { + DBG("SSL not yet supported on this module!"); + } + // Make sure we'll be getting data manually on this connection + sendAT(GF("+CIPRXGET=1")); + if (waitResponse() != 1) { + return false; + } + + // Establish a connection in multi-socket mode + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + sendAT(GF("+CIPOPEN="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), + port); + // The reply is +CIPOPEN: ## of socket created + if (waitResponse(timeout_ms, GF(GSM_NL "+CIPOPEN:")) != 1) { + return false; + } + return true; } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+CIPSEND="), mux, ',', len); + sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len); if (waitResponse(GF(">")) != 1) { return 0; } @@ -685,23 +696,23 @@ protected: size_t modemRead(size_t size, uint8_t mux) { #ifdef TINY_GSM_USE_HEX - sendAT(GF("+CIPRXGET=3,"), mux, ',', size); + sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size); if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #else - sendAT(GF("+CIPRXGET=2,"), mux, ',', size); + sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size); if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #endif streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX streamSkipUntil(','); // Skip mux/cid (connecion id) - size_t len_requested = stream.readStringUntil(',').toInt(); + int len_requested = stream.readStringUntil(',').toInt(); // ^^ Requested number of data bytes (1-1460 bytes)to be read - size_t len_confirmed = stream.readStringUntil('\n').toInt(); + int len_confirmed = stream.readStringUntil('\n').toInt(); // ^^ The data length which not read in the buffer - for (size_t i=0; i_timeout)) { TINY_GSM_YIELD(); } diff --git a/src/TinyGsmClientSIM800.h b/src/TinyGsmClientSIM800.h index b5f2b6f..c505eca 100644 --- a/src/TinyGsmClientSIM800.h +++ b/src/TinyGsmClientSIM800.h @@ -165,6 +165,7 @@ public: bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } @@ -174,9 +175,19 @@ public: if (waitResponse() != 1) { return false; } + DBG(GF("### Modem:"), getModemName()); - getSimStatus(); - return true; + + int 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); + } + // if the sim is ready, or it's locked but no pin has been provided, return true + else { + return (ret == SIM_READY || ret == SIM_LOCKED); + } } String getModemName() { @@ -801,7 +812,7 @@ protected: } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+CIPSEND="), mux, ',', len); + sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len); if (waitResponse(GF(">")) != 1) { return 0; } @@ -816,25 +827,25 @@ protected: size_t modemRead(size_t size, uint8_t mux) { #ifdef TINY_GSM_USE_HEX - sendAT(GF("+CIPRXGET=3,"), mux, ',', size); + sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size); if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #else - sendAT(GF("+CIPRXGET=2,"), mux, ',', size); + sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size); if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #endif streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX streamSkipUntil(','); // Skip mux - size_t len_requested = stream.readStringUntil(',').toInt(); + int len_requested = stream.readStringUntil(',').toInt(); // ^^ Requested number of data bytes (1-1460 bytes)to be read - size_t len_confirmed = stream.readStringUntil('\n').toInt(); + int len_confirmed = stream.readStringUntil('\n').toInt(); // ^^ Confirmed number of data bytes to be read, which may be less than requested. // 0 indicates that no data can be read. // This is actually be the number of bytes that will be remaining after the read - for (size_t i=0; i_timeout)) { TINY_GSM_YIELD(); } diff --git a/src/TinyGsmClientSaraR4.h b/src/TinyGsmClientSaraR4.h index dec40b1..b37e75f 100644 --- a/src/TinyGsmClientSaraR4.h +++ b/src/TinyGsmClientSaraR4.h @@ -54,9 +54,7 @@ class GsmClient : public Client public: GsmClient() {} - GsmClient(TinyGsmSaraR4& modem, uint8_t mux = 0) { - init(&modem, mux); - } + GsmClient(TinyGsmSaraR4& modem, uint8_t mux = 0) { init(&modem, mux); } virtual ~GsmClient(){} @@ -95,8 +93,18 @@ TINY_GSM_CLIENT_CONNECT_OVERLOADS() virtual void stop(uint32_t maxWaitMs) { TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() - at->sendAT(GF("+USOCL="), mux); - at->waitResponse((maxWaitMs - (millis() - startMillis))); // NOTE: can take up to 120s to get a response + + // // synchronous close + // at->sendAT(GF("+USOCL="), mux); + // // NOTE: can take up to 120s to get a response + // at->waitResponse((maxWaitMs - (millis() - startMillis))); + // sock_connected = false; + + // faster asynchronous close + // NOT supported on SARA-R404M / SARA-R410M-01B + at->sendAT(GF("+USOCL="), mux, GF(",1")); + // NOTE: can take up to 120s to get a response + at->waitResponse((maxWaitMs - (millis() - startMillis))); sock_connected = false; } @@ -170,16 +178,16 @@ public: * Basic functions */ - bool begin(const char* pin = NULL) { - return init(pin); - } + bool begin(const char* pin = NULL) { return init(pin); } bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } - sendAT(GF("E0")); // Echo Off + + sendAT(GF("E0")); // Echo Off if (waitResponse() != 1) { return false; } @@ -191,7 +199,7 @@ public: #endif waitResponse(); - getModemName(); + DBG(GF("### Modem:"), getModemName()); int ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim @@ -199,7 +207,8 @@ public: simUnlock(pin); return (getSimStatus() == SIM_READY); } - // if the sim is ready, or it's locked but no pin has been provided, return true + // if the sim is ready, or it's locked but no pin has been provided,return + // return true else { return (ret == SIM_READY || ret == SIM_LOCKED); } @@ -224,7 +233,8 @@ public: String name = res1 + String(' ') + res2; DBG("### Modem:", name); - if (!name.startsWith("u-blox SARA-R4") && !name.startsWith("u-blox SARA-N4")) { + if (!name.startsWith("u-blox SARA-R4") && + !name.startsWith("u-blox SARA-N4")) { DBG("### WARNING: You are using the wrong TinyGSM modem!"); } @@ -308,13 +318,14 @@ TINY_GSM_MODEM_GET_SIMCCID_CCID() } SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - for (unsigned long start = millis(); millis() - start < timeout_ms; ) { + for (unsigned long start = millis(); millis() - start < timeout_ms;) { sendAT(GF("+CPIN?")); if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) { delay(1000); continue; } - int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED")); + int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), + GF("NOT INSERTED")); waitResponse(); switch (status) { case 2: @@ -347,10 +358,10 @@ TINY_GSM_MODEM_GET_CSQ() TINY_GSM_MODEM_WAIT_FOR_NETWORK() - bool setURAT( uint8_t urat ) { + bool setURAT(uint8_t urat) { // AT+URAT=[,[,<2ndPreferredAct>]] - sendAT(GF("+COPS=2")); // Deregister from network + sendAT(GF("+COPS=2")); // Deregister from network if (waitResponse() != 1) { return false; } @@ -358,7 +369,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() if (waitResponse() != 1) { return false; } - sendAT(GF("+COPS=0")); // Auto-register to the network + sendAT(GF("+COPS=0")); // Auto-register to the network if (waitResponse() != 1) { return false; } @@ -370,7 +381,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() */ bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { - gprsDisconnect(); + // gprsDisconnect(); sendAT(GF("+CGATT=1")); // attach to GPRS if (waitResponse(360000L) != 1) { @@ -382,8 +393,9 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() // serial interface. This is the only command set supported by the LTE-M // and LTE NB-IoT modules (SARA-R4xx, SARA-N4xx) + // Set the authentication if (user && strlen(user) > 0) { - sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"'); // Set the authentication + sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"'); waitResponse(); } @@ -399,9 +411,10 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } bool gprsDisconnect() { - sendAT(GF("+CGACT=1,0")); // Deactivate PDP context 1 + // sendAT(GF("+CGACT=0,1")); // Deactivate PDP context 1 + sendAT(GF("+CGACT=0")); // Deactivate all contexts if (waitResponse(40000L) != 1) { - return false; + // return false; } sendAT(GF("+CGATT=0")); // detach from GPRS @@ -509,6 +522,8 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { percent = getBattPercent(); + chargeState = 0; + milliVolts = 0; return true; } @@ -522,10 +537,9 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() if (waitResponse(GF(GSM_NL "+UTEMP:")) != 1) { return (float)-9999; } - streamSkipUntil(','); // Skip units (C/F) int16_t res = stream.readStringUntil('\n').toInt(); float temp = -9999; - if (res != 65535) { + if (res != -1) { temp = ((float)res)/10; } return temp; @@ -538,11 +552,12 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() protected: bool modemConnect(const char* host, uint16_t port, uint8_t* mux, - bool ssl = false, int timeout_s = 120) - { + bool ssl = false, int timeout_s = 120) { uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; - sendAT(GF("+USOCR=6")); // create a socket - if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) { // reply is +USOCR: ## of socket created + // create a socket + sendAT(GF("+USOCR=6")); + // reply is +USOCR: ## of socket created + if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) { return false; } *mux = stream.readStringUntil('\n').toInt(); @@ -558,17 +573,31 @@ protected: waitResponse(); // Enable KEEPALIVE, 30 sec - //sendAT(GF("+USOSO="), *mux, GF(",6,2,30000")); - //waitResponse(); + // sendAT(GF("+USOSO="), *mux, GF(",6,2,30000")); + // waitResponse(); // connect on the allocated socket - sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port); - int rsp = waitResponse(timeout_ms); - return (1 == rsp); + + // Use an asynchronous open to reduce the number of terminal freeze-ups + // This is still blocking until the URC arrives + // The SARA-R410M-02B with firmware revisions prior to L0.0.00.00.05.08 + // has a nasty habit of locking up when opening a socket, especially if + // the cellular service is poor. + // NOT supported on SARA-R404M / SARA-R410M-01B + sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port, ",1"); + waitResponse(timeout_ms, GF(GSM_NL "+UUSOCO: ")); + stream.readStringUntil(',').toInt(); // skip repeated mux + int connection_status = stream.readStringUntil('\n').toInt(); + return (0 == connection_status); + + // use synchronous open + // sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port, ",0"); + // int rsp = waitResponse(timeout_ms); + // return (1 == rsp); } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+USOWR="), mux, ',', len); + sendAT(GF("+USOWR="), mux, ',', (uint16_t)len); if (waitResponse(GF("@")) != 1) { return 0; } @@ -579,22 +608,22 @@ protected: if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) { return 0; } - streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip mux int sent = stream.readStringUntil('\n').toInt(); waitResponse(); // sends back OK after the confirmation of number sent return sent; } size_t modemRead(size_t size, uint8_t mux) { - sendAT(GF("+USORD="), mux, ',', size); + sendAT(GF("+USORD="), mux, ',', (uint16_t)size); if (waitResponse(GF(GSM_NL "+USORD:")) != 1) { return 0; } - streamSkipUntil(','); // Skip mux - size_t len = stream.readStringUntil(',').toInt(); + streamSkipUntil(','); // Skip mux + int len = stream.readStringUntil(',').toInt(); streamSkipUntil('\"'); - for (size_t i=0; i 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case + if (a <= 0) continue; // Skip 0x00 bytes, just in case data += (char)a; if (r1 && data.endsWith(r1)) { index = 1; @@ -724,30 +753,32 @@ finish: } data = ""; } - //data.replace(GSM_NL, "/"); - //DBG('<', index, '>', data); + // data.replace(GSM_NL, "/"); + // DBG('<', index, '>', data); return index; } uint8_t waitResponse(uint32_t timeout_ms, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), + GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { String data; return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); } - uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + uint8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), + GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { return waitResponse(1000, r1, r2, r3, r4, r5); } public: - Stream& stream; + Stream& stream; protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + GsmClient* sockets[TINY_GSM_MUX_COUNT]; }; #endif diff --git a/src/TinyGsmClientSequansMonarch.h b/src/TinyGsmClientSequansMonarch.h index 7810193..6b44fbc 100644 --- a/src/TinyGsmClientSequansMonarch.h +++ b/src/TinyGsmClientSequansMonarch.h @@ -195,15 +195,29 @@ public: bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } + sendAT(GF("E0")); // Echo Off if (waitResponse() != 1) { return false; } - getSimStatus(); - return true; + + DBG(GF("### Modem:"), getModemName()); + + int 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); + } + // if the sim is ready, or it's locked but no pin has been provided, return + // true + else { + return (ret == SIM_READY || ret == SIM_LOCKED); + } } String getModemName() { @@ -570,7 +584,7 @@ protected: return 0; } - sendAT(GF("+SQNSSENDEXT="), mux, ',', len); + sendAT(GF("+SQNSSENDEXT="), mux, ',', (uint16_t)len); waitResponse(10000L, GF(GSM_NL "> ")); stream.write((uint8_t*)buff, len); stream.flush(); @@ -605,13 +619,13 @@ protected: size_t modemRead(size_t size, uint8_t mux) { - sendAT(GF("+SQNSRECV="), mux, ',', size); + sendAT(GF("+SQNSRECV="), mux, ',', (uint16_t)size); if (waitResponse(GF("+SQNSRECV: ")) != 1) { return 0; } streamSkipUntil(','); // Skip mux - size_t len = stream.readStringUntil('\n').toInt(); - for (size_t i=0; i_timeout)) { TINY_GSM_YIELD(); } \ char c = stream.read(); \ diff --git a/src/TinyGsmClientUBLOX.h b/src/TinyGsmClientUBLOX.h index 3fa2f25..09702b1 100644 --- a/src/TinyGsmClientUBLOX.h +++ b/src/TinyGsmClientUBLOX.h @@ -176,9 +176,11 @@ public: bool init(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + if (!testAT()) { return false; } + sendAT(GF("E0")); // Echo Off if (waitResponse() != 1) { return false; @@ -535,7 +537,9 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { + chargeState = 0; percent = getBattPercent(); + milliVolts = 0; return true; } @@ -579,7 +583,7 @@ protected: } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+USOWR="), mux, ',', len); + sendAT(GF("+USOWR="), mux, ',', (uint16_t)len); if (waitResponse(GF("@")) != 1) { return 0; } @@ -597,15 +601,15 @@ protected: } size_t modemRead(size_t size, uint8_t mux) { - sendAT(GF("+USORD="), mux, ',', size); + sendAT(GF("+USORD="), mux, ',', (uint16_t)size); if (waitResponse(GF(GSM_NL "+USORD:")) != 1) { return 0; } streamSkipUntil(','); // Skip mux - size_t len = stream.readStringUntil(',').toInt(); + int len = stream.readStringUntil(',').toInt(); streamSkipUntil('\"'); - for (size_t i=0; istreamClear(); // Empty anything in the buffer before starting - sock_connected = at->modemConnect(ip, port, mux, false, timeout_s); + sock_connected = at->modemConnect(ip, port, mux, false); return sock_connected; } virtual int connect(IPAddress ip, uint16_t port) { - return connect(ip, port, 75); + return connect(ip, port, 0); } virtual void stop(uint32_t maxWaitMs) { at->streamClear(); // Empty anything in the buffer - at->commandMode(); - // For WiFi models, there's no direct way to close the socket. This is a - // hack to shut the socket by setting the timeout to zero. - if (at->beeType == XBEE_S6B_WIFI) { - at->sendAT(GF("TM0")); // Set socket timeout (using Digi default of 10 seconds) - at->waitResponse(maxWaitMs); // This response can be slow - at->writeChanges(); - } - // For cellular models, per documentation: If you change the TM (socket - // timeout) value while in Transparent Mode, the current connection is - // immediately closed. - at->sendAT(GF("TM64")); // Set socket timeout (using Digi default of 10 seconds) - at->waitResponse(maxWaitMs); // This response can be slow - at->writeChanges(); - at->exitCommand(); - at->streamClear(); // Empty anything remaining in the buffer + // empty the saved currently-in-use destination address + at->modemStop(maxWaitMs); + at->streamClear(); // Empty anything in the buffer sock_connected = false; + // Note: because settings are saved in flash, the XBEE will attempt to // reconnect to the previous socket if it receives any outgoing data. // Setting sock_connected to false after the stop ensures that connected() @@ -214,6 +205,10 @@ public: return true; } return sock_connected; + // NOTE: We dont't check or return + // modemGetConnected() because we don't + // want to go into command mode. + // return at->modemGetConnected(); } virtual operator bool() { return connected(); } @@ -251,9 +246,12 @@ public: } virtual int connect(IPAddress ip, uint16_t port, int timeout_s) { + if (timeout_s != 0) { + DBG("Timeout [", timeout_s, "] doesn't apply here."); + } // NOTE: Not caling stop() or yeild() here at->streamClear(); // Empty anything in the buffer before starting - sock_connected = at->modemConnect(ip, port, mux, true, timeout_s); + sock_connected = at->modemConnect(ip, port, mux, true); return sock_connected; } }; @@ -305,6 +303,10 @@ public: digitalWrite(resetPin, HIGH); } + if (pin && strlen(pin) > 0) { + DBG("XBee's do not support SIMs that require an unlock pin!"); + } + XBEE_COMMAND_START_DECORATOR(10, false) sendAT(GF("AP0")); // Put in transparent mode @@ -511,8 +513,15 @@ public: XBEE_COMMAND_END_DECORATOR } - bool poweroff() { // Not supported - return false; + bool poweroff() { // NOTE: Not supported for WiFi or older cellular firmware + XBEE_COMMAND_START_DECORATOR(5, false) + sendAT(GF("SD")); + bool ret_val = waitResponse(120000L) == 1; + if (ret_val) { + ret_val &= (sendATGetString(GF("AI")) == "2D"); + } + XBEE_COMMAND_END_DECORATOR + return ret_val; } bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED; @@ -524,6 +533,9 @@ public: */ bool simUnlock(const char *pin) { // Not supported + if (pin && strlen(pin) > 0) { + DBG("XBee's do not support SIMs that require an unlock pin!"); + } return false; } @@ -535,7 +547,7 @@ public: return sendATGetString(GF("IM")); } - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { + SimStatus getSimStatus() { return SIM_READY; // unsupported } @@ -680,7 +692,7 @@ public: //nh For no pwd don't set setscurity or pwd if (ssid == NULL) retVal = false;; - if (pwd != NULL) + if (pwd && strlen(pwd) > 0) { sendAT(GF("EE"), 2); // Set security to WPA2 if (waitResponse() != 1) retVal = false; @@ -734,10 +746,19 @@ public: * GPRS functions */ - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + bool gprsConnect(const char* apn, const char* user = NULL, + const char* pwd = NULL) { + if (user && strlen(user) > 0) { + DBG("XBee's do not support SIMs that a user name/password!"); + } + if (pwd && strlen(pwd) > 0) { + DBG("XBee's do not support SIMs that a user name/password!"); + } XBEE_COMMAND_START_DECORATOR(5, false) sendAT(GF("AN"), apn); // Set the APN bool success = waitResponse() == 1; + sendAT(GF("AM0")); // Airplane mode off + waitResponse(5000); writeChanges(); XBEE_COMMAND_END_DECORATOR return success; @@ -748,9 +769,9 @@ public: sendAT(GF("AM1")); // Cheating and disconnecting by turning on airplane mode int8_t res = (1 == waitResponse(5000)); writeChanges(); - sendAT(GF("AM0")); // Airplane mode off - waitResponse(5000); - writeChanges(); + // sendAT(GF("AM0")); // Airplane mode off + // waitResponse(5000); + // writeChanges(); XBEE_COMMAND_END_DECORATOR return res; } @@ -796,12 +817,30 @@ public: */ // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; + uint16_t getBattVoltage() { + int16_t intRes = 0; + XBEE_COMMAND_START_DECORATOR(5, false) + if (beeType == XBEE_UNKNOWN) getSeries(); + if (beeType == XBEE_S6B_WIFI) { + sendAT(GF("%V")); + intRes = readResponseInt(); + } + XBEE_COMMAND_END_DECORATOR + return intRes; + } + int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE; uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE; + + bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { + chargeState = 0; + percent = 0; + milliVolts = getBattVoltage(); + return true; + } float getTemperature() { + XBEE_COMMAND_START_DECORATOR(5, (float)-9999) String res = sendATGetString(GF("TP")); if (res == "") { return (float)-9999; @@ -809,6 +848,7 @@ public: char buf[5] = {0,}; res.toCharArray(buf, 5); int8_t intRes = (int8_t)strtol(buf, 0, 16); // degrees Celsius displayed in 8-bit two's complement format. + XBEE_COMMAND_END_DECORATOR return (float)intRes; } @@ -816,10 +856,35 @@ public: * Client related functions */ -protected: + protected: - IPAddress getHostIP(const char* host, int timeout_s = 45) { - String strIP; strIP.reserve(16); + int16_t getConnectionIndicator() { + XBEE_COMMAND_START_DECORATOR(5, false) + sendAT(GF("CI")); + int16_t intRes = readResponseInt(); + XBEE_COMMAND_END_DECORATOR + return intRes; + } + + IPAddress getOperatingIP() { + String strIP; + strIP.reserve(16); + + XBEE_COMMAND_START_DECORATOR(5, IPAddress(0, 0, 0, 0)) + sendAT(GF("OD")); + strIP = stream.readStringUntil('\r'); // read result + strIP.trim(); + XBEE_COMMAND_END_DECORATOR + + if (strIP != "" && strIP != GF("ERROR")) { + return TinyGsmIpFromString(strIP); + } else + return IPAddress(0, 0, 0, 0); + } + + IPAddress lookupHostIP(const char* host, int timeout_s = 45) { + String strIP; + strIP.reserve(16); unsigned long startMillis = millis(); uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; bool gotIP = false; @@ -850,10 +915,8 @@ protected: bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false, int timeout_s = 75) { - unsigned long startMillis = millis(); - uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; bool retVal = false; - XBEE_COMMAND_START_DECORATOR(5, false) + XBEE_COMMAND_START_DECORATOR(5, false) // If this is a new host name, replace the saved host and wipe out the saved host IP if (this->savedHost != String(host)) { @@ -863,12 +926,12 @@ protected: // If we don't have a good IP for the host, we need to do a DNS search if (savedHostIP == IPAddress(0,0,0,0)) { - savedHostIP = getHostIP(host, timeout_s); // This will return 0.0.0.0 if lookup fails + savedHostIP = lookupHostIP(host, timeout_s); // This will return 0.0.0.0 if lookup fails } // If we now have a valid IP address, use it to connect if (savedHostIP != IPAddress(0,0,0,0)) { // Only re-set connection information if we have an IP address - retVal = modemConnect(savedHostIP, port, mux, ssl, timeout_ms - (millis() - startMillis)); + retVal = modemConnect(savedHostIP, port, mux, ssl); } XBEE_COMMAND_END_DECORATOR @@ -876,13 +939,21 @@ protected: return retVal; } - bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false, int timeout_s = 75) { - + bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, + bool ssl = false) { bool success = true; - uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; + + if (mux != 0) { + DBG("XBee only supports 1 IP channel in transparent mode!"); + } + + // empty the saved currelty-in-use destination address + savedOperatingIP = IPAddress(0, 0, 0, 0); + XBEE_COMMAND_START_DECORATOR(5, false) - if (ip != savedIP) { // Can skip almost everything if there's no change in the IP address + if (ip != savedIP) { // Can skip almost everything if there's no + // change in the IP address savedIP = ip; // Set the newly requested IP address String host; host.reserve(16); host += ip[0]; @@ -909,11 +980,14 @@ protected: success &= writeChanges(); } - for (unsigned long start = millis(); millis() - start < timeout_ms; ) { - if (modemGetConnected()) { - sockets[mux]->sock_connected = true; - break; - } + // we'll accept either unknown or connected + if (beeType != XBEE_S6B_WIFI) { + uint16_t ci = getConnectionIndicator(); + success &= (ci == 0x00 || ci == 0xFF || ci == 0x28); + } + + if (success) { + sockets[mux]->sock_connected = true; } XBEE_COMMAND_END_DECORATOR @@ -921,9 +995,55 @@ protected: return success; } + bool modemStop(uint32_t maxWaitMs) { + streamClear(); // Empty anything in the buffer + // empty the saved currently-in-use destination address + savedOperatingIP = IPAddress(0, 0, 0, 0); + + XBEE_COMMAND_START_DECORATOR(5, false) + + // Get the current socket timeout + sendAT(GF("TM")); + String timeoutUsed = readResponseString(5000L); + + // For WiFi models, there's no direct way to close the socket. This is a + // hack to shut the socket by setting the timeout to zero. + if (beeType == XBEE_S6B_WIFI) { + sendAT(GF("TM0")); // Set socket timeout to 0 + waitResponse(maxWaitMs); // This response can be slow + writeChanges(); + } + + // For cellular models, per documentation: If you write the TM (socket + // timeout) value while in Transparent Mode, the current connection is + // immediately closed - this works even if the TM values is unchanged + sendAT(GF("TM"), timeoutUsed); // Re-set socket timeout + waitResponse(maxWaitMs); // This response can be slow + writeChanges(); + + XBEE_COMMAND_END_DECORATOR + return true; + } + int16_t modemSend(const void* buff, size_t len, uint8_t mux = 0) { + if (mux != 0) { + DBG("XBee only supports 1 IP channel in transparent mode!"); + } stream.write((uint8_t*)buff, len); stream.flush(); + + if (beeType != XBEE_S6B_WIFI) { + // After a send, verify the outgoing ip if it isn't set + if (savedOperatingIP == IPAddress(0, 0, 0, 0)) { + modemGetConnected(); + } + // After sending several characters, also re-check + // NOTE: I'm intentionally not checking after every single character! + else if (len > 5) { + modemGetConnected(); + } + } + return len; } @@ -937,17 +1057,11 @@ protected: XBEE_COMMAND_START_DECORATOR(5, false) - // Verify that we're connected to the *right* IP address - // We might be connected - but to the wrong thing - // NOTE: In transparent mode, there is only one connection possible - no multiplex - // String strIP; strIP.reserve(16); - // sendAT(GF("DL")); - // strIP = stream.readStringUntil('\r'); // read result - // if (TinyGsmIpFromString(strIP) != savedIP) return exitAndFail(); - if (beeType == XBEE_UNKNOWN) getSeries(); // Need to know the bee type to interpret response - switch (beeType){ // The wifi be can only say if it's connected to the netowrk + switch (beeType){ + + // The wifi be can only say if it's connected to the netowrk case XBEE_S6B_WIFI: { RegStatus s = getRegistrationStatus(); XBEE_COMMAND_END_DECORATOR @@ -956,22 +1070,110 @@ protected: } return (s == REG_OK); // if it's connected, we hope the sockets are too } - default: { // Cellular XBee's - sendAT(GF("CI")); - int16_t intRes = readResponseInt(); + + // Cellular XBee's + default: { + int16_t ci = getConnectionIndicator(); + // Get the operating destination address + IPAddress od = getOperatingIP(); XBEE_COMMAND_END_DECORATOR - switch(intRes) { - case 0x00: // 0x00 = The socket is definitely open - case 0x28: // 0x28 = "Unknown." - case 0xFF: // 0xFF = No known status - this is always returned prior to sending data + + switch(ci) { + + // 0x00 = The socket is definitely open + case 0x00: { + savedOperatingIP = od; + // but it's possible the socket is set to the wrong place + if (od != IPAddress(0, 0, 0, 0) && od != savedIP) { + sockets[0]->stop(); + return false; + } return true; - case 0x02: // 0x02 = Invalid parameters (bad IP/host) - case 0x12: // 0x12 = DNS query lookup failure - case 0x25: // 0x25 = Unknown server - DNS lookup failed (0x22 for UDP socket!) - savedIP = IPAddress(0,0,0,0); // force a lookup next time! - default: // If it's anything else (inc 0x02, 0x12, and 0x25)... - sockets[0]->sock_connected = false; // ...it's definitely NOT connected + } + + // 0x28 = "Unknown." + // 0xFF = No known status - always returned prior to sending data + case 0x28: + case 0xFF: { + // If we previously had an operating destination and we no longer do, + // the socket must have closed + if (od == IPAddress(0, 0, 0, 0) && savedOperatingIP != IPAddress(0, 0, 0, 0)) { + savedOperatingIP = od; + sockets[0]->sock_connected = false; + return false; + } + // else if the operating destination exists, but is wrong + // we need to close and re-open + else if (od != IPAddress(0, 0, 0, 0) && od != savedIP) { + sockets[0]->stop(); + return false; + } + // else if the operating destination exists and matches, we're + // good to go + else if (od != IPAddress(0, 0, 0, 0) && od == savedIP) { + savedOperatingIP = od; + return true; + } + // If we never had an operating destination, then sock may be open + // but data never sent - this is the dreaded "we don't know" + else { + savedOperatingIP = od; + return true; + } + + // // Ask for information about any open sockets + // sendAT(GF("SI")); + // String open_socks = stream.readStringUntil('\r'); + // open_socks.replace(GSM_NL, ""); + // open_socks.trim(); + // if (open_socks != "") { + // // In transparent mode, only socket 0 should be possible + // sendAT(GF("SI0")); + // // read socket it + // String sock_id = stream.readStringUntil('\r'); + // // read socket state + // String sock_state = stream.readStringUntil('\r'); + // // read socket protocol (TCP/UDP) + // String sock_protocol = stream.readStringUntil('\r'); + // // read local port number + // String local_port = stream.readStringUntil('\r'); + // // read remote port number + // String remote_port = stream.readStringUntil('\r'); + // // read remote ip address + // String remoted_address = + // stream.readStringUntil('\r'); // read result + // stream.readStringUntil('\r'); // final carriage return + // } + } + + // 0x21 = User closed + // 0x27 = Connection lost + // If the connection is lost or timed out on our side, + // we force close so it can reopen + case 0x21 : + case 0x27 : { + sendAT(GF("TM")); // Get socket timeout + String timeoutUsed = readResponseString(5000L); + sendAT(GF("TM"), timeoutUsed); // Re-set socket timeout + waitResponse(5000L); // This response can be slow + } + + // 0x02 = Invalid parameters (bad IP/host) + // 0x12 = DNS query lookup failure + // 0x25 = Unknown server - DNS lookup failed (0x22 for UDP socket!) + case 0x02: + case 0x12: + case 0x25: { + savedIP = IPAddress(0, 0, 0, 0); // force a lookup next time! + } + + // If it's anything else (inc 0x02, 0x12, and 0x25)... + // it's definitely NOT connected + default: { + sockets[0]->sock_connected = false; + savedOperatingIP = od; return false; + } } } } @@ -1175,6 +1377,7 @@ protected: IPAddress savedIP; String savedHost; IPAddress savedHostIP; + IPAddress savedOperatingIP; bool inCommandMode; uint32_t lastCommandModeMillis; GsmClient* sockets[TINY_GSM_MUX_COUNT]; diff --git a/src/TinyGsmCommon.h b/src/TinyGsmCommon.h index 658bc68..bb2e541 100644 --- a/src/TinyGsmCommon.h +++ b/src/TinyGsmCommon.h @@ -10,7 +10,7 @@ #define TinyGsmCommon_h // The current library version number -#define TINYGSM_VERSION "0.9.7" +#define TINYGSM_VERSION "0.9.17" #if defined(SPARK) || defined(PARTICLE) #include "Particle.h" @@ -490,9 +490,12 @@ String TinyGsmDecodeHex16bit(String &instr) { // Unlocks a sim via the 3GPP TS command AT+CPIN #define TINY_GSM_MODEM_SIM_UNLOCK_CPIN() \ - bool simUnlock(const char *pin) { \ - sendAT(GF("+CPIN=\""), pin, GF("\"")); \ - return waitResponse() == 1; \ + bool simUnlock(const char *pin) { \ + if (pin && strlen(pin) > 0) { \ + sendAT(GF("+CPIN=\""), pin, GF("\"")); \ + return waitResponse() == 1; \ + } \ + return true; \ } diff --git a/tools/Diagnostics/Diagnostics.ino b/tools/Diagnostics/Diagnostics.ino index b66e1b4..962043c 100644 --- a/tools/Diagnostics/Diagnostics.ino +++ b/tools/Diagnostics/Diagnostics.ino @@ -69,11 +69,12 @@ // set GSM PIN, if any #define GSM_PIN "" -// Your GPRS credentials -// Leave empty, if missing user or pass +// Your GPRS credentials, if any const char apn[] = "YourAPN"; const char gprsUser[] = ""; const char gprsPass[] = ""; + +// Your WiFi connection credentials, if applicable const char wifiSSID[] = "YourSSID"; const char wifiPass[] = "YourWiFiPass"; @@ -83,6 +84,20 @@ const char resource[] = "/TinyGSM/logo.txt"; #include +// Just in case someone defined the wrong thing.. +#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS false +#define TINY_GSM_USE_WIFI true +#endif +#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI +#undef TINY_GSM_USE_GPRS +#undef TINY_GSM_USE_WIFI +#define TINY_GSM_USE_GPRS true +#define TINY_GSM_USE_WIFI false +#endif + #ifdef DUMP_AT_COMMANDS #include StreamDebugger debugger(SerialAT, SerialMon); @@ -111,8 +126,8 @@ void setup() { SerialMon.println("Wait..."); // Set GSM module baud rate - TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); - // SerialAT.begin(115200); + // TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); + SerialAT.begin(115200); delay(3000); } @@ -147,6 +162,7 @@ void loop() { #endif #if TINY_GSM_USE_WIFI + // Wifi connection parameters must be set before waiting for the network SerialMon.print(F("Setting SSID/password...")); if (!modem.networkConnect(wifiSSID, wifiPass)) { SerialMon.println(" fail"); @@ -175,7 +191,8 @@ void loop() { } SerialMon.println(F(" [OK]")); -#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_HAS_GPRS +#if TINY_GSM_USE_GPRS + // GPRS connection parameters are usually set after network registration SerialMon.print("Connecting to "); SerialMon.print(apn); if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { @@ -233,8 +250,14 @@ void loop() { client.stop(); SerialMon.println(F("Server disconnected")); +#if TINY_GSM_USE_WIFI + modem.networkDisconnect(); + SerialMon.println(F("WiFi disconnected")); +#endif +#if TINY_GSM_USE_GPRS modem.gprsDisconnect(); SerialMon.println(F("GPRS disconnected")); +#endif SerialMon.println(); SerialMon.println(F("************************"));