From d3d1083d8f2f12864eec71f1fa36516161c4dd8e Mon Sep 17 00:00:00 2001 From: Sara Damiano Date: Thu, 6 Feb 2020 15:20:44 -0500 Subject: [PATCH] CRTP!!!! Totally untested Signed-off-by: Sara Damiano --- .clang-format | 110 ++ .gitignore | 1 + cpplint.cfg | 3 + examples/AllFunctions/AllFunctions.ino | 6 +- examples/BlynkClient/BlynkClient.ino | 2 +- examples/FileDownload/FileDownload.ino | 12 +- examples/HttpClient/HttpClient.ino | 6 +- examples/HttpsClient/HttpsClient.ino | 6 +- examples/MqttClient/MqttClient.ino | 8 +- examples/WebClient/WebClient.ino | 8 +- examples/more/Hologram_Dash/Hologram_Dash.ino | 8 +- examples/more/Industruino/Industruino.ino | 6 +- .../SIM800_SslSetCert/SIM800_SslSetCert.ino | 2 +- src/TinyGsmClient.h | 175 +- src/TinyGsmClientA6.h | 588 +++--- src/TinyGsmClientBG96.h | 653 +++---- src/TinyGsmClientESP8266.h | 489 +++-- src/TinyGsmClientM590.h | 515 ++--- src/TinyGsmClientM95.h | 679 +++---- src/TinyGsmClientMC60.h | 703 +++---- src/TinyGsmClientSIM5360.h | 671 +++---- src/TinyGsmClientSIM7000.h | 835 +++----- src/TinyGsmClientSIM7600.h | 689 +++---- src/TinyGsmClientSIM800.h | 850 +++----- src/TinyGsmClientSIM808.h | 95 +- src/TinyGsmClientSaraR4.h | 689 +++---- src/TinyGsmClientSequansMonarch.h | 665 +++---- src/TinyGsmClientUBLOX.h | 603 +++--- src/TinyGsmClientXBee.h | 1075 ++++++----- src/TinyGsmCommon.h | 1708 +++++++++++------ tools/test_build/test_build.ino | 27 +- 31 files changed, 5226 insertions(+), 6661 deletions(-) create mode 100644 .clang-format create mode 100644 cpplint.cfg diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..110d35d --- /dev/null +++ b/.clang-format @@ -0,0 +1,110 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlines: Left +AlignOperands: false +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: "^ IWYU pragma:" +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +# ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 60 +PenaltyReturnTypeOnItsOwnLine: 5 +PointerAlignment: Left +PointerBindsToType: true +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 2 +UseTab: Never +--- diff --git a/.gitignore b/.gitignore index 051117d..b5e30af 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ .clang_complete .gcc-flags.json platformio.ini +extra_envs.ini lib/readme.txt include/readme.txt .atomrc.cson diff --git a/cpplint.cfg b/cpplint.cfg new file mode 100644 index 0000000..6485487 --- /dev/null +++ b/cpplint.cfg @@ -0,0 +1,3 @@ +# Allow references to be used to change values +filter=-runtime/references +filter=-build/namespaces diff --git a/examples/AllFunctions/AllFunctions.ino b/examples/AllFunctions/AllFunctions.ino index 7164086..7313a6d 100644 --- a/examples/AllFunctions/AllFunctions.ino +++ b/examples/AllFunctions/AllFunctions.ino @@ -42,7 +42,7 @@ //SoftwareSerial SerialAT(2, 3); // RX, TX // See all AT commands, if wanted -//#define DUMP_AT_COMMANDS +// #define DUMP_AT_COMMANDS // Define the serial console for debug prints, if needed #define TINY_GSM_DEBUG SerialMon @@ -68,8 +68,8 @@ #define GSM_PIN "" // Set phone numbers, if you want to test SMS and Calls -//#define SMS_TARGET "+380xxxxxxxxx" -//#define CALL_TARGET "+380xxxxxxxxx" +// #define SMS_TARGET "+380xxxxxxxxx" +// #define CALL_TARGET "+380xxxxxxxxx" // Your GPRS credentials, if any const char apn[] = "YourAPN"; diff --git a/examples/BlynkClient/BlynkClient.ino b/examples/BlynkClient/BlynkClient.ino index 0ae92ee..20f98c0 100644 --- a/examples/BlynkClient/BlynkClient.ino +++ b/examples/BlynkClient/BlynkClient.ino @@ -26,7 +26,7 @@ // Default heartbeat interval for GSM is 60 // If you want override this value, uncomment and set this option: -//#define BLYNK_HEARTBEAT 30 +// #define BLYNK_HEARTBEAT 30 // Select your modem: #define TINY_GSM_MODEM_SIM800 diff --git a/examples/FileDownload/FileDownload.ino b/examples/FileDownload/FileDownload.ino index cfab6a7..f20e187 100644 --- a/examples/FileDownload/FileDownload.ino +++ b/examples/FileDownload/FileDownload.ino @@ -52,14 +52,14 @@ #define TINY_GSM_RX_BUFFER 1024 // See all AT commands, if wanted -//#define DUMP_AT_COMMANDS +// #define DUMP_AT_COMMANDS // Define the serial console for debug prints, if needed #define TINY_GSM_DEBUG SerialMon -//#define LOGGING // <- Logging is for the HTTP library +// #define LOGGING // <- Logging is for the HTTP library // Add a reception delay - may be needed for a fast processor at a slow baud rate -//#define TINY_GSM_YIELD() { delay(2); } +// #define TINY_GSM_YIELD() { delay(2); } // Define how you're planning to connect to the internet #define TINY_GSM_USE_GPRS true @@ -217,7 +217,7 @@ void loop() { client.print("Connection: close\r\n\r\n"); // Let's see what the entire elapsed time is, from after we send the request. - unsigned long timeElapsed = millis(); + uint32_t timeElapsed = millis(); SerialMon.println(F("Waiting for response header")); @@ -246,7 +246,7 @@ void loop() { // SerialMon.print(c, HEX); // SerialMon.print(' '); // if (isprint(c)) - // SerialMon.print((char) c); + // SerialMon.print(reinterpret_cast c); // else // SerialMon.print('*'); // SerialMon.print(' '); @@ -301,7 +301,7 @@ void loop() { while (readLength < contentLength && client.connected() && millis() - clientReadStartTime < clientReadTimeout) { while (client.available()) { uint8_t c = client.read(); - //SerialMon.print((char)c); // Uncomment this to show data + //SerialMon.print(reinterpret_castc); // Uncomment this to show data crc.update(c); readLength++; if (readLength % (contentLength / 13) == 0) { diff --git a/examples/HttpClient/HttpClient.ino b/examples/HttpClient/HttpClient.ino index d77f1de..1bff394 100644 --- a/examples/HttpClient/HttpClient.ino +++ b/examples/HttpClient/HttpClient.ino @@ -57,18 +57,18 @@ #define TINY_GSM_RX_BUFFER 650 // See all AT commands, if wanted -//#define DUMP_AT_COMMANDS +// #define DUMP_AT_COMMANDS // Define the serial console for debug prints, if needed #define TINY_GSM_DEBUG SerialMon -//#define LOGGING // <- Logging is for the HTTP library +// #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 TINY_GSM_YIELD() { delay(2); } // Define how you're planning to connect to the internet #define TINY_GSM_USE_GPRS true diff --git a/examples/HttpsClient/HttpsClient.ino b/examples/HttpsClient/HttpsClient.ino index 6330ea9..fbbd02a 100644 --- a/examples/HttpsClient/HttpsClient.ino +++ b/examples/HttpsClient/HttpsClient.ino @@ -49,18 +49,18 @@ #define TINY_GSM_RX_BUFFER 650 // See all AT commands, if wanted -//#define DUMP_AT_COMMANDS +// #define DUMP_AT_COMMANDS // Define the serial console for debug prints, if needed #define TINY_GSM_DEBUG SerialMon -//#define LOGGING // <- Logging is for the HTTP library +// #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 TINY_GSM_YIELD() { delay(2); } // Define how you're planning to connect to the internet #define TINY_GSM_USE_GPRS true diff --git a/examples/MqttClient/MqttClient.ino b/examples/MqttClient/MqttClient.ino index 1e269d8..aa0876c 100644 --- a/examples/MqttClient/MqttClient.ino +++ b/examples/MqttClient/MqttClient.ino @@ -60,7 +60,7 @@ //SoftwareSerial SerialAT(2, 3); // RX, TX // See all AT commands, if wanted -//#define DUMP_AT_COMMANDS +// #define DUMP_AT_COMMANDS // Define the serial console for debug prints, if needed #define TINY_GSM_DEBUG SerialMon @@ -70,7 +70,7 @@ #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 TINY_GSM_YIELD() { delay(2); } // Define how you're planning to connect to the internet #define TINY_GSM_USE_GPRS true @@ -125,7 +125,7 @@ PubSubClient mqtt(client); #define LED_PIN 13 int ledStatus = LOW; -long lastReconnectAttempt = 0; +uint32_t lastReconnectAttempt = 0; void mqttCallback(char* topic, byte* payload, unsigned int len) { SerialMon.print("Message arrived ["); @@ -252,7 +252,7 @@ void loop() { if (!mqtt.connected()) { SerialMon.println("=== MQTT NOT CONNECTED ==="); // Reconnect every 10 seconds - unsigned long t = millis(); + uint32_t t = millis(); if (t - lastReconnectAttempt > 10000L) { lastReconnectAttempt = t; if (mqttConnect()) { diff --git a/examples/WebClient/WebClient.ino b/examples/WebClient/WebClient.ino index 8207c06..1e80494 100644 --- a/examples/WebClient/WebClient.ino +++ b/examples/WebClient/WebClient.ino @@ -47,7 +47,7 @@ #define TINY_GSM_RX_BUFFER 650 // See all AT commands, if wanted -//#define DUMP_AT_COMMANDS +// #define DUMP_AT_COMMANDS // Define the serial console for debug prints, if needed #define TINY_GSM_DEBUG SerialMon @@ -57,10 +57,10 @@ #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 TINY_GSM_YIELD() { delay(2); } // Uncomment this if you want to use SSL -//#define USE_SSL +// #define USE_SSL // Define how you're planning to connect to the internet #define TINY_GSM_USE_GPRS true @@ -210,7 +210,7 @@ void loop() { client.print("Connection: close\r\n\r\n"); client.println(); - unsigned long timeout = millis(); + uint32_t timeout = millis(); while (client.connected() && millis() - timeout < 10000L) { // Print available data while (client.available()) { diff --git a/examples/more/Hologram_Dash/Hologram_Dash.ino b/examples/more/Hologram_Dash/Hologram_Dash.ino index 331fbd1..ae4543a 100644 --- a/examples/more/Hologram_Dash/Hologram_Dash.ino +++ b/examples/more/Hologram_Dash/Hologram_Dash.ino @@ -12,15 +12,15 @@ #define TINY_GSM_MODEM_UBLOX // Increase RX buffer if needed -//#define TINY_GSM_RX_BUFFER 512 +// #define TINY_GSM_RX_BUFFER 512 #include // Uncomment this if you want to see all AT commands -//#define DUMP_AT_COMMANDS +// #define DUMP_AT_COMMANDS // Uncomment this if you want to use SSL -//#define USE_SSL +// #define USE_SSL // Set serial for debug console (to the Serial Monitor, speed 115200) #define SerialMon Serial @@ -108,7 +108,7 @@ void loop() { client.print(String("Host: ") + server + "\r\n"); client.print("Connection: close\r\n\r\n"); - unsigned long timeout = millis(); + uint32_t timeout = millis(); while (client.connected() && millis() - timeout < 10000L) { // Print available data while (client.available()) { diff --git a/examples/more/Industruino/Industruino.ino b/examples/more/Industruino/Industruino.ino index b51acc6..6b8b48e 100644 --- a/examples/more/Industruino/Industruino.ino +++ b/examples/more/Industruino/Industruino.ino @@ -18,16 +18,16 @@ #define TINY_GSM_MODEM_SIM800 // Increase RX buffer if needed -//#define TINY_GSM_RX_BUFFER 512 +// #define TINY_GSM_RX_BUFFER 512 #include #include // Uncomment this if you want to see all AT commands -//#define DUMP_AT_COMMANDS +// #define DUMP_AT_COMMANDS // Uncomment this if you want to use SSL -//#define USE_SSL +// #define USE_SSL // Set serial for debug console (to the Serial Monitor, speed 115200) #define SerialMon SerialUSB diff --git a/examples/more/SIM800_SslSetCert/SIM800_SslSetCert.ino b/examples/more/SIM800_SslSetCert/SIM800_SslSetCert.ino index e2a679d..8aadb3c 100644 --- a/examples/more/SIM800_SslSetCert/SIM800_SslSetCert.ino +++ b/examples/more/SIM800_SslSetCert/SIM800_SslSetCert.ino @@ -28,7 +28,7 @@ #define SerialAT Serial1 // Uncomment this if you want to see all AT commands -//#define DUMP_AT_COMMANDS +// #define DUMP_AT_COMMANDS #ifdef DUMP_AT_COMMANDS diff --git a/src/TinyGsmClient.h b/src/TinyGsmClient.h index 0b25d28..fd23b11 100644 --- a/src/TinyGsmClient.h +++ b/src/TinyGsmClient.h @@ -6,129 +6,128 @@ * @date Nov 2016 */ -#ifndef TinyGsmClient_h -#define TinyGsmClient_h +#ifndef SRC_TINYGSMCLIENT_h +#define SRC_TINYGSMCLIENT_h #if defined(TINY_GSM_MODEM_SIM800) - #define TINY_GSM_MODEM_HAS_GPRS - #define TINY_GSM_MODEM_HAS_SSL - #include - typedef TinyGsmSim800 TinyGsm; - typedef TinyGsmSim800::GsmClient TinyGsmClient; - typedef TinyGsmSim800::GsmClientSecure TinyGsmClientSecure; +#define TINY_GSM_MODEM_HAS_GPRS +#define TINY_GSM_MODEM_HAS_SSL +#include "TinyGsmClientSIM800.h" +typedef TinyGsmSim800 TinyGsm; +typedef TinyGsmSim800::GsmClientSim800 TinyGsmClient; +typedef TinyGsmSim800::GsmClientSecureSim800 TinyGsmClientSecure; #elif defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868) - #define TINY_GSM_MODEM_HAS_GPRS - #define TINY_GSM_MODEM_HAS_SSL - #define TINY_GSM_MODEM_HAS_GPS - #include - typedef TinyGsmSim808 TinyGsm; - typedef TinyGsmSim808::GsmClient TinyGsmClient; - typedef TinyGsmSim808::GsmClientSecure TinyGsmClientSecure; +#define TINY_GSM_MODEM_HAS_GPRS +#define TINY_GSM_MODEM_HAS_SSL +#define TINY_GSM_MODEM_HAS_GPS +#include "TinyGsmClientSIM808.h" +typedef TinyGsmSim808 TinyGsm; +typedef TinyGsmSim808::GsmClientSim800 TinyGsmClient; +typedef TinyGsmSim808::GsmClientSecureSim800 TinyGsmClientSecure; #elif defined(TINY_GSM_MODEM_SIM900) - #define TINY_GSM_MODEM_HAS_GPRS - #include - typedef TinyGsmSim800 TinyGsm; - typedef TinyGsmSim800::GsmClient TinyGsmClient; +#define TINY_GSM_MODEM_HAS_GPRS +#include "TinyGsmClientSIM800.h" +typedef TinyGsmSim800 TinyGsm; +typedef TinyGsmSim800::GsmClientSim800 TinyGsmClient; #elif defined(TINY_GSM_MODEM_SIM7000) - #define TINY_GSM_MODEM_HAS_GPRS - #define TINY_GSM_MODEM_HAS_GPS - #include - typedef TinyGsmSim7000 TinyGsm; - typedef TinyGsmSim7000::GsmClient TinyGsmClient; - // typedef TinyGsmSim7000::GsmClientSecure TinyGsmClientSecure; TODO! +#define TINY_GSM_MODEM_HAS_GPRS +#define TINY_GSM_MODEM_HAS_GPS +#include "TinyGsmClientSIM7000.h" +typedef TinyGsmSim7000 TinyGsm; +typedef TinyGsmSim7000::GsmClientSim7000 TinyGsmClient; +// typedef TinyGsmSim7000::GsmClientSecureSim7000 TinyGsmClientSecure; TODO! #elif defined(TINY_GSM_MODEM_SIM5320) || defined(TINY_GSM_MODEM_SIM5360) || \ - defined(TINY_GSM_MODEM_SIM5300) || defined(TINY_GSM_MODEM_SIM7100) - #define TINY_GSM_MODEM_HAS_GPRS - #include - typedef TinyGsmSim5360 TinyGsm; - typedef TinyGsmSim5360::GsmClient TinyGsmClient; + defined(TINY_GSM_MODEM_SIM5300) || defined(TINY_GSM_MODEM_SIM7100) +#define TINY_GSM_MODEM_HAS_GPRS +#include "TinyGsmClientSIM5360.h" +typedef TinyGsmSim5360 TinyGsm; +typedef TinyGsmSim5360::GsmClientSim5360 TinyGsmClient; #elif defined(TINY_GSM_MODEM_SIM7600) || defined(TINY_GSM_MODEM_SIM7800) || \ defined(TINY_GSM_MODEM_SIM7500) - #define TINY_GSM_MODEM_HAS_GPRS - #include - typedef TinyGsmSim7600 TinyGsm; - typedef TinyGsmSim7600::GsmClient TinyGsmClient; +#define TINY_GSM_MODEM_HAS_GPRS +#include "TinyGsmClientSIM7600.h" +typedef TinyGsmSim7600 TinyGsm; +typedef TinyGsmSim7600::GsmClientSim7600 TinyGsmClient; #elif defined(TINY_GSM_MODEM_UBLOX) - #define TINY_GSM_MODEM_HAS_GPRS - #define TINY_GSM_MODEM_HAS_SSL - #include - typedef TinyGsmUBLOX TinyGsm; - typedef TinyGsmUBLOX::GsmClient TinyGsmClient; - typedef TinyGsmUBLOX::GsmClientSecure TinyGsmClientSecure; +#define TINY_GSM_MODEM_HAS_GPRS +#define TINY_GSM_MODEM_HAS_SSL +#include "TinyGsmClientUBLOX.h" +typedef TinyGsmUBLOX TinyGsm; +typedef TinyGsmUBLOX::GsmClientUBLOX TinyGsmClient; +typedef TinyGsmUBLOX::GsmClientSecureUBLOX TinyGsmClientSecure; #elif defined(TINY_GSM_MODEM_SARAR4) - #define TINY_GSM_MODEM_HAS_GPRS - #define TINY_GSM_MODEM_HAS_SSL - #include - typedef TinyGsmSaraR4 TinyGsm; - typedef TinyGsmSaraR4::GsmClient TinyGsmClient; - typedef TinyGsmSaraR4::GsmClientSecure TinyGsmClientSecure; +#define TINY_GSM_MODEM_HAS_GPRS +#define TINY_GSM_MODEM_HAS_SSL +#include "TinyGsmClientSaraR4.h" +typedef TinyGsmSaraR4 TinyGsm; +typedef TinyGsmSaraR4::GsmClientSaraR4 TinyGsmClient; +typedef TinyGsmSaraR4::GsmClientSecureR4 TinyGsmClientSecure; #elif defined(TINY_GSM_MODEM_M95) - #define TINY_GSM_MODEM_HAS_GPRS - #include - typedef TinyGsmM95 TinyGsm; - typedef TinyGsmM95::GsmClient TinyGsmClient; +#define TINY_GSM_MODEM_HAS_GPRS +#include "TinyGsmClientM95.h" +typedef TinyGsmM95 TinyGsm; +typedef TinyGsmM95::GsmClientM95 TinyGsmClient; #elif defined(TINY_GSM_MODEM_BG96) - #define TINY_GSM_MODEM_HAS_GPRS - #include - typedef TinyGsmBG96 TinyGsm; - typedef TinyGsmBG96::GsmClient TinyGsmClient; +#define TINY_GSM_MODEM_HAS_GPRS +#include "TinyGsmClientBG96.h" +typedef TinyGsmBG96 TinyGsm; +typedef TinyGsmBG96::GsmClientBG96 TinyGsmClient; #elif defined(TINY_GSM_MODEM_A6) || defined(TINY_GSM_MODEM_A7) - #define TINY_GSM_MODEM_HAS_GPRS - #include - typedef TinyGsmA6 TinyGsm; - typedef TinyGsmA6::GsmClient TinyGsmClient; +#define TINY_GSM_MODEM_HAS_GPRS +#include "TinyGsmClientA6.h" +typedef TinyGsmA6 TinyGsm; +typedef TinyGsmA6::GsmClientA6 TinyGsmClient; #elif defined(TINY_GSM_MODEM_M590) - #define TINY_GSM_MODEM_HAS_GPRS - #include - typedef TinyGsmM590 TinyGsm; - typedef TinyGsmM590::GsmClient TinyGsmClient; +#define TINY_GSM_MODEM_HAS_GPRS +#include "TinyGsmClientM590.h" +typedef TinyGsmM590 TinyGsm; +typedef TinyGsmM590::GsmClientM590 TinyGsmClient; #elif defined(TINY_GSM_MODEM_MC60) || defined(TINY_GSM_MODEM_MC60E) - #include - #define TINY_GSM_MODEM_HAS_GPRS - #define TINY_GSM_MODEM_HAS_GPS - typedef TinyGsmMC60 TinyGsm; - typedef TinyGsmMC60::GsmClient TinyGsmClient; +#include "TinyGsmClientMC60.h" +#define TINY_GSM_MODEM_HAS_GPRS +#define TINY_GSM_MODEM_HAS_GPS +typedef TinyGsmMC60 TinyGsm; +typedef TinyGsmMC60::GsmClientMC60 TinyGsmClient; #elif defined(TINY_GSM_MODEM_ESP8266) - #define TINY_GSM_MODEM_HAS_WIFI - #define TINY_GSM_MODEM_HAS_SSL - #include - typedef TinyGsmESP8266 TinyGsm; - typedef TinyGsmESP8266::GsmClient TinyGsmClient; - typedef TinyGsmESP8266::GsmClientSecure TinyGsmClientSecure; +#define TINY_GSM_MODEM_HAS_WIFI +#define TINY_GSM_MODEM_HAS_SSL +#include "TinyGsmClientESP8266.h" +typedef TinyGsmESP8266 TinyGsm; +typedef TinyGsmESP8266::GsmClientESP8266 TinyGsmClient; +typedef TinyGsmESP8266::GsmClientSecureESP8266 TinyGsmClientSecure; #elif defined(TINY_GSM_MODEM_XBEE) - #define TINY_GSM_MODEM_HAS_GPRS - #define TINY_GSM_MODEM_HAS_WIFI - #define TINY_GSM_MODEM_HAS_SSL - #include - typedef TinyGsmXBee TinyGsm; - typedef TinyGsmXBee::GsmClient TinyGsmClient; - typedef TinyGsmXBee::GsmClientSecure TinyGsmClientSecure; +#define TINY_GSM_MODEM_HAS_GPRS +#define TINY_GSM_MODEM_HAS_WIFI +#define TINY_GSM_MODEM_HAS_SSL +#include "TinyGsmClientXBee.h" +typedef TinyGsmXBee TinyGsm; +typedef TinyGsmXBee::GsmClientXBee TinyGsmClient; +typedef TinyGsmXBee::GsmClientSecureXBee TinyGsmClientSecure; #elif defined(TINY_GSM_MODEM_SEQUANS_MONARCH) - #define TINY_GSM_MODEM_HAS_GPRS - #define TINY_GSM_MODEM_HAS_SSL - #include - typedef TinyGsmSequansMonarch TinyGsm; - typedef TinyGsmSequansMonarch::GsmClient TinyGsmClient; - typedef TinyGsmSequansMonarch::GsmClientSecure TinyGsmClientSecure; - +#define TINY_GSM_MODEM_HAS_GPRS +#define TINY_GSM_MODEM_HAS_SSL +#include "TinyGsmClientSequansMonarch.h" +typedef TinyGsmSequansMonarch TinyGsm; +typedef TinyGsmSequansMonarch::GsmClientSequansMonarch TinyGsmClient; +typedef TinyGsmSequansMonarch::GsmClientSecureSequansMonarch TinyGsmClientSecure; #else - #error "Please define GSM modem model" +#error "Please define GSM modem model" #endif #endif diff --git a/src/TinyGsmClientA6.h b/src/TinyGsmClientA6.h index 14dc15a..fc89369 100644 --- a/src/TinyGsmClientA6.h +++ b/src/TinyGsmClientA6.h @@ -6,31 +6,23 @@ * @date Nov 2016 */ -#ifndef TinyGsmClientA6_h -#define TinyGsmClientA6_h -//#pragma message("TinyGSM: TinyGsmClientA6") +#ifndef SRC_TINYGSMCLIENTA6_H_ +#define SRC_TINYGSMCLIENTA6_H_ +// #pragma message("TinyGSM: TinyGsmClientA6") -//#define TINY_GSM_DEBUG Serial - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 256 -#endif +// #define TINY_GSM_DEBUG Serial #define TINY_GSM_MUX_COUNT 8 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,112 +31,101 @@ enum RegStatus { REG_UNKNOWN = 4, }; - class TinyGsmA6 -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmA6; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} - - GsmClient(TinyGsmA6& modem) { - init(&modem); - } + : public TinyGsmModem { + friend class TinyGsmModem; - virtual ~GsmClient(){} - - bool init(TinyGsmA6* modem) { - this->at = modem; - this->mux = -1; - sock_connected = false; + /* + * Inner Client + */ + public: + class GsmClientA6 : public GsmClient { + friend class TinyGsmA6; - return true; - } + public: + GsmClientA6() {} -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - uint8_t newMux = -1; - sock_connected = at->modemConnect(host, port, &newMux, timeout_s); - if (sock_connected) { - mux = newMux; - at->sockets[mux] = this; + explicit GsmClientA6(TinyGsmA6& modem) { + init(&modem, -1); } - return sock_connected; - } - -TINY_GSM_CLIENT_CONNECT_OVERLOADS() - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_YIELD(); - at->sendAT(GF("+CIPCLOSE="), mux); - sock_connected = false; - at->waitResponse(maxWaitMs); - rx.clear(); - } + bool init(TinyGsmA6* modem, uint8_t) { + this->at = modem; + this->mux = -1; + sock_connected = false; - virtual void stop() { stop(1000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + uint8_t newMux = -1; + sock_connected = at->modemConnect(host, port, &newMux, timeout_s); + if (sock_connected) { + mux = newMux; + at->sockets[mux] = this; + } + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() + void stop(uint32_t maxWaitMs) { + TINY_GSM_YIELD(); + at->sendAT(GF("+CIPCLOSE="), mux); + sock_connected = false; + at->waitResponse(maxWaitMs); + rx.clear(); + } + void stop() override { + stop(1000L); + } -TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() + /* + * Extended API + */ -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; /* - * Extended API + * Inner Secure Client */ - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + // Doesn't support SSL -private: - TinyGsmA6* at; - uint8_t mux; - bool sock_connected; - RxFifo rx; -}; - - -public: - - TinyGsmA6(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmA6(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } + if (!testAT()) { return false; } // sendAT(GF("&FZ")); // Factory + Reset // waitResponse(); sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } #ifdef TINY_GSM_DEBUG sendAT(GF("+CMEE=2")); // turn on verbose error codes @@ -152,7 +133,8 @@ public: sendAT(GF("+CMEE=0")); // turn off error codes #endif waitResponse(); - sendAT(GF("+CMER=3,0,0,2")); // Set unsolicited result code output destination + sendAT( + GF("+CMER=3,0,0,2")); // Set unsolicited result code output destination waitResponse(); DBG(GF("### Modem:"), getModemName()); @@ -162,272 +144,184 @@ public: 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 { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { - #if defined(TINY_GSM_MODEM_A6) - return "AI-Thinker A6"; - #elif defined(TINY_GSM_MODEM_A7) - return "AI-Thinker A7"; - #endif - return "AI-Thinker A6"; - } - -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_LISTEN() - - bool factoryDefault() { + bool factoryDefaultImpl() { sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write waitResponse(); - sendAT(GF("&W")); // Write configuration + sendAT(GF("&W")); // Write configuration return waitResponse() == 1; } -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { + bool thisHasSSL() { return false; } - bool hasWifi() { + bool thisHasWifi() { return false; } - bool hasGPRS() { + bool thisHasGPRS() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } + protected: + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("+RST=1")); delay(3000); return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+CPOF")); + // +CPOF: MS OFF OK return waitResponse() == 1; } - bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE; /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - - String getSimCCID() { + protected: + String getSimCCIDImpl() { sendAT(GF("+CCID")); - if (waitResponse(GF(GSM_NL "+SCID: SIM Card ID:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+SCID: SIM Card ID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); return res; } -TINY_GSM_MODEM_GET_IMEI_GSN() - - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - 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")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; - } - -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) - - String getOperator() { - sendAT(GF("+COPS=3,0")); // Set format - waitResponse(); - - sendAT(GF("+COPS?")); - if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { - return ""; - } - streamSkipUntil('"'); // Skip mode and format - String res = stream.readStringUntil('"'); - waitResponse(); - return res; - } - /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CREG"); + } -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { gprsDisconnect(); sendAT(GF("+CGATT=1")); - if (waitResponse(60000L) != 1) - return false; + if (waitResponse(60000L) != 1) { return false; } - // TODO: wait AT+CGATT? + // TODO(?): wait AT+CGATT? sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"'); waitResponse(); if (!user) user = ""; - if (!pwd) pwd = ""; + if (!pwd) pwd = ""; sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\"")); - if (waitResponse(60000L) != 1) { - return false; - } + if (waitResponse(60000L) != 1) { return false; } sendAT(GF("+CGACT=1,1")); waitResponse(60000L); sendAT(GF("+CIPMUX=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } - bool gprsDisconnect() { + bool gprsDisconnectImpl() { // Shut the TCP/IP connection sendAT(GF("+CIPSHUT")); - if (waitResponse(60000L) != 1) - return false; + if (waitResponse(60000L) != 1) { return false; } - for (int i = 0; i<3; i++) { + for (int i = 0; i < 3; i++) { sendAT(GF("+CGATT=0")); - if (waitResponse(5000L) == 1) - return true; + if (waitResponse(5000L) == 1) { return true; } } return false; } - bool isGprsConnected() { - sendAT(GF("+CGATT?")); - if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { - return false; - } - int res = stream.readStringUntil('\n').toInt(); + String getOperatorImpl() { + sendAT(GF("+COPS=3,0")); // Set format + waitResponse(); + + sendAT(GF("+COPS?")); + if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { return ""; } + streamSkipUntil('"'); // Skip mode and format + String res = stream.readStringUntil('"'); waitResponse(); - return (res == 1); + return res; } /* * IP Address functions */ - - String getLocalIP() { + protected: + String getLocalIPImpl() { sendAT(GF("+CIFSR")); String res; - if (waitResponse(10000L, res) != 1) { - return ""; - } + if (waitResponse(10000L, res) != 1) { return ""; } res.replace(GSM_NL "OK" GSM_NL, ""); res.replace(GSM_NL, ""); res.trim(); return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * Phone Call functions */ - - bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE; - - bool callAnswer() { - sendAT(GF("A")); - return waitResponse() == 1; - } - + protected: // Returns true on pick-up, false on error/busy - bool callNumber(const String& number) { + bool callNumberImpl(const String& number) { if (number == GF("last")) { sendAT(GF("DLST")); } else { sendAT(GF("D\""), number, "\";"); } - if (waitResponse(5000L) != 1) { - return false; - } + if (waitResponse(5000L) != 1) { return false; } - if (waitResponse(60000L, - GF(GSM_NL "+CIEV: \"CALL\",1"), - GF(GSM_NL "+CIEV: \"CALL\",0"), - GFP(GSM_ERROR)) != 1) - { + if (waitResponse(60000L, GF(GSM_NL "+CIEV: \"CALL\",1"), + GF(GSM_NL "+CIEV: \"CALL\",0"), GFP(GSM_ERROR)) != 1) { return false; } - int rsp = waitResponse(60000L, - GF(GSM_NL "+CIEV: \"SOUNDER\",0"), - GF(GSM_NL "+CIEV: \"CALL\",0")); + int rsp = waitResponse(60000L, GF(GSM_NL "+CIEV: \"SOUNDER\",0"), + GF(GSM_NL "+CIEV: \"CALL\",0")); - int rsp2 = waitResponse(300L, GF(GSM_NL "BUSY" GSM_NL), GF(GSM_NL "NO ANSWER" GSM_NL)); + int rsp2 = waitResponse(300L, GF(GSM_NL "BUSY" GSM_NL), + GF(GSM_NL "NO ANSWER" GSM_NL)); return rsp == 1 && rsp2 == 0; } - bool callHangup() { - sendAT(GF("H")); - return waitResponse() == 1; - } - // 0-9,*,#,A,B,C,D - bool dtmfSend(char cmd, unsigned duration_ms = 100) { + bool dtmfSendImpl(char cmd, unsigned duration_ms = 100) { duration_ms = constrain(duration_ms, 100, 1000); // The duration parameter is not working, so we simulate it using delay.. - // TODO: Maybe there's another way... + // TODO(?): Maybe there's another way... - //sendAT(GF("+VTD="), duration_ms / 100); - //waitResponse(); + // sendAT(GF("+VTD="), duration_ms / 100); + // waitResponse(); sendAT(GF("+VTS="), cmd); if (waitResponse(10000L) == 1) { @@ -440,7 +334,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * Audio functions */ - + public: bool audioSetHeadphones() { sendAT(GF("+SNFS=0")); return waitResponse() == 1; @@ -459,19 +353,15 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * Messaging functions */ - - String sendUSSD(const String& code) { + protected: + String sendUSSDImpl(const String& code) { sendAT(GF("+CMGF=1")); waitResponse(); sendAT(GF("+CSCS=\"HEX\"")); waitResponse(); sendAT(GF("+CUSD=1,\""), code, GF("\",15")); - if (waitResponse(10000L) != 1) { - return ""; - } - if (waitResponse(GF(GSM_NL "+CUSD:")) != 1) { - return ""; - } + if (waitResponse(10000L) != 1) { return ""; } + if (waitResponse(GF(GSM_NL "+CUSD:")) != 1) { return ""; } stream.readStringUntil('"'); String hex = stream.readStringUntil('"'); stream.readStringUntil(','); @@ -486,38 +376,35 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } } - bool sendSMS(const String& number, const String& text) { - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - - /* * Location functions */ + protected: + String getGsmLocationImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * GPS location functions + */ + public: + // No functions of this type supported /* - * Battery & temperature functions + * Time functions */ + protected: + // Can follow the standard CCLK function in the template + // Note - the clock probably has to be set manaually first - uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * Battery & temperature functions + */ + protected: + uint16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - int8_t getBattPercent() { + int8_t getBattPercentImpl() { sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - streamSkipUntil(','); // Skip battery charge status + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return false; } + streamSkipUntil(','); // Skip battery charge status // Read battery charge level int res = stream.readStringUntil('\n').toInt(); // Wait for final OK @@ -525,57 +412,37 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() return res; } - uint8_t getBattChargeState() { + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) { sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - // Read battery charge status - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return false; } chargeState = stream.readStringUntil(',').toInt(); - percent = stream.readStringUntil('\n').toInt(); - milliVolts = 0; + percent = stream.readStringUntil('\n').toInt(); + milliVolts = 0; // Wait for final OK waitResponse(); return true; } - - float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; + float getTemperatureImpl() TINY_GSM_ATTR_NOT_AVAILABLE; /* * Client related functions */ - -protected: - - bool modemConnect(const char* host, uint16_t port, uint8_t* mux, int timeout_s = 75) { - unsigned long startMillis = millis(); - uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; - - sendAT(GF("+CIPSTART="), GF("\"TCP"), GF("\",\""), host, GF("\","), port); - if (waitResponse(timeout_ms, GF(GSM_NL "+CIPNUM:")) != 1) { - return false; - } + protected: + bool modemConnect(const char* host, uint16_t port, uint8_t* mux, + int timeout_s = 75) { + uint32_t startMillis = millis(); + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + + sendAT(GF("+CIPSTART="), GF("\"TCP"), GF("\",\""), host, GF("\","), port); + if (waitResponse(timeout_ms, GF(GSM_NL "+CIPNUM:")) != 1) { return false; } int newMux = stream.readStringUntil('\n').toInt(); - int rsp = waitResponse((timeout_ms- (millis() - startMillis)), - GF("CONNECT OK" GSM_NL), - GF("CONNECT FAIL" GSM_NL), + int rsp = waitResponse((timeout_ms - (millis() - startMillis)), + GF("CONNECT OK" GSM_NL), GF("CONNECT FAIL" GSM_NL), GF("ALREADY CONNECT" GSM_NL)); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } *mux = newMux; return (1 == rsp); @@ -583,37 +450,38 @@ protected: int16_t modemSend(const void* buff, size_t len, uint8_t mux) { sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len); - if (waitResponse(2000L, GF(GSM_NL ">")) != 1) { - return 0; - } - stream.write((uint8_t*)buff, len); + if (waitResponse(2000L, GF(GSM_NL ">")) != 1) { return 0; } + stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(10000L, GFP(GSM_OK), GF(GSM_NL "FAIL")) != 1) { - return 0; - } + if (waitResponse(10000L, GFP(GSM_OK), GF(GSM_NL "FAIL")) != 1) { return 0; } return len; } + size_t modemRead(size_t, uint8_t) { + return 0; + } + size_t modemGetAvailable(uint8_t) { + return 0; + } + bool modemGetConnected(uint8_t) { - sendAT(GF("+CIPSTATUS")); //TODO mux? - int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\"")); + sendAT(GF("+CIPSTATUS")); // TODO(?) mux? + int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), + GF(",\"CLOSING\""), GF(",\"INITIAL\"")); waitResponse(); return 1 == res; } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -621,15 +489,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -646,19 +514,19 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 5; goto finish; } else if (data.endsWith(GF("+CIPRCV:"))) { - int mux = stream.readStringUntil(',').toInt(); - int len = stream.readStringUntil(',').toInt(); + int mux = stream.readStringUntil(',').toInt(); + int len = stream.readStringUntil(',').toInt(); int len_orig = len; if (len > sockets[mux]->rx.free()) { DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free()); } else { DBG("### Got: ", len, "->", sockets[mux]->rx.free()); } - while (len--) { - TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT - } - if (len_orig > sockets[mux]->available()) { // TODO - DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); + while (len--) { moveCharFromStreamToFifo(mux); } + // TODO(?) Deal with missing characters + if (len_orig > sockets[mux]->available()) { + DBG("### Fewer characters received than expected: ", + sockets[mux]->available(), " vs ", len_orig); } data = ""; } else if (data.endsWith(GF("+TCPCLOSED:"))) { @@ -671,38 +539,36 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } 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=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + 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) { 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=NULL, 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; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientA6* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTA6_H_ diff --git a/src/TinyGsmClientBG96.h b/src/TinyGsmClientBG96.h index cd738d7..dad31cb 100644 --- a/src/TinyGsmClientBG96.h +++ b/src/TinyGsmClientBG96.h @@ -6,31 +6,23 @@ * @date Apr 2018 */ -#ifndef TinyGsmClientBG96_h -#define TinyGsmClientBG96_h -//#pragma message("TinyGSM: TinyGsmClientBG96") +#ifndef SRC_TINYGSMCLIENTBG96_H_ +#define SRC_TINYGSMCLIENTBG96_H_ +// #pragma message("TinyGSM: TinyGsmClientBG96") -//#define TINY_GSM_DEBUG Serial - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 64 -#endif +// #define TINY_GSM_DEBUG Serial #define TINY_GSM_MUX_COUNT 12 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,474 +31,303 @@ enum RegStatus { REG_UNKNOWN = 4, }; +class TinyGsmBG96 + : public TinyGsmModem { + friend class TinyGsmModem; -class TinyGsmBG96: public TinyGsmUTFSMS -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmBG96; - typedef TinyGsmFifo RxFifo; + /* + * Inner Client + */ + public: + class GsmClientBG96 : public GsmClient { + friend class TinyGsmBG96; -public: - GsmClient() {} + public: + GsmClientBG96() {} - GsmClient(TinyGsmBG96& modem, uint8_t mux = 1) { - init(&modem, mux); - } + explicit GsmClientBG96(TinyGsmBG96& modem, uint8_t mux = 1) { + init(&modem, mux); + } - virtual ~GsmClient(){} + bool init(TinyGsmBG96* modem, uint8_t mux = 1) { + this->at = modem; + this->mux = mux; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; - bool init(TinyGsmBG96* modem, uint8_t mux = 1) { - this->at = modem; - this->mux = mux; - sock_available = 0; - prev_check = 0; - sock_connected = false; - got_data = false; + at->sockets[mux] = this; - at->sockets[mux] = this; + return true; + } - return true; - } + public: + virtual int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, false, timeout_s); - return sock_connected; - } + void stop(uint32_t maxWaitMs) { + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+QICLOSE="), mux); + sock_connected = false; + at->waitResponse(); + } + void stop() override { + stop(15000L); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + /* + * Extended API + */ - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() - at->sendAT(GF("+QICLOSE="), mux); - sock_connected = false; - at->waitResponse(); - } + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; - virtual void stop() { stop(15000L); } + /* + * Inner Secure Client + */ -TINY_GSM_CLIENT_WRITE() + /* + class GsmClientSecureBG96 : public GsmClientBG96 + { + public: + GsmClientSecure() {} -TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() + GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 1) + : public GsmClient(modem, mux) + {} -TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + public: + virtual int connect(const char *host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + }; + */ /* - * Extended API + * Constructor */ - - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; - -private: - TinyGsmBG96* at; - uint8_t mux; - uint16_t sock_available; - uint32_t prev_check; - bool sock_connected; - bool got_data; - RxFifo rx; -}; - - -// class GsmClientSecure : public GsmClient -// { -// public: -// GsmClientSecure() {} -// -// GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 1) -// : GsmClient(modem, mux) -// {} -// -// virtual ~GsmClientSecure(){} -// -// public: -// virtual int connect(const char *host, uint16_t port, int timeout_s) { -// stop(); -// TINY_GSM_YIELD(); -// rx.clear(); -// sock_connected = at->modemConnect(host, port, mux, true, timeout_s); -// return sock_connected; -// } -// }; - - -public: - - TinyGsmBG96(Stream& stream) - : stream(stream) - { + public: + explicit TinyGsmBG96(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } - - // sendAT(GF("&FZ")); // Factory + Reset - // waitResponse(); + if (!testAT()) { return false; } sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } + +#ifdef TINY_GSM_DEBUG + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes +#endif + waitResponse(); DBG(GF("### Modem:"), getModemName()); + // Enable automatic time zone update + sendAT(GF("+CTZU=1")); + if (waitResponse(10000L) != 1) { return false; } + 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 { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { - return "Quectel BG96"; - } - -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() - - bool factoryDefault() { - sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write - waitResponse(); - sendAT(GF("+IPR=0")); // Auto-baud - waitResponse(); - sendAT(GF("&W")); // Write configuration - return waitResponse() == 1; - } - -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { - return false; // TODO: For now + bool thisHasSSL() { + return false; // TODO(?): Add SSL support } - bool hasWifi() { + bool thisHasWifi() { return false; } - bool hasGPRS() { + bool thisHasGPRS() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } + protected: + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("+CFUN=1,1")); - if (waitResponse(60000L, GF("POWERED DOWN")) != 1) { - return false; - } + if (waitResponse(60000L, GF("POWERED DOWN")) != 1) { return false; } waitResponse(5000L, GF("RDY")); return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+QPOWD=1")); waitResponse(300); // returns OK first return waitResponse(300, GF("POWERED DOWN")) == 1; } - bool radioOff() { - sendAT(GF("+CFUN=0")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000); - return true; + // When entering into sleep mode is enabled, DTR is pulled up, and WAKEUP_IN + // is pulled up, the module can directly enter into sleep mode.If entering + // into sleep mode is enabled, DTR is pulled down, and WAKEUP_IN is pulled + // down, there is a need to pull the DTR pin and the WAKEUP_IN pin up first, + // and then the module can enter into sleep mode. + bool sleepEnableImpl(bool enable = true) { + sendAT(GF("+QSCLK="), enable); + return waitResponse() == 1; } /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - - String getSimCCID() { + protected: + String getSimCCIDImpl() { sendAT(GF("+QCCID")); - if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); return res; } -TINY_GSM_MODEM_GET_IMEI_GSN() - - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - 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")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; - } - -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) - -TINY_GSM_MODEM_GET_OPERATOR_COPS() - /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CREG"); + } -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { gprsDisconnect(); - //Configure the TCPIP Context - sendAT(GF("+QICSGP=1,1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\"")); - if (waitResponse() != 1) { - return false; - } + // Configure the TCPIP Context + sendAT(GF("+QICSGP=1,1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, + GF("\"")); + if (waitResponse() != 1) { return false; } - //Activate GPRS/CSD Context + // Activate GPRS/CSD Context sendAT(GF("+QIACT=1")); - if (waitResponse(150000L) != 1) { - return false; - } + if (waitResponse(150000L) != 1) { return false; } - //Attach to Packet Domain service - is this necessary? + // Attach to Packet Domain service - is this necessary? sendAT(GF("+CGATT=1")); - if (waitResponse(60000L) != 1) { - return false; - } + if (waitResponse(60000L) != 1) { return false; } return true; } - bool gprsDisconnect() { + bool gprsDisconnectImpl() { sendAT(GF("+QIDEACT=1")); // Deactivate the bearer context - if (waitResponse(40000L) != 1) - return false; + if (waitResponse(40000L) != 1) { return false; } return true; } - TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() - /* * IP Address functions */ - - String getLocalIP() { - sendAT(GF("+CGPADDR=1")); - if (waitResponse(GF(GSM_NL "+CGPADDR:")) != 1) { - return ""; - } - streamSkipUntil(','); // Skip context id - String res = stream.readStringUntil('\n'); - if (waitResponse() != 1) { - return ""; - } - return res; - } - - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } + protected: + // Can follow all of the IP functions from the template /* * Phone Call functions */ - - bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE; - - bool callAnswer() { - sendAT(GF("A")); - return waitResponse() == 1; - } - - // Returns true on pick-up, false on error/busy - bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool callHangup() { - sendAT(GF("H")); - return waitResponse() == 1; - } - - // 0-9,*,#,A,B,C,D - bool dtmfSend(char cmd, int duration_ms = 100) { // TODO: check - duration_ms = constrain(duration_ms, 100, 1000); - - sendAT(GF("+VTD="), duration_ms / 100); // VTD accepts in 1/10 of a second - waitResponse(); - - sendAT(GF("+VTS="), cmd); - return waitResponse(10000L) == 1; - } + protected: + // Can follow all of the phone call functions from the template /* * Messaging functions */ - - String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool sendSMS(const String& number, const String& text) { - sendAT(GF("+CMGF=1")); - waitResponse(); - //Set GSM 7 bit default alphabet (3GPP TS 23.038) - sendAT(GF("+CSCS=\"GSM\"")); - waitResponse(); - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - + protected: + // Follows all messaging functions per template /* * Location functions */ - - String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; + protected: + String getGsmLocationImpl() TINY_GSM_ATTR_NOT_AVAILABLE; /* - * Battery & temperature functions + * GPS location functions */ + public: + // No functions of this type supported - // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip battery charge status - streamSkipUntil(','); // Skip battery charge level - // return voltage in mV - uint16_t res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - int8_t getBattPercent() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - streamSkipUntil(','); // Skip battery charge status - // Read battery charge level - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - uint8_t getBattChargeState() { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - // Read battery charge status - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - chargeState = stream.readStringUntil(',').toInt(); - percent = stream.readStringUntil(',').toInt(); - milliVolts = stream.readStringUntil('\n').toInt(); - // Wait for final OK - waitResponse(); - return true; - } + /* + * Time functions + */ + protected: + // Can follow the standard CCLK function in the template - float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * Battery & temperature functions + */ + protected: + float getTemperatureImpl() TINY_GSM_ATTR_NOT_AVAILABLE; /* * Client related functions */ + protected: + 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/UDPSERVICE", "/", + // ,,(0-2; 0=buffer) + sendAT(GF("+QIOPEN=1,"), mux, GF(",\""), GF("TCP"), GF("\",\""), host, + GF("\","), port, GF(",0,0")); + waitResponse(); -protected: - - 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) - sendAT(GF("+QIOPEN=1,"), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(",0,0")); - rsp = waitResponse(); - - if (waitResponse(timeout_ms, GF(GSM_NL "+QIOPEN:")) != 1) { - return false; - } + if (waitResponse(timeout_ms, GF(GSM_NL "+QIOPEN:")) != 1) { return false; } - if (stream.readStringUntil(',').toInt() != mux) { - return false; - } + if (stream.readStringUntil(',').toInt() != mux) { return false; } // Read status rsp = stream.readStringUntil('\n').toInt(); @@ -515,28 +336,20 @@ protected: int16_t modemSend(const void* buff, size_t len, uint8_t mux) { sendAT(GF("+QISEND="), mux, ',', (uint16_t)len); - if (waitResponse(GF(">")) != 1) { - return 0; - } - stream.write((uint8_t*)buff, len); + if (waitResponse(GF(">")) != 1) { return 0; } + stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(GF(GSM_NL "SEND OK")) != 1) { - return 0; - } - // TODO: Wait for ACK? AT+QISEND=id,0 + if (waitResponse(GF(GSM_NL "SEND OK")) != 1) { return 0; } + // TODO(?): Wait for ACK? AT+QISEND=id,0 return len; } size_t modemRead(size_t size, uint8_t mux) { sendAT(GF("+QIRD="), mux, ',', (uint16_t)size); - if (waitResponse(GF("+QIRD:")) != 1) { - return 0; - } + if (waitResponse(GF("+QIRD:")) != 1) { return 0; } int len = stream.readStringUntil('\n').toInt(); - for (int i=0; isock_available = modemGetAvailable(mux); @@ -547,33 +360,28 @@ protected: sendAT(GF("+QIRD="), mux, GF(",0")); size_t result = 0; if (waitResponse(GF("+QIRD:")) == 1) { - streamSkipUntil(','); // Skip total received - streamSkipUntil(','); // Skip have read + streamSkipUntil(','); // Skip total received + streamSkipUntil(','); // Skip have read result = stream.readStringUntil('\n').toInt(); - if (result) { - DBG("### DATA AVAILABLE:", result, "on", mux); - } + if (result) { DBG("### DATA AVAILABLE:", result, "on", mux); } waitResponse(); } - if (!result) { - sockets[mux]->sock_connected = modemGetConnected(mux); - } + if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); } return result; } bool modemGetConnected(uint8_t mux) { sendAT(GF("+QISTATE=1,"), mux); - //+QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1" + // +QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1" - if (waitResponse(GF("+QISTATE:")) != 1) - return false; + if (waitResponse(GF("+QISTATE:")) != 1) { return false; } - streamSkipUntil(','); // Skip mux - streamSkipUntil(','); // Skip socket type - streamSkipUntil(','); // Skip remote ip - streamSkipUntil(','); // Skip remote port - streamSkipUntil(','); // Skip local port - int res = stream.readStringUntil(',').toInt(); // socket state + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip socket type + streamSkipUntil(','); // Skip remote ip + streamSkipUntil(','); // Skip remote port + streamSkipUntil(','); // Skip local port + int res = stream.readStringUntil(',').toInt(); // socket state waitResponse(); @@ -581,19 +389,16 @@ protected: return 2 == res; } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -601,15 +406,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -648,38 +453,36 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } 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=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + 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) { 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=NULL, 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; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientBG96* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTBG96_H_ diff --git a/src/TinyGsmClientESP8266.h b/src/TinyGsmClientESP8266.h index 3ea8e98..aac5e98 100644 --- a/src/TinyGsmClientESP8266.h +++ b/src/TinyGsmClientESP8266.h @@ -6,24 +6,21 @@ * @date Nov 2016 */ -#ifndef TinyGsmClientESP8266_h -#define TinyGsmClientESP8266_h -//#pragma message("TinyGSM: TinyGsmClientESP8266") +#ifndef SRC_TINYGSMCLIENTESP8266_H_ +#define SRC_TINYGSMCLIENTESP8266_H_ +// #pragma message("TinyGSM: TinyGsmClientESP8266") -//#define TINY_GSM_DEBUG Serial - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 512 -#endif +// #define TINY_GSM_DEBUG Serial #define TINY_GSM_MUX_COUNT 5 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; -static unsigned TINY_GSM_TCP_KEEP_ALIVE = 120; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; +static unsigned TINY_GSM_TCP_KEEP_ALIVE = 120; // status of ESP8266 station interface // 2 : ESP8266 station connected to an AP and has obtained IP @@ -38,214 +35,187 @@ enum RegStatus { REG_UNKNOWN = 6, }; - class TinyGsmESP8266 -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmESP8266; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} - - GsmClient(TinyGsmESP8266& modem, uint8_t mux = 1) { - init(&modem, mux); - } + : public TinyGsmModem { + friend class TinyGsmModem; - virtual ~GsmClient(){} - - bool init(TinyGsmESP8266* modem, uint8_t mux = 1) { - this->at = modem; - this->mux = mux; - sock_connected = false; + /* + * Inner Client + */ + public: + class GsmClientESP8266 : public GsmClient { + friend class TinyGsmESP8266; - at->sockets[mux] = this; + public: + GsmClientESP8266() {} - return true; - } - -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, false, timeout_s); - return sock_connected; - } + explicit GsmClientESP8266(TinyGsmESP8266& modem, uint8_t mux = 1) { + init(&modem, mux); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + bool init(TinyGsmESP8266* modem, uint8_t mux = 1) { + this->at = modem; + this->mux = mux; + sock_connected = false; - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_YIELD(); - at->sendAT(GF("+CIPCLOSE="), mux); - sock_connected = false; - at->waitResponse(maxWaitMs); - rx.clear(); - } + at->sockets[mux] = this; - virtual void stop() { stop(5000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() + void stop(uint32_t maxWaitMs) { + TINY_GSM_YIELD(); + at->sendAT(GF("+CIPCLOSE="), mux); + sock_connected = false; + at->waitResponse(maxWaitMs); + rx.clear(); + } + void stop() override { + stop(5000L); + } -TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() + /* + * Extended API + */ -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; /* - * Extended API + * Inner Secure Client */ + public: + class GsmClientSecureESP8266 : public GsmClientESP8266 { + public: + GsmClientSecureESP8266() {} - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + explicit GsmClientSecureESP8266(TinyGsmESP8266& modem, uint8_t mux = 1) + : GsmClientESP8266(modem, mux) {} -private: - TinyGsmESP8266* at; - uint8_t mux; - bool sock_connected; - RxFifo rx; -}; - - -class GsmClientSecure : public GsmClient -{ -public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmESP8266& modem, uint8_t mux = 1) - : GsmClient(modem, mux) - {} - - virtual ~GsmClientSecure() {} - -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, true, timeout_s); - return sock_connected; - } -}; - - -public: + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + }; - TinyGsmESP8266(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmESP8266(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } + + if (!testAT()) { return false; } if (pin && strlen(pin) > 0) { DBG("ESP8266 modules do not use an unlock pin!"); } - sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } sendAT(GF("+CIPMUX=1")); // Enable Multiple Connections - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } sendAT(GF("+CWMODE_CUR=1")); // Put into "station" mode - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } DBG(GF("### Modem:"), getModemName()); return true; } - String getModemName() { + String getModemNameImpl() { return "ESP8266"; } - void setBaud(unsigned long baud) { + void setBaudImpl(uint32_t baud) { sendAT(GF("+UART_CUR="), baud, "8,1,0,0"); } -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_LISTEN() - - bool factoryDefault() { + bool factoryDefaultImpl() { sendAT(GF("+RESTORE")); return waitResponse() == 1; } - String getModemInfo() { + String getModemInfoImpl() { sendAT(GF("+GMR")); String res; - if (waitResponse(1000L, res) != 1) { - return ""; - } + if (waitResponse(1000L, res) != 1) { return ""; } res.replace(GSM_NL "OK" GSM_NL, ""); res.replace(GSM_NL, " "); res.trim(); return res; } - bool hasSSL() { + bool thisHasSSL() { return true; } - bool hasWifi() { + bool thisHasWifi() { return true; } - bool hasGPRS() { + bool thisHasGPRS() { return false; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } + protected: + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("+RST")); - if (waitResponse(10000L) != 1) { - return false; - } - if (waitResponse(10000L, GF(GSM_NL "ready" GSM_NL)) != 1) { - return false; - } + if (waitResponse(10000L) != 1) { return false; } + if (waitResponse(10000L, GF(GSM_NL "ready" GSM_NL)) != 1) { return false; } delay(500); return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+GSLP=0")); // Power down indefinitely - until manually reset! return waitResponse() == 1; } - bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool radioOffImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE; /* * SIM card functions */ + protected: + // SIM card functions don't apply + /* + * Generic network functions + */ + public: RegStatus getRegistrationStatus() { sendAT(GF("+CIPSTATUS")); if (waitResponse(3000, GF("STATUS:")) != 1) return REG_UNKNOWN; @@ -255,52 +225,44 @@ TINY_GSM_MODEM_MAINTAIN_LISTEN() return (RegStatus)status; } - /* - * Generic network functions - */ - - int16_t getSignalQuality() { + protected: + int16_t getSignalQualityImpl() { sendAT(GF("+CWJAP_CUR?")); int res1 = waitResponse(GF("No AP"), GF("+CWJAP_CUR:")); if (res1 != 2) { waitResponse(); return 0; } - streamSkipUntil(','); // Skip SSID - streamSkipUntil(','); // Skip BSSID/MAC address - streamSkipUntil(','); // Skip Chanel number + streamSkipUntil(','); // Skip SSID + streamSkipUntil(','); // Skip BSSID/MAC address + streamSkipUntil(','); // Skip Chanel number int res2 = stream.parseInt(); // Read RSSI - waitResponse(); // Returns an OK after the value + waitResponse(); // Returns an OK after the value return res2; } - bool isNetworkConnected() { + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); if (s == REG_OK_IP || s == REG_OK_TCP) { // with these, we're definitely connected return true; - } - else if (s == REG_OK_NO_TCP) { + } else if (s == REG_OK_NO_TCP) { // with this, we may or may not be connected if (getLocalIP() == "") { return false; - } - else { + } else { return true; } - } - else { + } else { return false; } } - TINY_GSM_MODEM_WAIT_FOR_NETWORK() - /* * WiFi functions */ - - bool networkConnect(const char* ssid, const char* pwd) { + protected: + bool networkConnectImpl(const char* ssid, const char* pwd) { sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\"")); if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) { return false; @@ -309,7 +271,7 @@ TINY_GSM_MODEM_MAINTAIN_LISTEN() return true; } - bool networkDisconnect() { + bool networkDisconnectImpl() { sendAT(GF("+CWQAP")); bool retVal = waitResponse(10000L) == 1; waitResponse(GF("WIFI DISCONNECT")); @@ -319,74 +281,107 @@ TINY_GSM_MODEM_MAINTAIN_LISTEN() /* * IP Address functions */ - - String getLocalIP() { - sendAT(GF("+CIPSTA_CUR??")); + protected: + String getLocalIPImpl() { + sendAT(GF("+CIPSTA_CUR?")); int res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:")); - if (res1 != 2) { - return ""; - } + if (res1 != 2) { return ""; } String res2 = stream.readStringUntil('"'); waitResponse(); return res2; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } + /* + * Phone Call functions + */ + protected: + bool callAnswerImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool callNumberImpl(const String& number) TINY_GSM_ATTR_NOT_AVAILABLE; + bool callHangupImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool + dtmfSendImpl(char cmd, int duration_ms = 100) TINY_GSM_ATTR_NOT_AVAILABLE; /* - * Battery & temperature functions + * Messaging functions */ + protected: + String sendUSSDImpl(const String& code) TINY_GSM_ATTR_NOT_AVAILABLE; + bool sendSMSImpl(const String& number, + const String& text) TINY_GSM_ATTR_NOT_AVAILABLE; + bool sendSMS_UTF16Impl(const char* const number, const void* text, + size_t len) TINY_GSM_ATTR_NOT_AVAILABLE; - // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; - 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; - float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * Location functions + */ + protected: + String getGsmLocationImpl() TINY_GSM_ATTR_NOT_AVAILABLE; /* - * Client related functions + * GPS location functions */ + public: + // No functions of this type supported -protected: + /* + * Time functions + */ + protected: + String + getGSMDateTimeImpl(TinyGSMDateTimeFormat format) TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * Battery & temperature functions + */ + protected: + uint16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + int8_t getBattPercentImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE; + float getTemperatureImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + + /* + * Client related functions + */ + protected: bool modemConnect(const char* host, uint16_t port, uint8_t mux, - bool ssl = false, int timeout_s = 75) - { - uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; + bool ssl = false, int timeout_s = 75) { + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; if (ssl) { sendAT(GF("+CIPSSLSIZE=4096")); waitResponse(); } sendAT(GF("+CIPSTART="), mux, ',', ssl ? GF("\"SSL") : GF("\"TCP"), - GF("\",\""), host, GF("\","), port, GF(","), TINY_GSM_TCP_KEEP_ALIVE); - // TODO: Check mux - int rsp = waitResponse(timeout_ms, - GFP(GSM_OK), - GFP(GSM_ERROR), + GF("\",\""), host, GF("\","), port, GF(","), + TINY_GSM_TCP_KEEP_ALIVE); + // TODO(?): Check mux + int rsp = waitResponse(timeout_ms, GFP(GSM_OK), GFP(GSM_ERROR), GF("ALREADY CONNECT")); - // if (rsp == 3) waitResponse(); // May return "ERROR" after the "ALREADY CONNECT" + // if (rsp == 3) waitResponse(); + // May return "ERROR" after the "ALREADY CONNECT" return (1 == rsp); } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len); - if (waitResponse(GF(">")) != 1) { - return 0; - } - stream.write((uint8_t*)buff, len); + if (waitResponse(GF(">")) != 1) { return 0; } + stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(10000L, GF(GSM_NL "SEND OK" GSM_NL)) != 1) { - return 0; - } + if (waitResponse(10000L, GF(GSM_NL "SEND OK" GSM_NL)) != 1) { return 0; } return len; } + size_t modemRead(size_t, uint8_t) { + return 0; + } + size_t modemGetAvailable(uint8_t) { + return 0; + } + bool modemGetConnected(uint8_t mux) { sendAT(GF("+CIPSTATUS")); - if (waitResponse(3000, GF("STATUS:")) != 1) return false; + if (waitResponse(3000, GF("STATUS:")) != 1) { return false; } int status = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5")); if (status != 3) { @@ -399,14 +394,15 @@ protected: } 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)); + 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(','); // 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; } @@ -418,19 +414,16 @@ protected: return verified_connections[mux]; } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -438,15 +431,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -463,25 +456,27 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 5; goto finish; } else if (data.endsWith(GF("+IPD,"))) { - int mux = stream.readStringUntil(',').toInt(); - int len = stream.readStringUntil(':').toInt(); + int mux = stream.readStringUntil(',').toInt(); + int len = stream.readStringUntil(':').toInt(); int len_orig = len; if (len > sockets[mux]->rx.free()) { - DBG("### Buffer overflow: ", len, "received vs", sockets[mux]->rx.free(), "available"); + DBG("### Buffer overflow: ", len, "received vs", + sockets[mux]->rx.free(), "available"); } else { DBG("### Got Data: ", len, "on", mux); } - while (len--) { - TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT - } - if (len_orig > sockets[mux]->available()) { // TODO - DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); + while (len--) { moveCharFromStreamToFifo(mux); } + // TODO(SRGDamia1): deal with buffer overflow/missed characters + if (len_orig > sockets[mux]->available()) { + DBG("### Fewer characters received than expected: ", + sockets[mux]->available(), " vs ", len_orig); } data = ""; } else if (data.endsWith(GF("CLOSED"))) { - int muxStart = max(0,data.lastIndexOf(GSM_NL, data.length()-8)); + int muxStart = + TinyGsmMax(0, data.lastIndexOf(GSM_NL, data.length() - 8)); int coma = data.indexOf(',', muxStart); - int mux = data.substring(muxStart, coma).toInt(); + int mux = data.substring(muxStart, coma).toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } @@ -490,38 +485,36 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } 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=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + 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) { 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=NULL, 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; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientESP8266* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTESP8266_H_ diff --git a/src/TinyGsmClientM590.h b/src/TinyGsmClientM590.h index ba6851c..3cc3edd 100644 --- a/src/TinyGsmClientM590.h +++ b/src/TinyGsmClientM590.h @@ -6,31 +6,23 @@ * @date Nov 2016 */ -#ifndef TinyGsmClientM590_h -#define TinyGsmClientM590_h -//#pragma message("TinyGSM: TinyGsmClientM590") +#ifndef SRC_TINYGSMCLIENTM590_H_ +#define SRC_TINYGSMCLIENTM590_H_ +// #pragma message("TinyGSM: TinyGsmClientM590") -//#define TINY_GSM_DEBUG Serial - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 256 -#endif +// #define TINY_GSM_DEBUG Serial #define TINY_GSM_MUX_COUNT 2 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 3, REG_DENIED = 2, @@ -39,110 +31,89 @@ enum RegStatus { REG_UNKNOWN = 4, }; - class TinyGsmM590 -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmM590; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} + : public TinyGsmModem { + friend class TinyGsmModem; - GsmClient(TinyGsmM590& modem, uint8_t mux = 1) { - init(&modem, mux); - } - - virtual ~GsmClient(){} - - bool init(TinyGsmM590* modem, uint8_t mux = 1) { - this->at = modem; - this->mux = mux; - sock_connected = false; - - at->sockets[mux] = this; - - return true; - } + /* + * Inner Client + */ + public: + class GsmClientM590 : public GsmClient { + friend class TinyGsmM590; -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, timeout_s); + public: + GsmClientM590() {} - return sock_connected; - } + explicit GsmClientM590(TinyGsmM590& modem, uint8_t mux = 1) { + init(&modem, mux); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + bool init(TinyGsmM590* modem, uint8_t mux = 1) { + this->at = modem; + this->mux = mux; + sock_connected = false; - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_YIELD(); - at->sendAT(GF("+TCPCLOSE="), mux); - sock_connected = false; - at->waitResponse(maxWaitMs); - rx.clear(); - } + at->sockets[mux] = this; - virtual void stop() { stop(1000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() + void stop(uint32_t maxWaitMs) { + TINY_GSM_YIELD(); + at->sendAT(GF("+TCPCLOSE="), mux); + sock_connected = false; + at->waitResponse(maxWaitMs); + rx.clear(); + } + void stop() override { + stop(1000L); + } -TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() + /* + * Extended API + */ -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; /* - * Extended API + * Constructor */ - - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; - -private: - TinyGsmM590* at; - uint8_t mux; - bool sock_connected; - RxFifo rx; -}; - - -public: - - TinyGsmM590(Stream& stream) - : stream(stream) - { + public: + explicit TinyGsmM590(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } - - // sendAT(GF("&FZ")); // Factory + Reset - // waitResponse(); + if (!testAT()) { return false; } sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } #ifdef TINY_GSM_DEBUG sendAT(GF("+CMEE=2")); // turn on verbose error codes @@ -158,76 +129,63 @@ public: 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 { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { + // Doesn't support CGMI + String getModemNameImpl() { return "Neoway M590"; } -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_LISTEN() - - bool factoryDefault() { + // Extra stuff here - pwr save, internal stack + bool factoryDefaultImpl() { sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write waitResponse(); - sendAT(GF("+ICF=3,1")); // 8 data 0 parity 1 stop + sendAT(GF("+ICF=3,1")); // 8 data 0 parity 1 stop waitResponse(); - sendAT(GF("+ENPWRSAVE=0")); // Disable PWR save + sendAT(GF("+ENPWRSAVE=0")); // Disable PWR save waitResponse(); sendAT(GF("+XISP=0")); // Use internal stack waitResponse(); - sendAT(GF("&W")); // Write configuration + sendAT(GF("&W")); // Write configuration return waitResponse() == 1; } -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { + bool thisHasSSL() { return false; } - bool hasWifi() { + bool thisHasWifi() { return false; } - bool hasGPRS() { + bool thisHasGPRS() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } + protected: + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("+CFUN=15")); - if (waitResponse(10000L) != 1) { - return false; - } - //MODEM:STARTUP + if (waitResponse(10000L) != 1) { return false; } + // MODEM:STARTUP waitResponse(60000L, GF(GSM_NL "+PBREADY" GSM_NL)); return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+CPWROFF")); return waitResponse(3000L) == 1; } - bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool sleepEnable(bool enable = true) { + bool sleepEnableImpl(bool enable = true) { sendAT(GF("+ENPWRSAVE="), enable); return waitResponse() == 1; } @@ -235,54 +193,29 @@ TINY_GSM_MODEM_GET_INFO_ATI() /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - -TINY_GSM_MODEM_GET_SIMCCID_CCID() - -TINY_GSM_MODEM_GET_IMEI_GSN() - - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - 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")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; - } - -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) - -TINY_GSM_MODEM_GET_OPERATOR_COPS() + protected: + // Able to follow all SIM card functions as inherited from the template /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CREG"); + } -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { gprsDisconnect(); sendAT(GF("+XISP=0")); @@ -292,44 +225,42 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() waitResponse(); if (!user) user = ""; - if (!pwd) pwd = ""; + if (!pwd) pwd = ""; sendAT(GF("+XGAUTH=1,1,\""), user, GF("\",\""), pwd, GF("\"")); waitResponse(); sendAT(GF("+XIIC=1")); waitResponse(); - const unsigned long timeout_ms = 60000L; - for (unsigned long start = millis(); millis() - start < timeout_ms; ) { + const uint32_t timeout_ms = 60000L; + for (uint32_t start = millis(); millis() - start < timeout_ms;) { if (isGprsConnected()) { - //goto set_dns; // TODO + // goto set_dns; // TODO return true; } delay(500); } return false; -// set_dns: // TODO -// sendAT(GF("+DNSSERVER=1,8.8.8.8")); -// waitResponse(); -// -// sendAT(GF("+DNSSERVER=2,8.8.4.4")); -// waitResponse(); + // set_dns: // TODO + // sendAT(GF("+DNSSERVER=1,8.8.8.8")); + // waitResponse(); + // + // sendAT(GF("+DNSSERVER=2,8.8.4.4")); + // waitResponse(); return true; } - bool gprsDisconnect() { - // TODO: There is no command in AT command set + bool gprsDisconnectImpl() { + // TODO(?): There is no command in AT command set // XIIC=0 does not work return true; } - bool isGprsConnected() { + bool isGprsConnectedImpl() { sendAT(GF("+XIIC?")); - if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { - return false; - } + if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { return false; } int res = stream.readStringUntil(',').toInt(); waitResponse(); return res == 1; @@ -338,12 +269,10 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * IP Address functions */ - - String getLocalIP() { + protected: + String getLocalIPImpl() { sendAT(GF("+XIIC?")); - if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { return ""; } stream.readStringUntil(','); String res = stream.readStringUntil('\n'); waitResponse(); @@ -351,103 +280,64 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * Phone Call functions */ - - bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE; - - bool callAnswer() TINY_GSM_ATTR_NOT_AVAILABLE; - - bool callNumber(const String& number) TINY_GSM_ATTR_NOT_AVAILABLE; - - bool callHangup() TINY_GSM_ATTR_NOT_AVAILABLE; + protected: + bool callAnswerImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool callNumberImpl(const String& number) TINY_GSM_ATTR_NOT_AVAILABLE; + bool callHangupImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool + dtmfSendImpl(char cmd, int duration_ms = 100) TINY_GSM_ATTR_NOT_AVAILABLE; /* * Messaging functions */ - - String sendUSSD(const String& code) { - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("D"), code); - if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) { - return ""; - } - stream.readStringUntil('"'); - String hex = stream.readStringUntil('"'); - stream.readStringUntil(','); - int dcs = stream.readStringUntil('\n').toInt(); - - if (waitResponse() != 1) { - return ""; - } - - if (dcs == 15) { - return TinyGsmDecodeHex8bit(hex); - } else if (dcs == 72) { - return TinyGsmDecodeHex16bit(hex); - } else { - return hex; - } - } - - bool sendSMS(const String& number, const String& text) { - sendAT(GF("+CSCS=\"GSM\"")); - waitResponse(); - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - - bool sendSMS_UTF16(const String& number, const void* text, size_t len) - TINY_GSM_ATTR_NOT_AVAILABLE; + protected: + bool sendSMS_UTF16Impl(const String& number, const void* text, + size_t len) TINY_GSM_ATTR_NOT_AVAILABLE; /* * Location functions */ + protected: + String getGsmLocationImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * GPS location functions + */ + public: + // No functions of this type available + /* + * Time functions + */ + protected: + // Can follow the standard CCLK function in the template /* * Battery & temperature functions */ - - uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; - 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; - float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; + protected: + uint16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + int8_t getBattPercentImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE; + float getTemperatureImpl() TINY_GSM_ATTR_NOT_AVAILABLE; /* * Client related functions */ - -protected: - - bool modemConnect(const char* host, uint16_t port, uint8_t mux, int timeout_s = 75) { - uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; - for (int i=0; i<3; i++) { // TODO: no need for loop? + protected: + bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool, + int timeout_s = 75) { + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + for (int i = 0; i < 3; i++) { // TODO(?): no need for loop? String ip = dnsIpQuery(host); sendAT(GF("+TCPSETUP="), mux, GF(","), ip, GF(","), port); - int rsp = waitResponse(timeout_ms, - GF(",OK" GSM_NL), - GF(",FAIL" GSM_NL), - GF("+TCPSETUP:Error" GSM_NL)); + int rsp = waitResponse(timeout_ms, GF(",OK" GSM_NL), GF(",FAIL" GSM_NL), + GF("+TCPSETUP:Error" GSM_NL)); if (1 == rsp) { return true; } else if (3 == rsp) { @@ -461,50 +351,49 @@ protected: int16_t modemSend(const void* buff, size_t len, uint8_t mux) { sendAT(GF("+TCPSEND="), mux, ',', (uint16_t)len); - if (waitResponse(GF(">")) != 1) { - return 0; - } - stream.write((uint8_t*)buff, len); - stream.write((char)0x0D); + if (waitResponse(GF(">")) != 1) { return 0; } + stream.write(reinterpret_cast(buff), len); + stream.write(static_cast(0x0D)); stream.flush(); - if (waitResponse(30000L, GF(GSM_NL "+TCPSEND:")) != 1) { - return 0; - } + if (waitResponse(30000L, GF(GSM_NL "+TCPSEND:")) != 1) { return 0; } stream.readStringUntil('\n'); return len; } + size_t modemRead(size_t, uint8_t) { + return 0; + } + size_t modemGetAvailable(uint8_t) { + return 0; + } + bool modemGetConnected(uint8_t mux) { sendAT(GF("+CIPSTATUS="), mux); - int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\"")); + int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), + GF(",\"CLOSING\""), GF(",\"INITIAL\"")); waitResponse(); return 1 == res; } String dnsIpQuery(const char* host) { sendAT(GF("+DNS=\""), host, GF("\"")); - if (waitResponse(10000L, GF(GSM_NL "+DNS:")) != 1) { - return ""; - } + if (waitResponse(10000L, GF(GSM_NL "+DNS:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(GF("+DNS:OK" GSM_NL)); res.trim(); return res; } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -512,15 +401,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -537,19 +426,19 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 5; goto finish; } else if (data.endsWith(GF("+TCPRECV:"))) { - int mux = stream.readStringUntil(',').toInt(); - int len = stream.readStringUntil(',').toInt(); + int mux = stream.readStringUntil(',').toInt(); + int len = stream.readStringUntil(',').toInt(); int len_orig = len; if (len > sockets[mux]->rx.free()) { DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free()); } else { DBG("### Got: ", len, "->", sockets[mux]->rx.free()); } - while (len--) { - TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT - } - if (len_orig > sockets[mux]->available()) { // TODO - DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig); + while (len--) { moveCharFromStreamToFifo(mux); } + // TODO(?): Handle lost characters + if (len_orig > sockets[mux]->available()) { + DBG("### Fewer characters received than expected: ", + sockets[mux]->available(), " vs ", len_orig); } data = ""; } else if (data.endsWith(GF("+TCPCLOSE:"))) { @@ -563,38 +452,36 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } 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=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + 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) { 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=NULL, 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; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientM590* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTM590_H_ diff --git a/src/TinyGsmClientM95.h b/src/TinyGsmClientM95.h index 752d8e2..d983775 100644 --- a/src/TinyGsmClientM95.h +++ b/src/TinyGsmClientM95.h @@ -2,35 +2,28 @@ * @file TinyGsmClientM95.h * @author Volodymyr Shymanskyy, Pacman Pereira, and Replicade Ltd. * @license LGPL-3.0 - * @copyright Copyright (c) 2016 Volodymyr Shymanskyy, (c)2017 Replicade Ltd. + * @copyright Copyright (c) 2016 Volodymyr Shymanskyy, (c)2017 Replicade Ltd. + * * @date Nov 2016 */ -#ifndef TinyGsmClientM95_h -#define TinyGsmClientM95_h -//#pragma message("TinyGSM: TinyGsmClientM95") +#ifndef SRC_TINYGSMCLIENTM95_H_ +#define SRC_TINYGSMCLIENTM95_H_ +// #pragma message("TinyGSM: TinyGsmClientM95") -//#define TINY_GSM_DEBUG Serial - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 64 -#endif +// #define TINY_GSM_DEBUG Serial #define TINY_GSM_MUX_COUNT 6 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,132 +32,120 @@ enum RegStatus { REG_UNKNOWN = 4, }; +class TinyGsmM95 + : public TinyGsmModem { + friend class TinyGsmModem; -class TinyGsmM95: public TinyGsmUTFSMS -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmM95; - typedef TinyGsmFifo RxFifo; + /* + * Inner Client + */ + public: + class GsmClientM95 : public GsmClient { + friend class TinyGsmM95; -public: - GsmClient() {} + public: + GsmClientM95() {} - GsmClient(TinyGsmM95& modem, uint8_t mux = 1) { - init(&modem, mux); - } + explicit GsmClientM95(TinyGsmM95& modem, uint8_t mux = 1) { + init(&modem, mux); + } - virtual ~GsmClient(){} + bool init(TinyGsmM95* modem, uint8_t mux = 1) { + this->at = modem; + this->mux = mux; + sock_available = 0; + sock_connected = false; - bool init(TinyGsmM95* modem, uint8_t mux = 1) { - this->at = modem; - this->mux = mux; - sock_available = 0; - sock_connected = false; + at->sockets[mux] = this; - at->sockets[mux] = this; + return true; + } - return true; - } + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, false, timeout_s); - return sock_connected; - } + void stop(uint32_t maxWaitMs) { + uint32_t startMillis = millis(); + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+QICLOSE="), mux); + sock_connected = false; + at->waitResponse((maxWaitMs - (millis() - startMillis)), GF("CLOSED"), + GF("CLOSE OK"), GF("ERROR")); + } + void stop() override { + stop(75000L); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + /* + * Extended API + */ - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() - at->sendAT(GF("+QICLOSE="), mux); - sock_connected = false; - at->waitResponse((maxWaitMs - (millis() - startMillis)), GF("CLOSED"), GF("CLOSE OK"), GF("ERROR")); - } + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; - virtual void stop() { stop(75000L); } + /* + * Inner Secure Client + */ -TINY_GSM_CLIENT_WRITE() + /* + class GsmClientSecureM95 : public GsmClientM95 + { + public: + GsmClientSecure() {} -TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() + GsmClientSecure(TinyGsmm95& modem, uint8_t mux = 1) + : GsmClient(modem, mux) + {} -TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + public: + virtual int connect(const char *host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + }; + */ /* - * Extended API + * Constructor */ - - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; - -private: - TinyGsmM95* at; - uint8_t mux; - uint16_t sock_available; - bool sock_connected; - RxFifo rx; -}; - - -// class GsmClientSecure : public GsmClient -// { -// public: -// GsmClientSecure() {} -// -// GsmClientSecure(TinyGsmm95& modem, uint8_t mux = 1) -// : GsmClient(modem, mux) -// {} -// -// virtual ~GsmClientSecure() {} -// -// public: -// virtual int connect(const char *host, uint16_t port, int timeout_s) { -// stop(); -// TINY_GSM_YIELD(); -// rx.clear(); -// sock_connected = at->modemConnect(host, port, mux, true, timeout_s); -// return sock_connected; -// } -// }; - - -public: - - TinyGsmM95(Stream& stream) - : stream(stream) - { + public: + explicit TinyGsmM95(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } + if (!testAT()) { return false; } // sendAT(GF("&FZ")); // Factory + Reset // waitResponse(); sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } #ifdef TINY_GSM_DEBUG sendAT(GF("+CMEE=2")); // turn on verbose error codes @@ -175,62 +156,43 @@ public: DBG(GF("### Modem:"), getModemName()); + // Enable network time synchronization + sendAT(GF("+QNITZ=1")); + if (waitResponse(10000L) != 1) { return false; } + 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 { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { - return "Quectel M95"; + bool thisHasSSL() { + return false; // TODO(?): Add SSL support } -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_LISTEN() - - bool factoryDefault() { - sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write - waitResponse(); - sendAT(GF("+IPR=0")); // Auto-baud - waitResponse(); - sendAT(GF("&W")); // Write configuration - return waitResponse() == 1; - } - -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { - return false; // TODO: For now - } - - bool hasWifi() { + bool thisHasWifi() { return false; } - bool hasGPRS() { + bool thisHasGPRS() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } + protected: + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("+CFUN=0")); - if (waitResponse(10000L, GF("NORMAL POWER DOWN"), GF("OK"), GF("FAIL")) == 3) { + if (waitResponse(10000L, GF("NORMAL POWER DOWN"), GF("OK"), GF("FAIL")) == + 3) { return false; } sendAT(GF("+CFUN=1")); @@ -240,77 +202,51 @@ TINY_GSM_MODEM_GET_INFO_ATI() return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+QPOWD=1")); return waitResponse(300, GF("NORMAL POWER DOWN")) == 1; } - bool radioOff() { - sendAT(GF("+CFUN=0")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000); - return true; + // When entering into sleep mode is enabled, DTR is pulled up, and WAKEUP_IN + // is pulled up, the module can directly enter into sleep mode.If entering + // into sleep mode is enabled, DTR is pulled down, and WAKEUP_IN is pulled + // down, there is a need to pull the DTR pin and the WAKEUP_IN pin up first, + // and then the module can enter into sleep mode. + bool sleepEnableImpl(bool enable = true) { + sendAT(GF("+QSCLK="), enable); + return waitResponse() == 1; } - bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; - /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - - String getSimCCID() { + protected: + String getSimCCIDImpl() { sendAT(GF("+QCCID")); - if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); return res; } -TINY_GSM_MODEM_GET_IMEI_GSN() - - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - 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")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; - } - -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) - -TINY_GSM_MODEM_GET_OPERATOR_COPS() - /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CREG"); + } -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } - void setHostFormat( bool useDottedQuad ) { - if ( useDottedQuad ) { + public: + void setHostFormat(bool useDottedQuad) { + if (useDottedQuad) { sendAT(GF("+QIDNSIP=0")); } else { sendAT(GF("+QIDNSIP=1")); @@ -318,63 +254,47 @@ TINY_GSM_MODEM_GET_CSQ() waitResponse(); } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { gprsDisconnect(); // select foreground context 0 = VIRTUAL_UART_1 sendAT(GF("+QIFGCNT=0")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } - //Select GPRS (=1) as the Bearer - sendAT(GF("+QICSGP=1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\"")); - if (waitResponse() != 1) { - return false; - } + // Select GPRS (=1) as the Bearer + sendAT(GF("+QICSGP=1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, + GF("\"")); + if (waitResponse() != 1) { return false; } // Select TCP/IP transfer mode - NOT transparent mode sendAT(GF("+QIMODE=0")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } // Enable multiple TCP/IP connections sendAT(GF("+QIMUX=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } - //Start TCPIP Task and Set APN, User Name and Password - sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"" ); - if (waitResponse() != 1) { - return false; - } + // Start TCPIP Task and Set APN, User Name and Password + sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\""); + if (waitResponse() != 1) { return false; } - //Activate GPRS/CSD Context + // Activate GPRS/CSD Context sendAT(GF("+QIACT")); - if (waitResponse(60000L) != 1) { - return false; - } + if (waitResponse(60000L) != 1) { return false; } // Check that we have a local IP address - if (localIP() == IPAddress(0,0,0,0)) { - return false; - } + 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; - } + if (waitResponse() != 1) { return false; } // // Request an IP header for received data // // "IPD(data length):" @@ -390,7 +310,8 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() // return false; // } // - // // Do NOT show the protocol type at the end of the header for received data + // // 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) { @@ -407,18 +328,16 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() return true; } - bool gprsDisconnect() { - sendAT(GF("+QIDEACT")); + bool gprsDisconnectImpl() { + sendAT(GF("+QIDEACT")); // Deactivate the bearer context return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; } -TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() - /* * IP Address functions */ - - String getLocalIP() { + protected: + String getLocalIPImpl() { sendAT(GF("+QILOCIP")); stream.readStringUntil('\n'); String res = stream.readStringUntil('\n'); @@ -426,147 +345,59 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } + /* + * Phone Call functions + */ + protected: + // Can follow all of the phone call functions from the template /* * Messaging functions */ + protected: + // Can follow all template functions - String sendUSSD(const String& code) { - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("+CUSD=1,\""), code, GF("\"")); - if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) { - return ""; - } - stream.readStringUntil('"'); - String hex = stream.readStringUntil('"'); - stream.readStringUntil(','); - int dcs = stream.readStringUntil('\n').toInt(); - - if (waitResponse() != 1) { - return ""; - } - - if (dcs == 15) { - return TinyGsmDecodeHex8bit(hex); - } else if (dcs == 72) { - return TinyGsmDecodeHex16bit(hex); - } else { - return hex; - } - } - - bool sendSMS(const String& number, const String& text) { - sendAT(GF("+CMGF=1")); - waitResponse(); - //Set GSM 7 bit default alphabet (3GPP TS 23.038) - sendAT(GF("+CSCS=\"GSM\"")); - waitResponse(); - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - + public: /** Delete all SMS */ bool deleteAllSMS() { sendAT(GF("+QMGDA=6")); - if (waitResponse(waitResponse(60000L, GF("OK"), GF("ERROR")) == 1) ) { + if (waitResponse(waitResponse(60000L, GF("OK"), GF("ERROR")) == 1)) { return true; } return false; } /* - * Phone Call functions + * Location functions */ - - bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE; - bool callAnswer() TINY_GSM_ATTR_NOT_AVAILABLE; - bool callNumber(const String& number) TINY_GSM_ATTR_NOT_AVAILABLE; - bool callHangup() TINY_GSM_ATTR_NOT_AVAILABLE; + protected: + String getGsmLocationImpl() TINY_GSM_ATTR_NOT_AVAILABLE; /* - * Location functions + * GPS location functions */ + public: + // No functions of this type supported - String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * Time functions + */ + protected: + // Can follow the standard CCLK function in the template /* * Battery & temperature functions */ - - // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip battery charge status - streamSkipUntil(','); // Skip battery charge level - // return voltage in mV - uint16_t res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - int8_t getBattPercent() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - streamSkipUntil(','); // Skip battery charge status - // Read battery charge level - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - uint8_t getBattChargeState() { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - // Read battery charge status - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - chargeState = stream.readStringUntil(',').toInt(); - percent = stream.readStringUntil(',').toInt(); - milliVolts = stream.readStringUntil('\n').toInt(); - // Wait for final OK - waitResponse(); - return true; - } - - float getTemperature() { + protected: + float getTemperatureImpl() { sendAT(GF("+QTEMP")); if (waitResponse(GF(GSM_NL "+QTEMP:")) != 1) { - return (float)-9999; + return static_cast(-9999); } - streamSkipUntil(','); // Skip mode + streamSkipUntil(','); // Skip mode // Read charge of thermistor // milliVolts = stream.readStringUntil(',').toInt(); - streamSkipUntil(','); // Skip thermistor charge + streamSkipUntil(','); // Skip thermistor charge float temp = stream.readStringUntil('\n').toFloat(); // Wait for final OK waitResponse(); @@ -576,33 +407,25 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() /* * Client related functions */ - -protected: - - 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); + protected: + 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); } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { sendAT(GF("+QISEND="), mux, ',', (uint16_t)len); - if (waitResponse(GF(">")) != 1) { - return 0; - } - stream.write((uint8_t*)buff, len); + if (waitResponse(GF(">")) != 1) { return 0; } + stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(GF(GSM_NL "SEND OK")) != 1) { - return 0; - } + if (waitResponse(GF(GSM_NL "SEND OK")) != 1) { return 0; } // bool allAcknowledged = false; // // bool failed = false; @@ -621,11 +444,11 @@ protected: // } // waitResponse(5000L); - return len; // TODO + return len; // TODO(?): get len/ack properly } size_t modemRead(size_t size, uint8_t mux) { - // TODO: Does this work???? + // TODO(?): Does this work???? // AT+QIRD=,,, // id = GPRS context number = 0, set in GPRS connect // sc = role in connection = 1, client of connection @@ -641,40 +464,43 @@ protected: 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 = len; } + for (uint16_t i = 0; i < len; i++) { + moveCharFromStreamToFifo(mux); sockets[mux]->sock_available--; - // ^^ One less character available after moving from modem's FIFO to our FIFO + // ^^ 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; + sockets[mux]->sock_available = 0; + return 0; } } + size_t modemGetAvailable(uint8_t) { + return 0; + } + bool modemGetConnected(uint8_t mux) { sendAT(GF("+QISTATE=1,"), mux); - //+QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1" + // +QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1" - if (waitResponse(GF("+QISTATE:"))) - return false; + if (waitResponse(GF("+QISTATE:")) != 1) { return false; } - streamSkipUntil(','); // Skip mux - streamSkipUntil(','); // Skip socket type - streamSkipUntil(','); // Skip remote ip - streamSkipUntil(','); // Skip remote port - streamSkipUntil(','); // Skip local port - int res = stream.readStringUntil(',').toInt(); // socket state + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip socket type + streamSkipUntil(','); // Skip remote ip + streamSkipUntil(','); // Skip remote port + streamSkipUntil(','); // Skip local port + int res = stream.readStringUntil(',').toInt(); // socket state waitResponse(); @@ -682,19 +508,16 @@ protected: return 2 == res; } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -702,15 +525,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -738,9 +561,9 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } data = ""; } else if (data.endsWith(GF("CLOSED" GSM_NL))) { - int nl = data.lastIndexOf(GSM_NL, data.length()-8); - int coma = data.indexOf(',', nl+2); - int mux = data.substring(nl+2, coma).toInt(); + int nl = data.lastIndexOf(GSM_NL, data.length() - 8); + int coma = data.indexOf(',', nl + 2); + int mux = data.substring(nl + 2, coma).toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } @@ -749,38 +572,36 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } 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=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + 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) { 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=NULL, 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: + protected: Stream& stream; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + GsmClientM95* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTM95_H_ diff --git a/src/TinyGsmClientMC60.h b/src/TinyGsmClientMC60.h index 740ea06..9681153 100644 --- a/src/TinyGsmClientMC60.h +++ b/src/TinyGsmClientMC60.h @@ -9,32 +9,23 @@ * */ -#ifndef TinyGsmClientMC60_h -#define TinyGsmClientMC60_h -//#pragma message("TinyGSM: TinyGsmClientMC60") +#ifndef SRC_TINYGSMCLIENTMC60_H_ +#define SRC_TINYGSMCLIENTMC60_H_ +// #pragma message("TinyGSM: TinyGsmClientMC60") -//#define TINY_GSM_DEBUG Serial - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 64 -#endif +// #define TINY_GSM_DEBUG Serial #define TINY_GSM_MUX_COUNT 6 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, - SIM_ANTITHEFT_LOCKED = 3, -}; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -43,178 +34,150 @@ enum RegStatus { REG_UNKNOWN = 4, }; +class TinyGsmMC60 + : public TinyGsmModem { + friend class TinyGsmModem; -class TinyGsmMC60: public TinyGsmUTFSMS -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmMC60; - typedef TinyGsmFifo RxFifo; + /* + * Inner Client + */ + public: + class GsmClientMC60 : public GsmClient { + friend class TinyGsmMC60; -public: - GsmClient() {} + public: + GsmClientMC60() {} - GsmClient(TinyGsmMC60& modem, uint8_t mux = 1) { - init(&modem, mux); - } + explicit GsmClientMC60(TinyGsmMC60& modem, uint8_t mux = 1) { + init(&modem, mux); + } - virtual ~GsmClient(){} + bool init(TinyGsmMC60* modem, uint8_t mux = 1) { + this->at = modem; + this->mux = mux; + sock_available = 0; + sock_connected = false; - bool init(TinyGsmMC60* modem, uint8_t mux = 1) { - this->at = modem; - this->mux = mux; - sock_available = 0; - sock_connected = false; + at->sockets[mux] = this; - at->sockets[mux] = this; + return true; + } - return true; - } + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, false, timeout_s); - return sock_connected; - } + void stop(uint32_t maxWaitMs) { + uint32_t startMillis = millis(); + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+QICLOSE="), mux); + sock_connected = false; + at->waitResponse((maxWaitMs - (millis() - startMillis)), GF("CLOSED"), + GF("CLOSE OK"), GF("ERROR")); + } + void stop() override { + stop(75000L); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + /* + * Extended API + */ - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() - at->sendAT(GF("+QICLOSE="), mux); - sock_connected = false; - at->waitResponse((maxWaitMs - (millis() - startMillis)), GF("CLOSED"), GF("CLOSE OK"), GF("ERROR")); - } + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; - virtual void stop() { stop(75000L); } + /* + * Inner Secure Client + */ -TINY_GSM_CLIENT_WRITE() + /* + class GsmClientSecureMC60 : public GsmClientMC60 + { + public: + GsmClientSecure() {} -TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() + GsmClientSecure(TinyGsmMC60& modem, uint8_t mux = 1) + : GsmClient(modem, mux) + {} -TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + public: + virtual int connect(const char *host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + }; + */ /* - * Extended API + * Constructor */ - - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; - -private: - TinyGsmMC60* at; - uint8_t mux; - uint16_t sock_available; - bool sock_connected; - RxFifo rx; -}; - - -// class GsmClientSecure : public GsmClient -// { -// public: -// GsmClientSecure() {} -// -// GsmClientSecure(TinyGsmMC60& modem, uint8_t mux = 1) -// : GsmClient(modem, mux) -// {} -// -// virtual ~GsmClientSecure(){} -// -// public: -// virtual int connect(const char *host, uint16_t port, int timeout_s) { -// stop(); -// TINY_GSM_YIELD(); -// rx.clear(); -// sock_connected = at->modemConnect(host, port, mux, true, timeout_s); -// return sock_connected; -// } -// }; - - -public: - - TinyGsmMC60(Stream& stream) - : stream(stream) - { + public: + explicit TinyGsmMC60(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } + if (!testAT()) { return false; } // sendAT(GF("&FZ")); // Factory + Reset // waitResponse(); - sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } + +#ifdef TINY_GSM_DEBUG + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes +#endif + waitResponse(); DBG(GF("### Modem:"), getModemName()); + // Enable network time synchronization + sendAT(GF("+QNITZ=1")); + if (waitResponse(10000L) != 1) { return false; } + 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 { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { - #if defined(TINY_GSM_MODEM_MC60) - return "Quectel MC60"; - #elif defined(TINY_GSM_MODEM_MC60E) - return "Quectel MC60E"; - #endif - return "Quectel MC60"; - } - -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_LISTEN() - - bool factoryDefault() { - sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write - waitResponse(); - sendAT(GF("+IPR=0")); // Auto-baud - waitResponse(); - sendAT(GF("&W")); // Write configuration - return waitResponse() == 1; - } - -TINY_GSM_MODEM_GET_INFO_ATI() - /* - * under development - */ - // bool hasSSL() { + * under development + */ + // bool thisHasSSL() { // sendAT(GF("+QIPSSL=?")); // if (waitResponse(GF(GSM_NL "+CIPSSL:")) != 1) { // return false; @@ -222,125 +185,106 @@ TINY_GSM_MODEM_GET_INFO_ATI() // return waitResponse() == 1; // } - bool hasSSL() { - return false; // TODO: For now + bool thisHasSSL() { + return false; // TODO(?): Add SSL support } - bool hasWifi() { + bool thisHasWifi() { return false; } - bool hasGPRS() { + bool thisHasGPRS() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } + protected: + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("+CFUN=0")); - if (waitResponse(10000L) != 1) { - return false; - } + if (waitResponse(10000L) != 1) { return false; } sendAT(GF("+CFUN=1,1")); - if (waitResponse(10000L) != 1) { - return false; - } + if (waitResponse(10000L) != 1) { return false; } delay(3000); return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+QPOWD=1")); return waitResponse(GF("NORMAL POWER DOWN")) == 1; } - bool radioOff() { - if (!testAT()) { - return false; - } - sendAT(GF("+CFUN=0")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000); - return true; + // When entering into sleep mode is enabled, DTR is pulled up, and WAKEUP_IN + // is pulled up, the module can directly enter into sleep mode.If entering + // into sleep mode is enabled, DTR is pulled down, and WAKEUP_IN is pulled + // down, there is a need to pull the DTR pin and the WAKEUP_IN pin up first, + // and then the module can enter into sleep mode. + bool sleepEnableImpl(bool enable = true) { + sendAT(GF("+QSCLK="), enable); + return waitResponse() == 1; } - bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; - /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - -TINY_GSM_MODEM_GET_SIMCCID_CCID() - -TINY_GSM_MODEM_GET_IMEI_GSN() - - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - for (unsigned long start = millis(); millis() - start < timeout_ms; ) { + protected: + SimStatus getSimStatusImpl(uint32_t timeout_ms = 10000L) { + for (uint32_t 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"), GF("PH_SIM PIN"), GF("PH_SIM PUK")); + int status = + waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), + GF("NOT INSERTED"), GF("PH_SIM PIN"), GF("PH_SIM PUK")); waitResponse(); switch (status) { case 2: - case 3: return SIM_LOCKED; + case 3: return SIM_LOCKED; case 5: - case 6: return SIM_ANTITHEFT_LOCKED; - case 1: return SIM_READY; + case 6: return SIM_ANTITHEFT_LOCKED; + case 1: return SIM_READY; default: return SIM_ERROR; } } return SIM_ERROR; } -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) - -TINY_GSM_MODEM_GET_OPERATOR_COPS() - /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CREG"); + } -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { gprsDisconnect(); // select foreground context 0 = VIRTUAL_UART_1 sendAT(GF("+QIFGCNT=0")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } - //Select GPRS (=1) as the Bearer - sendAT(GF("+QICSGP=1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\"")); - if (waitResponse() != 1) { - return false; - } + // Select GPRS (=1) as the Bearer + sendAT(GF("+QICSGP=1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, + GF("\"")); + if (waitResponse() != 1) { return false; } - //Define PDP context - is this necessary? + // Define PDP context - is this necessary? sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"'); waitResponse(); @@ -350,56 +294,42 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() // Select TCP/IP transfer mode - NOT transparent mode sendAT(GF("+QIMODE=0")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } // Enable multiple TCP/IP connections sendAT(GF("+QIMUX=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } - //Start TCPIP Task and Set APN, User Name and Password - sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"" ); - if (waitResponse() != 1) { - return false; - } + // Start TCPIP Task and Set APN, User Name and Password + sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\""); + if (waitResponse() != 1) { return false; } - //Activate GPRS/CSD Context + // Activate GPRS/CSD Context sendAT(GF("+QIACT")); - if (waitResponse(60000L) != 1) { - return false; - } + if (waitResponse(60000L) != 1) { return false; } // Check that we have a local IP address - if (localIP() == IPAddress(0,0,0,0)) { - return false; - } + if (localIP() == IPAddress(0, 0, 0, 0)) { return false; } - //Set Method to Handle Received TCP/IP Data + // 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; - } + if (waitResponse() != 1) { return false; } return true; } - bool gprsDisconnect() { - sendAT(GF("+QIDEACT")); + bool gprsDisconnectImpl() { + sendAT(GF("+QIDEACT")); // Deactivate the bearer context return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; } -TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() - /* * IP Address functions */ - - String getLocalIP() { + protected: + String getLocalIPImpl() { sendAT(GF("+QILOCIP")); stream.readStringUntil('\n'); String res = stream.readStringUntil('\n'); @@ -407,61 +337,23 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } + /* + * Phone Call functions + */ + protected: + // Can follow all of the phone call functions from the template /* * Messaging functions */ + protected: + // Can follow all template functions - String sendUSSD(const String& code) { - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("+CUSD=1,\""), code, GF("\"")); - if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) { - return ""; - } - stream.readStringUntil('"'); - String hex = stream.readStringUntil('"'); - stream.readStringUntil(','); - int dcs = stream.readStringUntil('\n').toInt(); - - if (waitResponse() != 1) { - return ""; - } - - if (dcs == 15) { - return TinyGsmDecodeHex8bit(hex); - } else if (dcs == 72) { - return TinyGsmDecodeHex16bit(hex); - } else { - return hex; - } - } - - bool sendSMS(const String& number, const String& text) { - sendAT(GF("+CMGF=1")); - waitResponse(); - //Set GSM 7 bit default alphabet (3GPP TS 23.038) - sendAT(GF("+CSCS=\"GSM\"")); - waitResponse(); - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - + public: /** Delete all SMS */ bool deleteAllSMS() { sendAT(GF("+QMGDA=6")); - if (waitResponse(waitResponse(60000L, GF("OK"), GF("ERROR")) == 1) ) { + if (waitResponse(waitResponse(60000L, GF("OK"), GF("ERROR")) == 1)) { return true; } return false; @@ -470,118 +362,60 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() /* * Location functions */ - - String getGsmLocation() { - sendAT(GF("+CIPGSMLOC=1,1")); - if (waitResponse(10000L, GF(GSM_NL "+CIPGSMLOC:")) != 1) { - return ""; - } - String res = stream.readStringUntil('\n'); - waitResponse(); - res.trim(); - return res; - } + protected: + // Can use CIPGSMLOC as inherited from the template /* - * Battery & temperature functions + * GPS location functions */ + public: + // No functions of this type supported - // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip battery charge status - streamSkipUntil(','); // Skip battery charge level - // return voltage in mV - uint16_t res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - int8_t getBattPercent() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - streamSkipUntil(','); // Skip battery charge status - // Read battery charge level - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - uint8_t getBattChargeState() { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - // Read battery charge status - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - chargeState = stream.readStringUntil(',').toInt(); - percent = stream.readStringUntil(',').toInt(); - milliVolts = stream.readStringUntil('\n').toInt(); - // Wait for final OK - waitResponse(); - return true; - } + /* + * Time functions + */ + protected: + // Can follow the standard CCLK function in the template - float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * Battery & temperature functions + */ + protected: + float getTemperatureImpl() TINY_GSM_ATTR_NOT_AVAILABLE; /* * Client related functions */ - -protected: - + protected: 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)); + 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); } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { sendAT(GF("+QISEND="), mux, ',', (uint16_t)len); - if (waitResponse(GF(">")) != 1) { - return 0; - } - stream.write((uint8_t*)buff, len); + if (waitResponse(GF(">")) != 1) { return 0; } + stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(GF(GSM_NL "SEND OK")) != 1) { - return 0; - } + if (waitResponse(GF(GSM_NL "SEND OK")) != 1) { return 0; } bool allAcknowledged = false; // bool failed = false; - while ( !allAcknowledged ) { - sendAT( GF("+QISACK")); + 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 ) { + if (stream.readStringUntil('\n').toInt() == 0) { allAcknowledged = true; } } @@ -591,16 +425,16 @@ protected: // streamSkipUntil(','); // Skip mux // return stream.readStringUntil('\n').toInt(); - return len; // TODO + return len; // TODO(?): verify len/ack } size_t modemRead(size_t size, uint8_t mux) { - // TODO: Does this work???? + // 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 + // 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. @@ -615,36 +449,38 @@ protected: // 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 = len; } + for (uint16_t i = 0; i < len; i++) { + moveCharFromStreamToFifo(mux); sockets[mux]->sock_available--; - // ^^ One less character available after moving from modem's FIFO to our FIFO + // ^^ One less character available after moving from modem's FIFO to our + // FIFO } - waitResponse(); + waitResponse(); // ends with an OK DBG("### READ:", len, "from", mux); return len; } else { - sockets[mux]->sock_available = 0; - return 0; + sockets[mux]->sock_available = 0; + return 0; } } + size_t modemGetAvailable(uint8_t) { + return 0; + } + bool modemGetConnected(uint8_t mux) { sendAT(GF("+QISTATE=1,"), mux); - //+QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1" + // +QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1" - if (waitResponse(GF("+QISTATE:"))) - return false; + if (waitResponse(GF("+QISTATE:")) != 1) { return false; } - streamSkipUntil(','); // Skip mux - streamSkipUntil(','); // Skip socket type - streamSkipUntil(','); // Skip remote ip - streamSkipUntil(','); // Skip remote port - streamSkipUntil(','); // Skip local port - int res = stream.readStringUntil(',').toInt(); // socket state + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip socket type + streamSkipUntil(','); // Skip remote ip + streamSkipUntil(','); // Skip remote port + streamSkipUntil(','); // Skip local port + int res = stream.readStringUntil(',').toInt(); // socket state waitResponse(); @@ -652,19 +488,16 @@ protected: return 2 == res; } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL) - { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL, GsmConstStr r6 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -673,15 +506,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r6s(r6); r6s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s, ",", r6s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -700,7 +533,8 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } else if (r6 && data.endsWith(r6)) { index = 6; goto finish; - } else if (data.endsWith(GF(GSM_NL "+QIRD:"))) { // TODO: QIRD? or QIRDI? + } else if (data.endsWith( + GF(GSM_NL "+QIRD:"))) { // TODO(?): QIRD? or QIRDI? // +QIRDI: ,,,,,< tlen> streamSkipUntil(','); // Skip the context streamSkipUntil(','); // Skip the role @@ -711,14 +545,14 @@ TINY_GSM_MODEM_STREAM_UTILITIES() // 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]->sock_available = len_packet*num_packets; + sockets[mux]->sock_available = len_packet * num_packets; } data = ""; - DBG("### Got Data:", len, "on", mux); + DBG("### Got Data:", len_packet * num_packets, "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); - int mux = data.substring(nl+2, coma).toInt(); + int nl = data.lastIndexOf(GSM_NL, data.length() - 8); + int coma = data.indexOf(',', nl + 2); + int mux = data.substring(nl + 2, coma).toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } @@ -727,38 +561,37 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } 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=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL) - { + 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 r6 = NULL) { String data; return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5, r6); } - uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=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, GsmConstStr r6 = NULL) { return waitResponse(1000, r1, r2, r3, r4, r5, r6); } -public: - Stream& stream; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientMC60* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTMC60_H_ diff --git a/src/TinyGsmClientSIM5360.h b/src/TinyGsmClientSIM5360.h index e306a2e..e598dfc 100644 --- a/src/TinyGsmClientSIM5360.h +++ b/src/TinyGsmClientSIM5360.h @@ -6,31 +6,23 @@ * @date Nov 2016 */ -#ifndef TinyGsmClientSIM5360_h -#define TinyGsmClientSIM5360_h +#ifndef SRC_TINYGSMCLIENTSIM5360_H_ +#define SRC_TINYGSMCLIENTSIM5360_H_ // #define TINY_GSM_DEBUG Serial -//#define TINY_GSM_USE_HEX - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 64 -#endif +// #define TINY_GSM_USE_HEX #define TINY_GSM_MUX_COUNT 10 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,140 +31,148 @@ enum RegStatus { REG_UNKNOWN = 4, }; -enum TinyGSMDateTimeFormat { - DATE_FULL = 0, - DATE_TIME = 1, - DATE_DATE = 2 -}; - -class TinyGsmSim5360: public TinyGsmUTFSMS -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmSim5360; - typedef TinyGsmFifo RxFifo; +class TinyGsmSim5360 : public TinyGsmModem { + friend class TinyGsmModem; -public: - GsmClient() {} - - GsmClient(TinyGsmSim5360& modem, uint8_t mux = 0) { - init(&modem, mux); - } - - virtual ~GsmClient(){} - - bool init(TinyGsmSim5360* modem, uint8_t mux = 0) { - this->at = modem; - this->mux = mux; - sock_available = 0; - prev_check = 0; - sock_connected = false; - got_data = false; + /* + * Inner Client + */ + public: + class GsmClientSim5360 : public GsmClient { + friend class TinyGsmSim5360; - at->sockets[mux] = this; + public: + GsmClientSim5360() {} - return true; - } + explicit GsmClientSim5360(TinyGsmSim5360& modem, uint8_t mux = 0) { + init(&modem, mux); + } -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, false, timeout_s); - return sock_connected; - } + bool init(TinyGsmSim5360* modem, uint8_t mux = 0) { + this->at = modem; + this->mux = mux; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + at->sockets[mux] = this; - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() - at->sendAT(GF("+CIPCLOSE="), mux); - sock_connected = false; - at->waitResponse(); - } - - virtual void stop() { stop(15000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() + void stop(uint32_t maxWaitMs) { + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+CIPCLOSE="), mux); + sock_connected = false; + at->waitResponse(); + } + void stop() override { + stop(15000L); + } -TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() + /* + * Extended API + */ -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; /* - * Extended API + * Inner Secure Client */ - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; - -private: - TinyGsmSim5360* at; - uint8_t mux; - uint16_t sock_available; - uint32_t prev_check; - bool sock_connected; - bool got_data; - RxFifo rx; -}; + // TODO(?): Add SSL support + /* + class GsmClientSecureSim5360 : public GsmClientSim5360 { + public: + GsmClientSecureSim5360() {} + explicit GsmClientSecureSim5360(TinyGsmSim5360& modem, uint8_t mux = 1) + : GsmClientSim5360(modem, mux) {} -public: + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + }; + */ - TinyGsmSim5360(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmSim5360(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } + if (!testAT()) { return false; } - sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } + +#ifdef TINY_GSM_DEBUG + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes +#endif + waitResponse(); DBG(GF("### Modem:"), getModemName()); + // Enable automatic time zome update + sendAT(GF("+CTZU=1")); + if (waitResponse(10000L) != 1) { return false; } + 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 { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { - String name = "SIMCom SIM5360"; + String getModemNameImpl() { + String name = "SIMCom SIM5360"; sendAT(GF("+CGMM")); String res2; - if (waitResponse(1000L, res2) != 1) { - return name; - } + if (waitResponse(1000L, res2) != 1) { return name; } res2.replace(GSM_NL "OK" GSM_NL, ""); res2.replace("_", " "); res2.trim(); @@ -182,61 +182,47 @@ public: return name; } -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() - - bool factoryDefault() { // these commands aren't supported + bool factoryDefaultImpl() { // these commands aren't supported return false; } -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { - return false; // TODO: Module supports SSL, but not yet implemented + bool thisHasSSL() { + return false; // TODO(>): Module supports SSL, but not yet implemented } - bool hasWifi() { + bool thisHasWifi() { return false; } - bool hasGPRS() { + bool thisHasGPRS() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } + protected: + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("+REBOOT")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000L); // TODO: Test this delay! + if (waitResponse(10000L) != 1) { return false; } + delay(3000L); // TODO(?): Test this delay! return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+CPOF")); return waitResponse() == 1; } - bool radioOff() { + bool radioOffImpl() { sendAT(GF("+CFUN=4")); - if (waitResponse(10000L) != 1) { - return false; - } + if (waitResponse(10000L) != 1) { return false; } delay(3000); return true; } - bool sleepEnable(bool enable = true) { + bool sleepEnableImpl(bool enable = true) { sendAT(GF("+CSCLK="), enable); return waitResponse() == 1; } @@ -244,85 +230,54 @@ TINY_GSM_MODEM_GET_INFO_ATI() /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - -// Gets the CCID of a sim card via AT+CCID - String getSimCCID() { + protected: + // Gets the CCID of a sim card via AT+CCID + String getSimCCIDImpl() { sendAT(GF("+CICCID")); - if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); return res; } -TINY_GSM_MODEM_GET_IMEI_GSN() - - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - 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")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; - } - -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG) - -TINY_GSM_MODEM_GET_OPERATOR_COPS() - /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CGREG"); + } -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } + public: String getNetworkModes() { sendAT(GF("+CNMP=?")); - if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); return res; } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - String setNetworkMode(uint8_t mode) { - sendAT(GF("+CNMP="), mode); - if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { - return "OK"; - } + sendAT(GF("+CNMP="), mode); + if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return "OK"; } String res = stream.readStringUntil('\n'); waitResponse(); return res; } - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { gprsDisconnect(); // Make sure we're not connected first // Define the PDP context @@ -336,7 +291,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } // Define external PDP context 1 - sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"',",\"0.0.0.0\",0,0"); + sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"', ",\"0.0.0.0\",0,0"); waitResponse(); // The CGSOCKCONT commands define the "embedded" PDP context for TCP/IP @@ -367,21 +322,24 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() waitResponse(); // Configure socket parameters - //AT+CIPCCFG= [][,[][,[][,[][,]][,[[][,[]]]]]]]] - // NmRetry = number of retransmission to be made for an IP packet = 10 (default) - // DelayTm = number of milliseconds to delay to output data of Receiving = 0 (default) + // AT+CIPCCFG= , , , , , + // , + // NmRetry = number of retransmission to be made for an IP packet + // = 10 (default) + // DelayTm = number of milliseconds to delay before outputting received data + // = 0 (default) // Ack = sets whether reporting a string “Send ok” = 0 (don't report) // errMode = mode of reporting error result code = 0 (numberic values) - // HeaderType = which data header of receiving data in multi-client mode = 1 (“+RECEIVE,,”) - // AsyncMode = sets mode of executing commands = 0 (synchronous command executing) + // HeaderType = which data header of receiving data in multi-client mode + // = 1 (“+RECEIVE,,”) + // AsyncMode = sets mode of executing commands + // = 0 (synchronous command executing) // TimeoutVal = minimum retransmission timeout in milliseconds = 75000 sendAT(GF("+CIPCCFG=10,0,0,0,1,0,75000")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } // Configure timeouts for opening and closing sockets - // AT+CIPTIMEOUT=[][, [][, []]] + // AT+CIPTIMEOUT=, , sendAT(GF("+CIPTIMEOUT="), 75000, ',', 15000, ',', 15000); waitResponse(); @@ -393,45 +351,36 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() // We to ignore any immediate response and wait for the // URC to show it's really connected. sendAT(GF("+NETOPEN")); - if (waitResponse(75000L, GF(GSM_NL "+NETOPEN: 0")) != 1) { - return false; - } + if (waitResponse(75000L, GF(GSM_NL "+NETOPEN: 0")) != 1) { return false; } return true; } - bool gprsDisconnect() { + bool gprsDisconnectImpl() { // Close any open sockets for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { - GsmClient *sock = sockets[mux]; - if (sock) { - sock->stop(); - } + GsmClientSim5360* sock = sockets[mux]; + if (sock) { sock->stop(); } } // Stop the socket service - // Note: all sockets should be closed first - on 3G/4G models the sockets must be closed manually + // Note: all sockets should be closed first - on 3G/4G models the sockets + // must be closed manually sendAT(GF("+NETCLOSE")); - if (waitResponse(60000L, GF(GSM_NL "+NETCLOSE: 0")) != 1) { - return false; - } + if (waitResponse(60000L, GF(GSM_NL "+NETCLOSE: 0")) != 1) { return false; } return true; } - bool isGprsConnected() { + bool isGprsConnectedImpl() { sendAT(GF("+NETOPEN?")); // May return +NETOPEN: 1, 0. We just confirm that the first number is 1 - if (waitResponse(GF(GSM_NL "+NETOPEN: 1")) != 1) { - return false; - } + if (waitResponse(GF(GSM_NL "+NETOPEN: 1")) != 1) { return false; } waitResponse(); - sendAT(GF("+IPADDR")); // Inquire Socket PDP address + sendAT(GF("+IPADDR")); // Inquire Socket PDP address // sendAT(GF("+CGPADDR=1")); // Show PDP address - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } @@ -439,175 +388,92 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * IP Address functions */ - - String getLocalIP() { + protected: + String getLocalIPImpl() { sendAT(GF("+IPADDR")); // Inquire Socket PDP address // sendAT(GF("+CGPADDR=1")); // Show PDP address String res; - if (waitResponse(10000L, res) != 1) { - return ""; - } + if (waitResponse(10000L, res) != 1) { return ""; } res.replace(GSM_NL "OK" GSM_NL, ""); res.replace(GSM_NL, ""); res.trim(); return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * Phone Call functions */ - - bool setGsmBusy() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool callNumber() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool dtmfSend() TINY_GSM_ATTR_NOT_IMPLEMENTED; + protected: + bool callAnswerImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool callNumberImpl(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool callHangupImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool + dtmfSendImpl(char cmd, int duration_ms = 100) TINY_GSM_ATTR_NOT_IMPLEMENTED; /* * Messaging functions */ - - String sendUSSD(const String& code) { - // Select message format (1=text) - sendAT(GF("+CMGF=1")); - waitResponse(); - // Select TE character set - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("+CUSD=1,\""), code, GF("\"")); - if (waitResponse() != 1) { - return ""; - } - if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) { - return ""; - } - stream.readStringUntil('"'); - String hex = stream.readStringUntil('"'); - stream.readStringUntil(','); - int dcs = stream.readStringUntil('\n').toInt(); - - if (dcs == 15) { - return TinyGsmDecodeHex8bit(hex); - } else if (dcs == 72) { - return TinyGsmDecodeHex16bit(hex); - } else { - return hex; - } - } - - bool sendSMS(const String& number, const String& text) { - // Get SMS service centre address - sendAT(GF("+AT+CSCA?")); - waitResponse(); - // Select message format (1=text) - sendAT(GF("+CMGF=1")); - waitResponse(); - //Set GSM 7 bit default alphabet (3GPP TS 23.038) - sendAT(GF("+CSCS=\"GSM\"")); - waitResponse(); - // Send the message! - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - + protected: + // Follows all messaging functions per template /* * Location functions */ - - String getGsmLocation() TINY_GSM_ATTR_NOT_IMPLEMENTED; + protected: + String getGsmLocationImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; /* * GPS location functions */ + public: + // No functions of this type supported /* * Time functions */ + protected: + // Can follow the standard CCLK function in the template /* * Battery & temperature functions */ - - // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() { + protected: + uint16_t getBattVoltageImpl() { sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip battery charge status - streamSkipUntil(','); // Skip battery charge level + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return 0; } + streamSkipUntil(','); // Skip battery charge status + streamSkipUntil(','); // Skip battery charge level // get voltage in VOLTS float voltage = stream.readStringUntil('\n').toFloat(); // Wait for final OK waitResponse(); // Return millivolts - uint16_t res = voltage*1000; + uint16_t res = voltage * 1000; return res; } - int8_t getBattPercent() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - streamSkipUntil(','); // Skip battery charge status - // Read battery charge level - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - uint8_t getBattChargeState() { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - // Read battery charge status - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) { sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return false; } chargeState = stream.readStringUntil(',').toInt(); - percent = stream.readStringUntil(',').toInt(); + percent = stream.readStringUntil(',').toInt(); // get voltage in VOLTS float voltage = stream.readStringUntil('\n').toFloat(); - milliVolts = voltage*1000; + milliVolts = voltage * 1000; // Wait for final OK waitResponse(); return true; } // get temperature in degree celsius - float getTemperature() { + float getTemperatureImpl() { // Enable Temparature Reading sendAT(GF("+CMTE=1")); - if (waitResponse() != 1) { - return 0; - } + if (waitResponse() != 1) { return 0; } // Get Temparature Value sendAT(GF("+CMTE?")); - if (waitResponse(GF(GSM_NL "+CMTE:")) != 1) { - return false; - } + if (waitResponse(GF(GSM_NL "+CMTE:")) != 1) { return false; } float res = stream.readStringUntil('\n').toFloat(); // Wait for final OK waitResponse(); @@ -617,75 +483,67 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * Client related functions */ - -protected: - + protected: 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; - } - - if (ssl) { - DBG("SSL not yet supported on this module!"); - } + 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); + 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; - } + 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, ',', (uint16_t)len); - if (waitResponse(GF(">")) != 1) { - return 0; - } - stream.write((uint8_t*)buff, len); + if (waitResponse(GF(">")) != 1) { return 0; } + stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(GF(GSM_NL "+CIPSEND:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip mux - streamSkipUntil(','); // Skip requested bytes to send - // TODO: make sure requested and confirmed bytes match + if (waitResponse(GF(GSM_NL "+CIPSEND:")) != 1) { return 0; } + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip requested bytes to send + // TODO(?): make sure requested and confirmed bytes match return stream.readStringUntil('\n').toInt(); } size_t modemRead(size_t size, uint8_t mux) { #ifdef TINY_GSM_USE_HEX sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CIPRXGET:")) != 1) { - return 0; - } + if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #else sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CIPRXGET:")) != 1) { - return 0; - } + if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #endif - streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX - streamSkipUntil(','); // Skip mux/cid (connecion id) + streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX + streamSkipUntil(','); // Skip mux/cid (connecion id) int len_requested = stream.readStringUntil(',').toInt(); // ^^ Requested number of data bytes (1-1460 bytes)to be read int len_confirmed = stream.readStringUntil('\n').toInt(); // ^^ The data length which not read in the buffer - for (int i=0; i_timeout)) { TINY_GSM_YIELD(); } - char buf[4] = { 0, }; + while (stream.available() < 2 && + (millis() - startMillis < sockets[mux]->_timeout)) { + TINY_GSM_YIELD(); + } + char buf[4] = { + 0, + }; buf[0] = stream.read(); buf[1] = stream.read(); char c = strtol(buf, NULL, 16); #else - while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } + while (!stream.available() && + (millis() - startMillis < sockets[mux]->_timeout)) { + TINY_GSM_YIELD(); + } char c = stream.read(); #endif sockets[mux]->rx.put(c); @@ -701,25 +559,21 @@ protected: sendAT(GF("+CIPRXGET=4,"), mux); size_t result = 0; if (waitResponse(GF("+CIPRXGET:")) == 1) { - streamSkipUntil(','); // Skip mode 4 - streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip mode 4 + streamSkipUntil(','); // Skip mux result = stream.readStringUntil('\n').toInt(); waitResponse(); } DBG("### Available:", result, "on", mux); - if (!result) { - sockets[mux]->sock_connected = modemGetConnected(mux); - } + if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); } return result; } bool modemGetConnected(uint8_t mux) { // Read the status of all sockets at once sendAT(GF("+CIPCLOSE?")); - if (waitResponse(GF("+CIPCLOSE:")) != 1) { - return false; - } - for (int muxNo = 0; muxNo <= TINY_GSM_MUX_COUNT; muxNo++) { + if (waitResponse(GF("+CIPCLOSE:")) != 1) { return false; } + for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) { // +CIPCLOSE:,,..., sockets[muxNo]->sock_connected = stream.parseInt(); } @@ -727,19 +581,16 @@ protected: return sockets[mux]->sock_connected; } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -747,15 +598,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -787,7 +638,7 @@ TINY_GSM_MODEM_STREAM_UTILITIES() int mux = stream.readStringUntil(',').toInt(); int len = stream.readStringUntil('\n').toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; + sockets[mux]->got_data = true; sockets[mux]->sock_available = len; } data = ""; @@ -804,45 +655,41 @@ TINY_GSM_MODEM_STREAM_UTILITIES() // Need to close all open sockets and release the network library. // User will then need to reconnect. DBG("### Network error!"); - if (!isGprsConnected()) { - gprsDisconnect(); - } + if (!isGprsConnected()) { gprsDisconnect(); } data = ""; } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } 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=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + 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) { 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=NULL, 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; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientSim5360* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTSIM5360_H_ diff --git a/src/TinyGsmClientSIM7000.h b/src/TinyGsmClientSIM7000.h index 5f56091..a9390d7 100644 --- a/src/TinyGsmClientSIM7000.h +++ b/src/TinyGsmClientSIM7000.h @@ -6,31 +6,23 @@ * @date Nov 2016 */ -#ifndef TinyGsmClientSIM7000_h -#define TinyGsmClientSIM7000_h +#ifndef SRC_TINYGSMCLIENTSIM7000_H_ +#define SRC_TINYGSMCLIENTSIM7000_H_ // #define TINY_GSM_DEBUG Serial -//#define TINY_GSM_USE_HEX - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 64 -#endif +// #define TINY_GSM_USE_HEX #define TINY_GSM_MUX_COUNT 8 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,164 +31,146 @@ enum RegStatus { REG_UNKNOWN = 4, }; -enum TinyGSMDateTimeFormat { - DATE_FULL = 0, - DATE_TIME = 1, - DATE_DATE = 2 -}; - -class TinyGsmSim7000: public TinyGsmUTFSMS -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmSim7000; - typedef TinyGsmFifo RxFifo; +class TinyGsmSim7000 : public TinyGsmModem { + friend class TinyGsmModem; -public: - GsmClient() {} - - GsmClient(TinyGsmSim7000& modem, uint8_t mux = 1) { - init(&modem, mux); - } - - virtual ~GsmClient(){} - - bool init(TinyGsmSim7000* modem, uint8_t mux = 1) { - this->at = modem; - this->mux = mux; - sock_available = 0; - prev_check = 0; - sock_connected = false; - got_data = false; + /* + * Inner Client + */ + public: + class GsmClientSim7000 : public GsmClient { + friend class TinyGsmSim7000; - at->sockets[mux] = this; + public: + GsmClientSim7000() {} - return true; - } + explicit GsmClientSim7000(TinyGsmSim7000& modem, uint8_t mux = 1) { + init(&modem, mux); + } -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, false, timeout_s); - return sock_connected; - } + bool init(TinyGsmSim7000* modem, uint8_t mux = 1) { + this->at = modem; + this->mux = mux; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + at->sockets[mux] = this; - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() - at->sendAT(GF("+CIPCLOSE="), mux); - sock_connected = false; - at->waitResponse(); - } - - virtual void stop() { stop(15000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() + void stop(uint32_t maxWaitMs) { + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+CIPCLOSE="), mux); + sock_connected = false; + at->waitResponse(); + } + void stop() override { + stop(15000L); + } -TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() + /* + * Extended API + */ -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; /* - * Extended API + * Inner Secure Client */ - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; - -private: - TinyGsmSim7000* at; - uint8_t mux; - uint16_t sock_available; - uint32_t prev_check; - bool sock_connected; - bool got_data; - RxFifo rx; -}; - - -/*TODO! -class GsmClientSecure : public GsmClient -{ -public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmSim7000& modem, uint8_t mux = 1) - : GsmClient(modem, mux) - {} - - virtual ~GsmClientSecure(){} - -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, true, timeout_s); - return sock_connected; - } -}; -*/ + /*TODO(?)) + class GsmClientSecureSIM7000 : public GsmClientSim7000 + { + public: + GsmClientSecure() {} + GsmClientSecure(TinyGsmSim7000& modem, uint8_t mux = 1) + : public GsmClient(modem, mux) + {} -public: + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + }; + */ - TinyGsmSim7000(Stream& stream) - : stream(stream) - { + public: + explicit TinyGsmSim7000(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } + if (!testAT()) { return false; } - sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } + +#ifdef TINY_GSM_DEBUG + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes +#endif + waitResponse(); DBG(GF("### Modem:"), getModemName()); + // Enable Local Time Stamp for getting network time + sendAT(GF("+CLTS=1")); + if (waitResponse(10000L) != 1) { return false; } + 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 { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { - String name = "SIMCom SIM7000"; + String getModemNameImpl() { + String name = "SIMCom SIM7000"; sendAT(GF("+GMM")); String res2; - if (waitResponse(1000L, res2) != 1) { - return name; - } + if (waitResponse(1000L, res2) != 1) { return name; } res2.replace(GSM_NL "OK" GSM_NL, ""); res2.replace("_", " "); res2.trim(); @@ -206,77 +180,45 @@ public: return name; } -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() - - bool factoryDefault() { // these commands aren't supported + bool factoryDefaultImpl() { // these commands aren't supported return false; } -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { - return false; // TODO: Module supports SSL, but not yet implemented + bool thisHasSSL() { + return false; // TODO(?): Module supports SSL, but not yet implemented } - bool hasWifi() { + bool thisHasWifi() { return false; } - bool hasGPRS() { + bool thisHasGPRS() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } - //Enable Local Time Stamp for getting network time - // TODO: Find a better place for this - sendAT(GF("+CLTS=1")); - if (waitResponse(10000L) != 1) { - return false; - } + protected: + bool restartImpl() { sendAT(GF("+CFUN=0")); - if (waitResponse(10000L) != 1) { - return false; - } + if (waitResponse(10000L) != 1) { return false; } sendAT(GF("+CFUN=1,1")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000); //TODO: Test this delay + if (waitResponse(10000L) != 1) { return false; } + delay(3000); // TODO(SRGDamia1): Test this delay return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+CPOWD=1")); return waitResponse(GF("NORMAL POWER DOWN")) == 1; } - bool radioOff() { - sendAT(GF("+CFUN=0")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000); - return true; - } - - /* - During sleep, the SIM7000 module has its serial communication disabled. - In order to reestablish communication pull the DRT-pin of the SIM7000 module - LOW for at least 50ms. Then use this function to disable sleep mode. - The DTR-pin can then be released again. - */ - bool sleepEnable(bool enable = true) { + // During sleep, the SIM7000 module has its serial communication disabled. + // In order to reestablish communication pull the DRT-pin of the SIM7000 + // 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) { sendAT(GF("+CSCLK="), enable); return waitResponse() == 1; } @@ -284,64 +226,35 @@ TINY_GSM_MODEM_GET_INFO_ATI() /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - -TINY_GSM_MODEM_GET_SIMCCID_CCID() - -TINY_GSM_MODEM_GET_IMEI_GSN() - - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - 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")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; - } - -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG) - -TINY_GSM_MODEM_GET_OPERATOR_COPS() + protected: + // Able to follow all SIM card functions as inherited from the template /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CGREG"); + } -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } + public: String getNetworkModes() { sendAT(GF("+CNMP=?")); - if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); return res; } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - String setNetworkMode(uint8_t mode) { - sendAT(GF("+CNMP="), mode); - if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { - return "OK"; - } + sendAT(GF("+CNMP="), mode); + if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return "OK"; } String res = stream.readStringUntil('\n'); waitResponse(); return res; @@ -349,9 +262,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() String getPreferredModes() { sendAT(GF("+CMNB=?")); - if (waitResponse(GF(GSM_NL "+CMNB:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+CMNB:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); return res; @@ -359,24 +270,23 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() String setPreferredMode(uint8_t mode) { sendAT(GF("+CMNB="), mode); - if (waitResponse(GF(GSM_NL "+CMNB:")) != 1) { - return "OK"; - } + if (waitResponse(GF(GSM_NL "+CMNB:")) != 1) { return "OK"; } String res = stream.readStringUntil('\n'); waitResponse(); return res; } - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { gprsDisconnect(); // Set the Bearer for the IP - sendAT(GF("+SAPBR=3,1,\"Contype\",\"GPRS\"")); // Set the connection type to GPRS + sendAT(GF( + "+SAPBR=3,1,\"Contype\",\"GPRS\"")); // Set the connection type to GPRS waitResponse(); sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"'); // Set the APN @@ -404,81 +314,49 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() waitResponse(85000L); // Query the GPRS bearer context status sendAT(GF("+SAPBR=2,1")); - if (waitResponse(30000L) != 1) - return false; + if (waitResponse(30000L) != 1) { return false; } // Attach to GPRS sendAT(GF("+CGATT=1")); - if (waitResponse(60000L) != 1) - return false; + if (waitResponse(60000L) != 1) { return false; } - // TODO: wait AT+CGATT? + // TODO(?): wait AT+CGATT? // Set to multi-IP sendAT(GF("+CIPMUX=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } // Put in "quick send" mode (thus no extra "Send OK") sendAT(GF("+CIPQSEND=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } // Set to get data manually sendAT(GF("+CIPRXGET=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } // Start Task and Set APN, USER NAME, PASSWORD sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\"")); - if (waitResponse(60000L) != 1) { - return false; - } + if (waitResponse(60000L) != 1) { return false; } // Bring Up Wireless Connection with GPRS or CSD sendAT(GF("+CIICR")); - if (waitResponse(60000L) != 1) { - return false; - } + if (waitResponse(60000L) != 1) { return false; } // Get Local IP Address, only assigned after connection sendAT(GF("+CIFSR;E0")); - if (waitResponse(10000L) != 1) { - return false; - } + if (waitResponse(10000L) != 1) { return false; } return true; } - bool gprsDisconnect() { + bool gprsDisconnectImpl() { // Shut the TCP/IP connection + // CIPSHUT will close *all* open connections sendAT(GF("+CIPSHUT")); - if (waitResponse(60000L) != 1) - return false; + if (waitResponse(60000L) != 1) { return false; } sendAT(GF("+CGATT=0")); // Deactivate the bearer context - if (waitResponse(60000L) != 1) - return false; - - return true; - } - - bool isGprsConnected() { - sendAT(GF("+CGATT?")); - if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { - return false; - } - int res = stream.readStringUntil('\n').toInt(); - waitResponse(); - if (res != 1) - return false; - - sendAT(GF("+CIFSR;E0")); // Another option is to use AT+CGPADDR=1 - if (waitResponse() != 1) - return false; + if (waitResponse(60000L) != 1) { return false; } return true; } @@ -486,126 +364,60 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * IP Address functions */ - - String getLocalIP() { + protected: + String getLocalIPImpl() { sendAT(GF("+CIFSR;E0")); String res; - if (waitResponse(10000L, res) != 1) { - return ""; - } + if (waitResponse(10000L, res) != 1) { return ""; } res.replace(GSM_NL "OK" GSM_NL, ""); res.replace(GSM_NL, ""); res.trim(); return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * Phone Call functions */ - - bool setGsmBusy() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool callNumber() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool dtmfSend() TINY_GSM_ATTR_NOT_IMPLEMENTED; + protected: + bool callAnswerImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool callNumberImpl(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool callHangupImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool + dtmfSendImpl(char cmd, int duration_ms = 100) TINY_GSM_ATTR_NOT_IMPLEMENTED; /* * Messaging functions */ - - String sendUSSD(const String& code) { - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("+CUSD=1,\""), code, GF("\"")); - if (waitResponse() != 1) { - return ""; - } - if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) { - return ""; - } - stream.readStringUntil('"'); - String hex = stream.readStringUntil('"'); - stream.readStringUntil(','); - int dcs = stream.readStringUntil('\n').toInt(); - - if (dcs == 15) { - return TinyGsmDecodeHex8bit(hex); - } else if (dcs == 72) { - return TinyGsmDecodeHex16bit(hex); - } else { - return hex; - } - } - - bool sendSMS(const String& number, const String& text) { - - sendAT(GF("+AT+CSCA?")); - waitResponse(); - sendAT(GF("+CMGF=1")); - waitResponse(); - //Set GSM 7 bit default alphabet (3GPP TS 23.038) - sendAT(GF("+CSCS=\"GSM\"")); - waitResponse(); - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - + protected: + // Follows all messaging functions per template /* * Location functions */ - - String getGsmLocation() { - sendAT(GF("+CIPGSMLOC=1,1")); - if (waitResponse(10000L, GF(GSM_NL "+CIPGSMLOC:")) != 1) { - return ""; - } - String res = stream.readStringUntil('\n'); - waitResponse(); - res.trim(); - return res; - } - + protected: + // Can return a location from CIPGSMLOC as per the template /* * GPS location functions */ - + public: // enable GPS bool enableGPS() { sendAT(GF("+CGNSPWR=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } bool disableGPS() { sendAT(GF("+CGNSPWR=0")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } // get the RAW GPS output String getGPSraw() { sendAT(GF("+CGNSINF")); - if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -613,22 +425,21 @@ 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) { - //String buffer = ""; + bool getGPS(float* lat, float* lon, float* speed = 0, int* alt = 0, + int* vsat = 0, int* usat = 0) { + // String buffer = ""; bool fix = false; sendAT(GF("+CGNSINF")); - if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { - return false; - } - - stream.readStringUntil(','); // mode - if ( stream.readStringUntil(',').toInt() == 1 ) fix = true; - stream.readStringUntil(','); //utctime - *lat = stream.readStringUntil(',').toFloat(); //lat - *lon = stream.readStringUntil(',').toFloat(); //lon - if (alt != NULL) *alt = stream.readStringUntil(',').toFloat(); //lon - if (speed != NULL) *speed = stream.readStringUntil(',').toFloat(); //speed + if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { return false; } + + stream.readStringUntil(','); // mode + if (stream.readStringUntil(',').toInt() == 1) fix = true; + stream.readStringUntil(','); // utctime + *lat = stream.readStringUntil(',').toFloat(); // lat + *lon = stream.readStringUntil(',').toFloat(); // lon + if (alt != NULL) *alt = stream.readStringUntil(',').toFloat(); // lon + if (speed != NULL) *speed = stream.readStringUntil(',').toFloat(); // speed stream.readStringUntil(','); stream.readStringUntil(','); stream.readStringUntil(','); @@ -636,8 +447,10 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() stream.readStringUntil(','); stream.readStringUntil(','); stream.readStringUntil(','); - if (vsat != NULL) *vsat = stream.readStringUntil(',').toInt(); //viewed satelites - if (usat != NULL) *usat = stream.readStringUntil(',').toInt(); //used satelites + if (vsat != NULL) + *vsat = stream.readStringUntil(',').toInt(); // viewed satelites + if (usat != NULL) + *usat = stream.readStringUntil(',').toInt(); // used satelites stream.readStringUntil('\n'); waitResponse(); @@ -648,59 +461,33 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * Time functions */ - - String getGSMDateTime(TinyGSMDateTimeFormat format) { - sendAT(GF("+CCLK?")); - if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) { - return ""; - } - - String res; - - switch(format) { - case DATE_FULL: - res = stream.readStringUntil('"'); - break; - case DATE_TIME: - streamSkipUntil(','); - res = stream.readStringUntil('"'); - break; - case DATE_DATE: - res = stream.readStringUntil(','); - break; - } - return res; - } - + public: // get GPS time - bool getGPSTime(int *year, int *month, int *day, int *hour, int *minute, int *second) { + bool getGPSTime(int* year, int* month, int* day, int* hour, int* minute, + int* second) { bool fix = false; char chr_buffer[12]; sendAT(GF("+CGNSINF")); - if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { - return false; - } + if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { return false; } for (int i = 0; i < 3; i++) { String buffer = stream.readStringUntil(','); buffer.toCharArray(chr_buffer, sizeof(chr_buffer)); switch (i) { case 0: - //mode + // mode break; case 1: - //fixstatus - if ( buffer.toInt() == 1 ) { - fix = buffer.toInt(); - } + // fixstatus + if (buffer.toInt() == 1) { fix = buffer.toInt(); } break; case 2: - *year = buffer.substring(0,4).toInt(); - *month = buffer.substring(4,6).toInt(); - *day = buffer.substring(6,8).toInt(); - *hour = buffer.substring(8,10).toInt(); - *minute = buffer.substring(10,12).toInt(); - *second = buffer.substring(12,14).toInt(); + *year = buffer.substring(0, 4).toInt(); + *month = buffer.substring(4, 6).toInt(); + *day = buffer.substring(6, 8).toInt(); + *hour = buffer.substring(8, 10).toInt(); + *minute = buffer.substring(10, 12).toInt(); + *second = buffer.substring(12, 14).toInt(); break; default: @@ -709,7 +496,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() break; } } - String res = stream.readStringUntil('\n'); + stream.readStringUntil('\n'); waitResponse(); if (fix) { @@ -722,130 +509,72 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * Battery & temperature functions */ - - // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip battery charge status - streamSkipUntil(','); // Skip battery charge level - // return voltage in mV - uint16_t res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - int8_t getBattPercent() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - streamSkipUntil(','); // Skip battery charge status - // Read battery charge level - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - uint8_t getBattChargeState() { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - // Read battery charge status - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - chargeState = stream.readStringUntil(',').toInt(); - percent = stream.readStringUntil(',').toInt(); - milliVolts = stream.readStringUntil('\n').toInt(); - // Wait for final OK - waitResponse(); - return true; - } - - float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; + protected: + float getTemperatureImpl() TINY_GSM_ATTR_NOT_AVAILABLE; /* * Client related functions */ - -protected: - - 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); + protected: + 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, ',', (uint16_t)len); - if (waitResponse(GF(">")) != 1) { - return 0; - } - stream.write((uint8_t*)buff, len); + if (waitResponse(GF(">")) != 1) { return 0; } + stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(GF(GSM_NL "DATA ACCEPT:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip mux + if (waitResponse(GF(GSM_NL "DATA ACCEPT:")) != 1) { return 0; } + streamSkipUntil(','); // Skip mux return stream.readStringUntil('\n').toInt(); } size_t modemRead(size_t size, uint8_t mux) { #ifdef TINY_GSM_USE_HEX sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CIPRXGET:")) != 1) { - return 0; - } + if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #else sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CIPRXGET:")) != 1) { - return 0; - } + if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #endif - streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX - streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX + streamSkipUntil(','); // Skip mux int len_requested = stream.readStringUntil(',').toInt(); // ^^ Requested number of data bytes (1-1460 bytes)to be read 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 (int i=0; i_timeout)) { TINY_GSM_YIELD(); } - char buf[4] = { 0, }; + while (stream.available() < 2 && + (millis() - startMillis < sockets[mux]->_timeout)) { + TINY_GSM_YIELD(); + } + char buf[4] = { + 0, + }; buf[0] = stream.read(); buf[1] = stream.read(); char c = strtol(buf, NULL, 16); #else - while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } + while (!stream.available() && + (millis() - startMillis < sockets[mux]->_timeout)) { + TINY_GSM_YIELD(); + } char c = stream.read(); #endif sockets[mux]->rx.put(c); @@ -861,38 +590,34 @@ protected: sendAT(GF("+CIPRXGET=4,"), mux); size_t result = 0; if (waitResponse(GF("+CIPRXGET:")) == 1) { - streamSkipUntil(','); // Skip mode 4 - streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip mode 4 + streamSkipUntil(','); // Skip mux result = stream.readStringUntil('\n').toInt(); waitResponse(); } DBG("### Available:", result, "on", mux); - if (!result) { - sockets[mux]->sock_connected = modemGetConnected(mux); - } + if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); } return result; } bool modemGetConnected(uint8_t mux) { sendAT(GF("+CIPSTATUS="), mux); - int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\"")); + int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), + GF(",\"CLOSING\""), GF(",\"INITIAL\"")); waitResponse(); return 1 == res; } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -900,15 +625,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -940,15 +665,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() int mux = stream.readStringUntil(',').toInt(); int len = stream.readStringUntil('\n').toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; + sockets[mux]->got_data = true; sockets[mux]->sock_available = len; } 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); - int mux = data.substring(nl+2, coma).toInt(); + int nl = data.lastIndexOf(GSM_NL, data.length() - 8); + int coma = data.indexOf(',', nl + 2); + int mux = data.substring(nl + 2, coma).toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } @@ -957,38 +682,36 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } 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=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + 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) { 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=NULL, 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; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientSim7000* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTSIM7000_H_ diff --git a/src/TinyGsmClientSIM7600.h b/src/TinyGsmClientSIM7600.h index c1ec12f..e747268 100644 --- a/src/TinyGsmClientSIM7600.h +++ b/src/TinyGsmClientSIM7600.h @@ -6,31 +6,23 @@ * @date Nov 2016 */ -#ifndef TinyGsmClientSIM7600_h -#define TinyGsmClientSIM7600_h +#ifndef SRC_TINYGSMCLIENTSIM7600_H_ +#define SRC_TINYGSMCLIENTSIM7600_H_ // #define TINY_GSM_DEBUG Serial -//#define TINY_GSM_USE_HEX - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 64 -#endif +// #define TINY_GSM_USE_HEX #define TINY_GSM_MUX_COUNT 10 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,140 +31,124 @@ enum RegStatus { REG_UNKNOWN = 4, }; -enum TinyGSMDateTimeFormat { - DATE_FULL = 0, - DATE_TIME = 1, - DATE_DATE = 2 -}; - -class TinyGsmSim7600: public TinyGsmUTFSMS -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmSim7600; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} +class TinyGsmSim7600 : public TinyGsmModem { + friend class TinyGsmModem; - GsmClient(TinyGsmSim7600& modem, uint8_t mux = 0) { - init(&modem, mux); - } - - virtual ~GsmClient(){} - - bool init(TinyGsmSim7600* modem, uint8_t mux = 0) { - this->at = modem; - this->mux = mux; - sock_available = 0; - prev_check = 0; - sock_connected = false; - got_data = false; - - at->sockets[mux] = this; + /* + * Inner Client + */ + public: + class GsmClientSim7600 : public GsmClient { + friend class TinyGsmSim7600; - return true; - } + public: + GsmClientSim7600() {} -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, false, timeout_s); - return sock_connected; - } + explicit GsmClientSim7600(TinyGsmSim7600& modem, uint8_t mux = 0) { + init(&modem, mux); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + bool init(TinyGsmSim7600* modem, uint8_t mux = 0) { + this->at = modem; + this->mux = mux; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() - at->sendAT(GF("+CIPCLOSE="), mux); - sock_connected = false; - at->waitResponse(); - } + at->sockets[mux] = this; - virtual void stop() { stop(15000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() + void stop(uint32_t maxWaitMs) { + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+CIPCLOSE="), mux); + sock_connected = false; + at->waitResponse(); + } + void stop() override { + stop(15000L); + } -TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() + /* + * Extended API + */ -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; /* - * Extended API + * Constructor */ - - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; - -private: - TinyGsmSim7600* at; - uint8_t mux; - uint16_t sock_available; - uint32_t prev_check; - bool sock_connected; - bool got_data; - RxFifo rx; -}; - - -public: - - TinyGsmSim7600(Stream& stream) - : stream(stream) - { + public: + explicit TinyGsmSim7600(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } + if (!testAT()) { return false; } - sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } + +#ifdef TINY_GSM_DEBUG + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes +#endif + waitResponse(); DBG(GF("### Modem:"), getModemName()); + // Enable automatic time zome update + sendAT(GF("+CTZU=1")); + if (waitResponse(10000L) != 1) { return false; } + 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 { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { - String name = "SIMCom SIM7600"; + String getModemNameImpl() { + String name = "SIMCom SIM7600"; sendAT(GF("+CGMM")); String res2; - if (waitResponse(1000L, res2) != 1) { - return name; - } + if (waitResponse(1000L, res2) != 1) { return name; } res2.replace(GSM_NL "OK" GSM_NL, ""); res2.replace("_", " "); res2.trim(); @@ -182,61 +158,47 @@ public: return name; } -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() - - bool factoryDefault() { // these commands aren't supported + bool factoryDefaultImpl() { // these commands aren't supported return false; } -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { - return false; // TODO: Module supports SSL, but not yet implemented + bool thisHasSSL() { + return false; // TODO(?): Module supports SSL, but not yet implemented } - bool hasWifi() { + bool thisHasWifi() { return false; } - bool hasGPRS() { + bool thisHasGPRS() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } + protected: + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("+CRESET")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(5000L); // TODO: Test this delay! + if (waitResponse(10000L) != 1) { return false; } + delay(5000L); // TODO(?): Test this delay! return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+CPOF")); return waitResponse() == 1; } - bool radioOff() { + bool radioOffImpl() { sendAT(GF("+CFUN=4")); - if (waitResponse(10000L) != 1) { - return false; - } + if (waitResponse(10000L) != 1) { return false; } delay(3000); return true; } - bool sleepEnable(bool enable = true) { + bool sleepEnableImpl(bool enable = true) { sendAT(GF("+CSCLK="), enable); return waitResponse() == 1; } @@ -244,85 +206,54 @@ TINY_GSM_MODEM_GET_INFO_ATI() /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - -// Gets the CCID of a sim card via AT+CCID - String getSimCCID() { + protected: + // Gets the CCID of a sim card via AT+CCID + String getSimCCIDImpl() { sendAT(GF("+CICCID")); - if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); return res; } -TINY_GSM_MODEM_GET_IMEI_GSN() - - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - 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")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; - } - -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG) - -TINY_GSM_MODEM_GET_OPERATOR_COPS() - /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CGREG"); + } -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } + public: String getNetworkModes() { sendAT(GF("+CNMP=?")); - if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); return res; } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - String setNetworkMode(uint8_t mode) { - sendAT(GF("+CNMP="), mode); - if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { - return "OK"; - } + sendAT(GF("+CNMP="), mode); + if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return "OK"; } String res = stream.readStringUntil('\n'); waitResponse(); return res; } - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { gprsDisconnect(); // Make sure we're not connected first // Define the PDP context @@ -336,7 +267,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } // Define external PDP context 1 - sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"',",\"0.0.0.0\",0,0"); + sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"', ",\"0.0.0.0\",0,0"); waitResponse(); // Configure TCP parameters @@ -350,21 +281,24 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() waitResponse(); // Configure socket parameters - //AT+CIPCCFG= [][,[][,[][,[][,]][,[[][,[]]]]]]]] - // NmRetry = number of retransmission to be made for an IP packet = 10 (default) - // DelayTm = number of milliseconds to delay to output data of Receiving = 0 (default) + // AT+CIPCCFG= , , , , , + // , + // NmRetry = number of retransmission to be made for an IP packet + // = 10 (default) + // DelayTm = number of milliseconds to delay before outputting received data + // = 0 (default) // Ack = sets whether reporting a string “Send ok” = 0 (don't report) // errMode = mode of reporting error result code = 0 (numberic values) - // HeaderType = which data header of receiving data in multi-client mode = 1 (“+RECEIVE,,”) - // AsyncMode = sets mode of executing commands = 0 (synchronous command executing) + // HeaderType = which data header of receiving data in multi-client mode + // = 1 (“+RECEIVE,,”) + // AsyncMode = sets mode of executing commands + // = 0 (synchronous command executing) // TimeoutVal = minimum retransmission timeout in milliseconds = 75000 sendAT(GF("+CIPCCFG=10,0,0,0,1,0,75000")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } // Configure timeouts for opening and closing sockets - // AT+CIPTIMEOUT=[][, [][, []]] + // AT+CIPTIMEOUT= , sendAT(GF("+CIPTIMEOUT="), 75000, ',', 15000, ',', 15000); waitResponse(); @@ -376,38 +310,30 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() // We to ignore any immediate response and wait for the // URC to show it's really connected. sendAT(GF("+NETOPEN")); - if (waitResponse(75000L, GF(GSM_NL "+NETOPEN: 0")) != 1) { - return false; - } + if (waitResponse(75000L, GF(GSM_NL "+NETOPEN: 0")) != 1) { return false; } return true; } - bool gprsDisconnect() { - + bool gprsDisconnectImpl() { // Close all sockets and stop the socket service - // Note: On the LTE models, this single command closes all sockets and the service + // Note: On the LTE models, this single command closes all sockets and the + // service sendAT(GF("+NETCLOSE")); - if (waitResponse(60000L, GF(GSM_NL "+NETCLOSE: 0")) != 1) { - return false; - } + if (waitResponse(60000L, GF(GSM_NL "+NETCLOSE: 0")) != 1) { return false; } return true; } - bool isGprsConnected() { + bool isGprsConnectedImpl() { sendAT(GF("+NETOPEN?")); // May return +NETOPEN: 1, 0. We just confirm that the first number is 1 - if (waitResponse(GF(GSM_NL "+NETOPEN: 1")) != 1) { - return false; - } + if (waitResponse(GF(GSM_NL "+NETOPEN: 1")) != 1) { return false; } waitResponse(); - sendAT(GF("+IPADDR")); // Inquire Socket PDP address + sendAT(GF("+IPADDR")); // Inquire Socket PDP address // sendAT(GF("+CGPADDR=1")); // Show PDP address - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } @@ -415,120 +341,61 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * IP Address functions */ - - String getLocalIP() { + protected: + String getLocalIPImpl() { sendAT(GF("+IPADDR")); // Inquire Socket PDP address // sendAT(GF("+CGPADDR=1")); // Show PDP address String res; - if (waitResponse(10000L, res) != 1) { - return ""; - } + if (waitResponse(10000L, res) != 1) { return ""; } res.replace(GSM_NL "OK" GSM_NL, ""); res.replace(GSM_NL, ""); res.trim(); return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * Phone Call functions */ - - bool setGsmBusy() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool callNumber() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool dtmfSend() TINY_GSM_ATTR_NOT_IMPLEMENTED; + protected: + bool callAnswerImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool callNumberImpl(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool callHangupImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool + dtmfSendImpl(char cmd, int duration_ms = 100) TINY_GSM_ATTR_NOT_IMPLEMENTED; /* * Messaging functions */ - - String sendUSSD(const String& code) { - // Select message format (1=text) - sendAT(GF("+CMGF=1")); - waitResponse(); - // Select TE character set - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("+CUSD=1,\""), code, GF("\"")); - if (waitResponse() != 1) { - return ""; - } - if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) { - return ""; - } - stream.readStringUntil('"'); - String hex = stream.readStringUntil('"'); - stream.readStringUntil(','); - int dcs = stream.readStringUntil('\n').toInt(); - - if (dcs == 15) { - return TinyGsmDecodeHex8bit(hex); - } else if (dcs == 72) { - return TinyGsmDecodeHex16bit(hex); - } else { - return hex; - } - } - - bool sendSMS(const String& number, const String& text) { - // Get SMS service centre address - sendAT(GF("+AT+CSCA?")); - waitResponse(); - // Select message format (1=text) - sendAT(GF("+CMGF=1")); - waitResponse(); - //Set GSM 7 bit default alphabet (3GPP TS 23.038) - sendAT(GF("+CSCS=\"GSM\"")); - waitResponse(); - // Send the message! - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - + protected: + // Follows all messaging functions per template /* * Location functions */ - - String getGsmLocation() TINY_GSM_ATTR_NOT_IMPLEMENTED; + protected: + String getGsmLocationImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; /* * GPS location functions */ + public: // enable GPS bool enableGPS() { sendAT(GF("+CGPS=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } bool disableGPS() { sendAT(GF("+CGPS=0")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } // get the RAW GPS output String getGPSraw() { sendAT(GF("+CGNSSINFO=32")); - if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -536,81 +403,78 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } // get GPS informations - bool getGPS(float *lat, float *lon, float *speed=0, int *alt=0) { - //String buffer = ""; + bool getGPS(float* lat, float* lon, float* speed = 0, int* alt = 0) { + // String buffer = ""; bool fix = false; sendAT(GF("+CGNSSINFO")); - if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { - return false; - } - - //stream.readStringUntil(','); // mode - if ( stream.readStringUntil(',').toInt() == 1 ) fix = true; - stream.readStringUntil(','); //gps - stream.readStringUntil(','); // glonass - stream.readStringUntil(','); // beidu - *lat = stream.readStringUntil(',').toFloat(); //lat - stream.readStringUntil(','); // N/S - *lon = stream.readStringUntil(',').toFloat(); //lon - 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 - stream.readStringUntil(','); //time - stream.readStringUntil(',');//PDOP - stream.readStringUntil(',');//HDOP - stream.readStringUntil(',');//VDOP + if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { return false; } + + // stream.readStringUntil(','); // mode + if (stream.readStringUntil(',').toInt() == 1) fix = true; + stream.readStringUntil(','); // gps + stream.readStringUntil(','); // glonass + stream.readStringUntil(','); // beidu + *lat = stream.readStringUntil(',').toFloat(); // lat + stream.readStringUntil(','); // N/S + *lon = stream.readStringUntil(',').toFloat(); // lon + 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 + stream.readStringUntil(','); // time + stream.readStringUntil(','); // PDOP + stream.readStringUntil(','); // HDOP + stream.readStringUntil(','); // VDOP stream.readStringUntil('\n'); waitResponse(); return fix; } + /* * Time functions */ + protected: + // Can follow the standard CCLK function in the template /* * Battery & temperature functions */ - + protected: // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() { + uint16_t getBattVoltageImpl() { sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return 0; - } + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return 0; } // get voltage in VOLTS float voltage = stream.readStringUntil('\n').toFloat(); // Wait for final OK waitResponse(); // Return millivolts - uint16_t res = voltage*1000; + uint16_t res = voltage * 1000; return res; } - int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE; + int8_t getBattPercentImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; + uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - bool getBattStats(uint8_t& chargeState, int8_t& percent, - uint16_t& milliVolts) { + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) { chargeState = 0; - percent = 0; - milliVolts = getBattVoltage(); + percent = 0; + milliVolts = getBattVoltage(); return true; } // get temperature in degree celsius - uint16_t getTemperature() { + uint16_t getTemperatureImpl() { sendAT(GF("+CPMUTEMP")); - if (waitResponse(GF(GSM_NL "+CPMUTEMP:")) != 1) { - return 0; - } + if (waitResponse(GF(GSM_NL "+CPMUTEMP:")) != 1) { return 0; } // return temperature in C uint16_t res = stream.readStringUntil('\n').toInt(); // Wait for final OK @@ -621,75 +485,67 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * Client related functions */ - -protected: - - 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; + protected: + 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, ',', (uint16_t)len); - if (waitResponse(GF(">")) != 1) { - return 0; - } - stream.write((uint8_t*)buff, len); + if (waitResponse(GF(">")) != 1) { return 0; } + stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(GF(GSM_NL "+CIPSEND:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip mux - streamSkipUntil(','); // Skip requested bytes to send - // TODO: make sure requested and confirmed bytes match + if (waitResponse(GF(GSM_NL "+CIPSEND:")) != 1) { return 0; } + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip requested bytes to send + // TODO(?): make sure requested and confirmed bytes match return stream.readStringUntil('\n').toInt(); } size_t modemRead(size_t size, uint8_t mux) { #ifdef TINY_GSM_USE_HEX sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CIPRXGET:")) != 1) { - return 0; - } + if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #else sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CIPRXGET:")) != 1) { - return 0; - } + if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #endif - streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX - streamSkipUntil(','); // Skip mux/cid (connecion id) + streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX + streamSkipUntil(','); // Skip mux/cid (connecion id) int len_requested = stream.readStringUntil(',').toInt(); // ^^ Requested number of data bytes (1-1460 bytes)to be read int len_confirmed = stream.readStringUntil('\n').toInt(); // ^^ The data length which not read in the buffer - for (int i=0; i_timeout)) { TINY_GSM_YIELD(); } - char buf[4] = { 0, }; + while (stream.available() < 2 && + (millis() - startMillis < sockets[mux]->_timeout)) { + TINY_GSM_YIELD(); + } + char buf[4] = { + 0, + }; buf[0] = stream.read(); buf[1] = stream.read(); char c = strtol(buf, NULL, 16); #else - while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } + while (!stream.available() && + (millis() - startMillis < sockets[mux]->_timeout)) { + TINY_GSM_YIELD(); + } char c = stream.read(); #endif sockets[mux]->rx.put(c); @@ -705,15 +561,13 @@ protected: sendAT(GF("+CIPRXGET=4,"), mux); size_t result = 0; if (waitResponse(GF("+CIPRXGET:")) == 1) { - streamSkipUntil(','); // Skip mode 4 - streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip mode 4 + streamSkipUntil(','); // Skip mux result = stream.readStringUntil('\n').toInt(); waitResponse(); } DBG("### Available:", result, "on", mux); - if (!result) { - sockets[mux]->sock_connected = modemGetConnected(mux); - } + if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); } return result; } @@ -723,7 +577,7 @@ protected: if (waitResponse(GF("+CIPCLOSE:")) != 1) { // return false; // TODO: Why does this not read correctly? } - for (int muxNo = 0; muxNo <= TINY_GSM_MUX_COUNT; muxNo++) { + for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) { // +CIPCLOSE:,,..., sockets[muxNo]->sock_connected = stream.parseInt(); } @@ -731,19 +585,16 @@ protected: return sockets[mux]->sock_connected; } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -751,15 +602,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -791,7 +642,7 @@ TINY_GSM_MODEM_STREAM_UTILITIES() int mux = stream.readStringUntil(',').toInt(); int len = stream.readStringUntil('\n').toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; + sockets[mux]->got_data = true; sockets[mux]->sock_available = len; } data = ""; @@ -808,45 +659,41 @@ TINY_GSM_MODEM_STREAM_UTILITIES() // Need to close all open sockets and release the network library. // User will then need to reconnect. DBG("### Network error!"); - if (!isGprsConnected()) { - gprsDisconnect(); - } + if (!isGprsConnected()) { gprsDisconnect(); } data = ""; } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } 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=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + 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) { 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=NULL, 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; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientSim7600* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTSIM7600_H_ diff --git a/src/TinyGsmClientSIM800.h b/src/TinyGsmClientSIM800.h index 5b0fa3d..c10fd54 100644 --- a/src/TinyGsmClientSIM800.h +++ b/src/TinyGsmClientSIM800.h @@ -6,32 +6,24 @@ * @date Nov 2016 */ -#ifndef TinyGsmClientSIM800_h -#define TinyGsmClientSIM800_h -//#pragma message("TinyGSM: TinyGsmClientSIM800") +#ifndef SRC_TINYGSMCLIENTSIM800_H_ +#define SRC_TINYGSMCLIENTSIM800_H_ +// #pragma message("TinyGSM: TinyGsmClientSIM800") -//#define TINY_GSM_DEBUG Serial -//#define TINY_GSM_USE_HEX - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 64 -#endif +// #define TINY_GSM_DEBUG Serial +// #define TINY_GSM_USE_HEX #define TINY_GSM_MUX_COUNT 5 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,174 +31,154 @@ enum RegStatus { REG_OK_ROAMING = 5, REG_UNKNOWN = 4, }; +class TinyGsmSim800 : public TinyGsmModem { + friend class TinyGsmModem; -enum TinyGSMDateTimeFormat { - DATE_FULL = 0, - DATE_TIME = 1, - DATE_DATE = 2 -}; - -class TinyGsmSim800 : public TinyGsmUTFSMS -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmSim800; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} - - GsmClient(TinyGsmSim800& modem, uint8_t mux = 1) { - init(&modem, mux); - } - - virtual ~GsmClient(){} - - bool init(TinyGsmSim800* modem, uint8_t mux = 1) { - this->at = modem; - this->mux = mux; - sock_available = 0; - prev_check = 0; - sock_connected = false; - got_data = false; - - at->sockets[mux] = this; + /* + * Inner Client + */ + public: + class GsmClientSim800 : public GsmClient { + friend class TinyGsmSim800; - return true; - } + public: + GsmClientSim800() {} -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, false, timeout_s); - return sock_connected; - } + explicit GsmClientSim800(TinyGsmSim800& modem, uint8_t mux = 1) { + init(&modem, mux); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + bool init(TinyGsmSim800* modem, uint8_t mux = 1) { + this->at = modem; + this->mux = mux; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() - at->sendAT(GF("+CIPCLOSE="), mux, GF(",1")); // Quick close - sock_connected = false; - at->waitResponse(); - } + at->sockets[mux] = this; - virtual void stop() { stop(15000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() + void stop(uint32_t maxWaitMs) { + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+CIPCLOSE="), mux, GF(",1")); // Quick close + sock_connected = false; + at->waitResponse(); + } + void stop() override { + stop(15000L); + } -TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() + /* + * Extended API + */ -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; /* - * Extended API + * Inner Secure Client */ + public: + class GsmClientSecureSim800 : public GsmClientSim800 { + public: + GsmClientSecureSim800() {} - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; - -private: - TinyGsmSim800* at; - uint8_t mux; - uint16_t sock_available; - uint32_t prev_check; - bool sock_connected; - bool got_data; - RxFifo rx; -}; - - -class GsmClientSecure : public GsmClient -{ -public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmSim800& modem, uint8_t mux = 1) - : GsmClient(modem, mux) - {} - - virtual ~GsmClientSecure(){} + explicit GsmClientSecureSim800(TinyGsmSim800& modem, uint8_t mux = 1) + : GsmClientSim800(modem, mux) {} -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, true, timeout_s); - return sock_connected; - } -}; - - -public: + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + }; - TinyGsmSim800(Stream& stream) - : stream(stream) - { + public: + explicit TinyGsmSim800(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } + if (!testAT()) { return false; } // sendAT(GF("&FZ")); // Factory + Reset // waitResponse(); - sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } + +#ifdef TINY_GSM_DEBUG + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes +#endif + waitResponse(); DBG(GF("### Modem:"), getModemName()); + // Enable Local Time Stamp for getting network time + sendAT(GF("+CLTS=1")); + if (waitResponse(10000L) != 1) { return false; } + 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 { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { + String getModemNameImpl() { String name = ""; - #if defined(TINY_GSM_MODEM_SIM800) - name = "SIMCom SIM800"; - #elif defined(TINY_GSM_MODEM_SIM808) - name = "SIMCom SIM808"; - #elif defined(TINY_GSM_MODEM_SIM868) - name = "SIMCom SIM868"; - #elif defined(TINY_GSM_MODEM_SIM900) - name = "SIMCom SIM900"; - #endif +#if defined(TINY_GSM_MODEM_SIM800) + name = "SIMCom SIM800"; +#elif defined(TINY_GSM_MODEM_SIM808) + name = "SIMCom SIM808"; +#elif defined(TINY_GSM_MODEM_SIM868) + name = "SIMCom SIM868"; +#elif defined(TINY_GSM_MODEM_SIM900) + name = "SIMCom SIM900"; +#endif sendAT(GF("+GMM")); String res2; - if (waitResponse(1000L, res2) != 1) { - return name; - } + if (waitResponse(1000L, res2) != 1) { return name; } res2.replace(GSM_NL "OK" GSM_NL, ""); res2.replace("_", " "); res2.trim(); @@ -216,97 +188,65 @@ public: return name; } -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() - - bool factoryDefault() { + bool factoryDefaultImpl() { sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write waitResponse(); - sendAT(GF("+IPR=0")); // Auto-baud + sendAT(GF("+IPR=0")); // Auto-baud waitResponse(); - sendAT(GF("+IFC=0,0")); // No Flow Control + sendAT(GF("+IFC=0,0")); // No Flow Control waitResponse(); - sendAT(GF("+ICF=3,3")); // 8 data 0 parity 1 stop + sendAT(GF("+ICF=3,3")); // 8 data 0 parity 1 stop waitResponse(); - sendAT(GF("+CSCLK=0")); // Disable Slow Clock + sendAT(GF("+CSCLK=0")); // Disable Slow Clock waitResponse(); - sendAT(GF("&W")); // Write configuration + sendAT(GF("&W")); // Write configuration return waitResponse() == 1; } -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { + bool thisHasSSL() { #if defined(TINY_GSM_MODEM_SIM900) return false; #else sendAT(GF("+CIPSSL=?")); - if (waitResponse(GF(GSM_NL "+CIPSSL:")) != 1) { - return false; - } + if (waitResponse(GF(GSM_NL "+CIPSSL:")) != 1) { return false; } return waitResponse() == 1; #endif } - bool hasWifi() { + bool thisHasWifi() { return false; } - bool hasGPRS() { + bool thisHasGPRS() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } - //Enable Local Time Stamp for getting network time - // TODO: Find a better place for this - sendAT(GF("+CLTS=1")); - if (waitResponse(10000L) != 1) { - return false; - } + protected: + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("&W")); waitResponse(); sendAT(GF("+CFUN=0")); - if (waitResponse(10000L) != 1) { - return false; - } + if (waitResponse(10000L) != 1) { return false; } sendAT(GF("+CFUN=1,1")); - if (waitResponse(10000L) != 1) { - return false; - } + if (waitResponse(10000L) != 1) { return false; } delay(3000); return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+CPOWD=1")); return waitResponse(10000L, GF("NORMAL POWER DOWN")) == 1; } - bool radioOff() { - sendAT(GF("+CFUN=0")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000); - return true; - } - - /* - During sleep, the SIM800 module has its serial communication disabled. In order to reestablish communication - pull the DRT-pin of the SIM800 module LOW for at least 50ms. Then use this function to disable sleep mode. - The DTR-pin can then be released again. - */ - bool sleepEnable(bool enable = true) { + // During sleep, the SIM800 module has its serial communication disabled. In + // order to reestablish communication pull the DRT-pin of the SIM800 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) { sendAT(GF("+CSCLK="), enable); return waitResponse() == 1; } @@ -314,58 +254,34 @@ TINY_GSM_MODEM_GET_INFO_ATI() /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - -TINY_GSM_MODEM_GET_SIMCCID_CCID() - -TINY_GSM_MODEM_GET_IMEI_GSN() - - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - 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"), GF("NOT READY")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; - } - -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) - -TINY_GSM_MODEM_GET_OPERATOR_COPS() + protected: + // Able to follow all SIM card functions as inherited from the template /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CREG"); + } -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { gprsDisconnect(); // Set the Bearer for the IP - sendAT(GF("+SAPBR=3,1,\"Contype\",\"GPRS\"")); // Set the connection type to GPRS + sendAT(GF( + "+SAPBR=3,1,\"Contype\",\"GPRS\"")); // Set the connection type to GPRS waitResponse(); sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"'); // Set the APN @@ -393,88 +309,53 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() waitResponse(85000L); // Query the GPRS bearer context status sendAT(GF("+SAPBR=2,1")); - if (waitResponse(30000L) != 1) - return false; + if (waitResponse(30000L) != 1) { return false; } // Attach to GPRS sendAT(GF("+CGATT=1")); - if (waitResponse(60000L) != 1) - return false; + if (waitResponse(60000L) != 1) { return false; } - // TODO: wait AT+CGATT? + // TODO(?): wait AT+CGATT? // Set to multi-IP sendAT(GF("+CIPMUX=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } // Put in "quick send" mode (thus no extra "Send OK") sendAT(GF("+CIPQSEND=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } // Set to get data manually sendAT(GF("+CIPRXGET=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } // Start Task and Set APN, USER NAME, PASSWORD sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\"")); - if (waitResponse(60000L) != 1) { - return false; - } + if (waitResponse(60000L) != 1) { return false; } // Bring Up Wireless Connection with GPRS or CSD sendAT(GF("+CIICR")); - if (waitResponse(60000L) != 1) { - return false; - } + if (waitResponse(60000L) != 1) { return false; } // Get Local IP Address, only assigned after connection sendAT(GF("+CIFSR;E0")); - if (waitResponse(10000L) != 1) { - return false; - } + if (waitResponse(10000L) != 1) { return false; } // Configure Domain Name Server (DNS) sendAT(GF("+CDNSCFG=\"8.8.8.8\",\"8.8.4.4\"")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } - bool gprsDisconnect() { + bool gprsDisconnectImpl() { // Shut the TCP/IP connection // CIPSHUT will close *all* open connections sendAT(GF("+CIPSHUT")); - if (waitResponse(60000L) != 1) - return false; + if (waitResponse(60000L) != 1) { return false; } sendAT(GF("+CGATT=0")); // Deactivate the bearer context - if (waitResponse(60000L) != 1) - return false; - - return true; - } - - bool isGprsConnected() { - sendAT(GF("+CGATT?")); - if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { - return false; - } - int res = stream.readStringUntil('\n').toInt(); - waitResponse(); - if (res != 1) - return false; - - sendAT(GF("+CIFSR;E0")); // Another option is to use AT+CGPADDR=1 - if (waitResponse() != 1) - return false; + if (waitResponse(60000L) != 1) { return false; } return true; } @@ -482,273 +363,96 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * IP Address functions */ - - String getLocalIP() { + protected: + String getLocalIPImpl() { sendAT(GF("+CIFSR;E0")); String res; - if (waitResponse(10000L, res) != 1) { - return ""; - } + if (waitResponse(10000L, res) != 1) { return ""; } res.replace(GSM_NL "OK" GSM_NL, ""); res.replace(GSM_NL, ""); res.trim(); return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * Phone Call functions */ - + public: bool setGsmBusy(bool busy = true) { sendAT(GF("+GSMBUSY="), busy ? 1 : 0); return waitResponse() == 1; } - bool callAnswer() { - sendAT(GF("A")); - return waitResponse() == 1; - } - - // Returns true on pick-up, false on error/busy - bool callNumber(const String& number) { - if (number == GF("last")) { - sendAT(GF("DL")); - } else { - sendAT(GF("D"), number, ";"); - } - int status = waitResponse(60000L, - GFP(GSM_OK), - GF("BUSY" GSM_NL), - GF("NO ANSWER" GSM_NL), - GF("NO CARRIER" GSM_NL)); - switch (status) { - case 1: return true; - case 2: - case 3: return false; - default: return false; - } - } - - bool callHangup() { - sendAT(GF("H")); - return waitResponse() == 1; - } - - // 0-9,*,#,A,B,C,D - bool dtmfSend(char cmd, int duration_ms = 100) { - duration_ms = constrain(duration_ms, 100, 1000); - - sendAT(GF("+VTD="), duration_ms / 100); // VTD accepts in 1/10 of a second - waitResponse(); - - sendAT(GF("+VTS="), cmd); - return waitResponse(10000L) == 1; - } - /* * Messaging functions */ - - String sendUSSD(const String& code) { - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("+CUSD=1,\""), code, GF("\"")); - if (waitResponse() != 1) { - return ""; - } - if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) { - return ""; - } - stream.readStringUntil('"'); - String hex = stream.readStringUntil('"'); - stream.readStringUntil(','); - int dcs = stream.readStringUntil('\n').toInt(); - - if (dcs == 15) { - return TinyGsmDecodeHex8bit(hex); - } else if (dcs == 72) { - return TinyGsmDecodeHex16bit(hex); - } else { - return hex; - } - } - - bool sendSMS(const String& number, const String& text) { - sendAT(GF("+CMGF=1")); - waitResponse(); - //Set GSM 7 bit default alphabet (3GPP TS 23.038) - sendAT(GF("+CSCS=\"GSM\"")); - waitResponse(); - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } + protected: + // Follows all messaging functions per template /* * Location functions */ + protected: + // Can return a location from CIPGSMLOC as per the template - String getGsmLocation() { - sendAT(GF("+CIPGSMLOC=1,1")); - if (waitResponse(10000L, GF(GSM_NL "+CIPGSMLOC:")) != 1) { - return ""; - } - String res = stream.readStringUntil('\n'); - waitResponse(); - res.trim(); - return res; - } + /* + * GPS location functions + */ + public: + // No functions of this type supported /* * Time functions */ - - String getGSMDateTime(TinyGSMDateTimeFormat format) { - sendAT(GF("+CCLK?")); - if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) { - return ""; - } - - String res; - - switch(format) { - case DATE_FULL: - res = stream.readStringUntil('"'); - break; - case DATE_TIME: - streamSkipUntil(','); - res = stream.readStringUntil('"'); - break; - case DATE_DATE: - res = stream.readStringUntil(','); - break; - } - return res; - } + protected: + // Can follow the standard CCLK function in the template /* * Battery & temperature functions */ - - // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip battery charge status - streamSkipUntil(','); // Skip battery charge level - // return voltage in mV - uint16_t res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - int8_t getBattPercent() { - sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - streamSkipUntil(','); // Skip battery charge status - // Read battery charge level - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - uint8_t getBattChargeState() { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - // Read battery charge status - int res = stream.readStringUntil(',').toInt(); - // Wait for final OK - waitResponse(); - return res; - } - - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { - sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { - return false; - } - chargeState = stream.readStringUntil(',').toInt(); - percent = stream.readStringUntil(',').toInt(); - milliVolts = stream.readStringUntil('\n').toInt(); - // Wait for final OK - waitResponse(); - return true; - } - - float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE; + protected: + float getTemperatureImpl() TINY_GSM_ATTR_NOT_AVAILABLE; /* * NTP server functions */ - + public: boolean isValidNumber(String str) { - if(!(str.charAt(0) == '+' || str.charAt(0) == '-' || isDigit(str.charAt(0)))) return false; + if (!(str.charAt(0) == '+' || str.charAt(0) == '-' || + isDigit(str.charAt(0)))) + return false; - for(byte i=1;i < str.length(); i++) { - if(!(isDigit(str.charAt(i)) || str.charAt(i) == '.')) return false; + for (byte i = 1; i < str.length(); i++) { + if (!(isDigit(str.charAt(i)) || str.charAt(i) == '.')) { return false; } } return true; } String ShowNTPError(byte error) { switch (error) { - case 1: - return "Network time synchronization is successful"; - case 61: - return "Network error"; - case 62: - return "DNS resolution error"; - case 63: - return "Connection error"; - case 64: - return "Service response error"; - case 65: - return "Service response timeout"; - default: - return "Unknown error: " + String(error); + case 1: return "Network time synchronization is successful"; + case 61: return "Network error"; + case 62: return "DNS resolution error"; + case 63: return "Connection error"; + case 64: return "Service response error"; + case 65: return "Service response timeout"; + default: return "Unknown error: " + String(error); } } byte NTPServerSync(String server = "pool.ntp.org", byte TimeZone = 3) { sendAT(GF("+CNTPCID=1")); - if (waitResponse(10000L) != 1) { - return -1; - } + if (waitResponse(10000L) != 1) { return -1; } sendAT(GF("+CNTP="), server, ',', String(TimeZone)); - if (waitResponse(10000L) != 1) { - return -1; - } + if (waitResponse(10000L) != 1) { return -1; } sendAT(GF("+CNTP")); if (waitResponse(10000L, GF(GSM_NL "+CNTP:"))) { - String result = stream.readStringUntil('\n'); - result.trim(); - if (isValidNumber(result)) - { - return result.toInt(); - } - } - else { + String result = stream.readStringUntil('\n'); + result.trim(); + if (isValidNumber(result)) { return result.toInt(); } + } else { return -1; } return -1; @@ -757,76 +461,69 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * Client related functions */ - -protected: - + 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; + bool ssl = false, int timeout_s = 75) { + int rsp; + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; #if !defined(TINY_GSM_MODEM_SIM900) sendAT(GF("+CIPSSL="), ssl); rsp = waitResponse(); - if (ssl && rsp != 1) { - return false; - } + if (ssl && rsp != 1) { return false; } #endif - 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 - ); + 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, ',', (uint16_t)len); - if (waitResponse(GF(">")) != 1) { - return 0; - } - stream.write((uint8_t*)buff, len); + if (waitResponse(GF(">")) != 1) { return 0; } + stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(GF(GSM_NL "DATA ACCEPT:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip mux + if (waitResponse(GF(GSM_NL "DATA ACCEPT:")) != 1) { return 0; } + streamSkipUntil(','); // Skip mux return stream.readStringUntil('\n').toInt(); } size_t modemRead(size_t size, uint8_t mux) { #ifdef TINY_GSM_USE_HEX sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CIPRXGET:")) != 1) { - return 0; - } + if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #else sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CIPRXGET:")) != 1) { - return 0; - } + if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } #endif - streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX - streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX + streamSkipUntil(','); // Skip mux int len_requested = stream.readStringUntil(',').toInt(); // ^^ Requested number of data bytes (1-1460 bytes)to be read 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 (int i=0; i_timeout)) { TINY_GSM_YIELD(); } - char buf[4] = { 0, }; + while (stream.available() < 2 && + (millis() - startMillis < sockets[mux]->_timeout)) { + TINY_GSM_YIELD(); + } + char buf[4] = { + 0, + }; buf[0] = stream.read(); buf[1] = stream.read(); char c = strtol(buf, NULL, 16); #else - while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } + while (!stream.available() && + (millis() - startMillis < sockets[mux]->_timeout)) { + TINY_GSM_YIELD(); + } char c = stream.read(); #endif sockets[mux]->rx.put(c); @@ -842,40 +539,36 @@ protected: sendAT(GF("+CIPRXGET=4,"), mux); size_t result = 0; if (waitResponse(GF("+CIPRXGET:")) == 1) { - streamSkipUntil(','); // Skip mode 4 - streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip mode 4 + streamSkipUntil(','); // Skip mux result = stream.readStringUntil('\n').toInt(); waitResponse(); } DBG("### Available:", result, "on", mux); - if (!result) { - sockets[mux]->sock_connected = modemGetConnected(mux); - } + if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); } return result; } bool modemGetConnected(uint8_t mux) { sendAT(GF("+CIPSTATUS="), mux); waitResponse(GF("+CIPSTATUS")); - int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), - GF(",\"REMOTE CLOSING\""), GF(",\"INITIAL\"")); + int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), + GF(",\"CLOSING\""), GF(",\"REMOTE CLOSING\""), + GF(",\"INITIAL\"")); waitResponse(); return 1 == res; } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -883,14 +576,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { + TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -922,15 +616,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() int mux = stream.readStringUntil(',').toInt(); int len = stream.readStringUntil('\n').toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; + sockets[mux]->got_data = true; sockets[mux]->sock_available = len; } 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); - int mux = data.substring(nl+2, coma).toInt(); + int nl = data.lastIndexOf(GSM_NL, data.length() - 8); + int coma = data.indexOf(',', nl + 2); + int mux = data.substring(nl + 2, coma).toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } @@ -939,38 +633,36 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } 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=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + 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) { 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=NULL, 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; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientSim800* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTSIM800_H_ diff --git a/src/TinyGsmClientSIM808.h b/src/TinyGsmClientSIM808.h index 35754a0..9df90a7 100644 --- a/src/TinyGsmClientSIM808.h +++ b/src/TinyGsmClientSIM808.h @@ -6,34 +6,26 @@ * @date Nov 2016 */ -#ifndef TinyGsmClientSIM808_h -#define TinyGsmClientSIM808_h -//#pragma message("TinyGSM: TinyGsmClientSIM808") +#ifndef SRC_TINYGSMCLIENTSIM808_H_ +#define SRC_TINYGSMCLIENTSIM808_H_ +// #pragma message("TinyGSM: TinyGsmClientSIM808") #include - -class TinyGsmSim808: public TinyGsmSim800 -{ - -public: - - TinyGsmSim808(Stream& stream) - : TinyGsmSim800(stream) - {} +class TinyGsmSim808 : public TinyGsmSim800 { + public: + explicit TinyGsmSim808(Stream& stream) : TinyGsmSim800(stream) {} /* * GPS location functions */ - + public: // enable GPS bool enableGPS() { // uint16_t state; sendAT(GF("+CGNSPWR=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } @@ -42,9 +34,7 @@ public: // uint16_t state; sendAT(GF("+CGNSPWR=0")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } @@ -53,9 +43,7 @@ public: // works only with ans SIM808 V2 String getGPSraw() { sendAT(GF("+CGNSINF")); - if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -64,23 +52,22 @@ public: // get GPS informations // works only with ans SIM808 V2 - bool getGPS(float *lat, float *lon, float *speed=0, int *alt=0, int *vsat=0, int *usat=0) { - //String buffer = ""; + bool getGPS(float* lat, float* lon, float* speed = 0, int* alt = 0, + int* vsat = 0, int* usat = 0) { + // String buffer = ""; // char chr_buffer[12]; bool fix = false; sendAT(GF("+CGNSINF")); - if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { - return false; - } - - stream.readStringUntil(','); // mode - if ( stream.readStringUntil(',').toInt() == 1 ) fix = true; - stream.readStringUntil(','); //utctime - *lat = stream.readStringUntil(',').toFloat(); //lat - *lon = stream.readStringUntil(',').toFloat(); //lon - if (alt != NULL) *alt = stream.readStringUntil(',').toFloat(); //lon - if (speed != NULL) *speed = stream.readStringUntil(',').toFloat(); //speed + if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { return false; } + + stream.readStringUntil(','); // mode + if (stream.readStringUntil(',').toInt() == 1) fix = true; + stream.readStringUntil(','); // utctime + *lat = stream.readStringUntil(',').toFloat(); // lat + *lon = stream.readStringUntil(',').toFloat(); // lon + if (alt != NULL) *alt = stream.readStringUntil(',').toFloat(); // lon + if (speed != NULL) *speed = stream.readStringUntil(',').toFloat(); // speed stream.readStringUntil(','); stream.readStringUntil(','); stream.readStringUntil(','); @@ -88,8 +75,10 @@ public: stream.readStringUntil(','); stream.readStringUntil(','); stream.readStringUntil(','); - if (vsat != NULL) *vsat = stream.readStringUntil(',').toInt(); //viewed satelites - if (usat != NULL) *usat = stream.readStringUntil(',').toInt(); //used satelites + if (vsat != NULL) + *vsat = stream.readStringUntil(',').toInt(); // viewed satelites + if (usat != NULL) + *usat = stream.readStringUntil(',').toInt(); // used satelites stream.readStringUntil('\n'); waitResponse(); @@ -99,34 +88,31 @@ public: // get GPS time // works only with SIM808 V2 - bool getGPSTime(int *year, int *month, int *day, int *hour, int *minute, int *second) { + bool getGPSTime(int* year, int* month, int* day, int* hour, int* minute, + int* second) { bool fix = false; char chr_buffer[12]; sendAT(GF("+CGNSINF")); - if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { - return false; - } + if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { return false; } for (int i = 0; i < 3; i++) { String buffer = stream.readStringUntil(','); buffer.toCharArray(chr_buffer, sizeof(chr_buffer)); switch (i) { case 0: - //mode + // mode break; case 1: - //fixstatus - if ( buffer.toInt() == 1 ) { - fix = buffer.toInt(); - } + // fixstatus + if (buffer.toInt() == 1) { fix = buffer.toInt(); } break; case 2: - *year = buffer.substring(0,4).toInt(); - *month = buffer.substring(4,6).toInt(); - *day = buffer.substring(6,8).toInt(); - *hour = buffer.substring(8,10).toInt(); - *minute = buffer.substring(10,12).toInt(); - *second = buffer.substring(12,14).toInt(); + *year = buffer.substring(0, 4).toInt(); + *month = buffer.substring(4, 6).toInt(); + *day = buffer.substring(6, 8).toInt(); + *hour = buffer.substring(8, 10).toInt(); + *minute = buffer.substring(10, 12).toInt(); + *second = buffer.substring(12, 14).toInt(); break; default: @@ -135,7 +121,7 @@ public: break; } } - String res = stream.readStringUntil('\n'); + stream.readStringUntil('\n'); waitResponse(); if (fix) { @@ -144,7 +130,6 @@ public: return false; } } - }; -#endif +#endif // SRC_TINYGSMCLIENTSIM808_H_ diff --git a/src/TinyGsmClientSaraR4.h b/src/TinyGsmClientSaraR4.h index afb35ba..7a130f9 100644 --- a/src/TinyGsmClientSaraR4.h +++ b/src/TinyGsmClientSaraR4.h @@ -6,32 +6,23 @@ * @date Nov 2016 */ -#ifndef TinyGsmClientSaraR4_h -#define TinyGsmClientSaraR4_h -//#pragma message("TinyGSM: TinyGsmClientSaraR4") +#ifndef SRC_TINYGSMCLIENTSARAR4_H_ +#define SRC_TINYGSMCLIENTSARAR4_H_ +// #pragma message("TinyGSM: TinyGsmClientSaraR4") -//#define TINY_GSM_DEBUG Serial - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 64 -#endif +// #define TINY_GSM_DEBUG Serial #define TINY_GSM_MUX_COUNT 7 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; - enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -40,185 +31,157 @@ enum RegStatus { REG_UNKNOWN = 4, }; +class TinyGsmSaraR4 : public TinyGsmModem { + friend class TinyGsmModem; -class TinyGsmSaraR4 -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmSaraR4; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} + /* + * Inner Client + */ + public: + class GsmClientSaraR4 : public GsmClient { + friend class TinyGsmSaraR4; - GsmClient(TinyGsmSaraR4& modem, uint8_t mux = 0) { init(&modem, mux); } + public: + GsmClientSaraR4() {} - virtual ~GsmClient(){} + explicit GsmClientSaraR4(TinyGsmSaraR4& modem, uint8_t mux = 0) { + init(&modem, mux); + } - bool init(TinyGsmSaraR4* modem, uint8_t mux = 0) { - this->at = modem; - this->mux = mux; - sock_available = 0; - prev_check = 0; - sock_connected = false; - got_data = false; + bool init(TinyGsmSaraR4* modem, uint8_t mux = 0) { + this->at = modem; + this->mux = mux; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; - at->sockets[mux] = this; + at->sockets[mux] = this; - return true; - } + return true; + } -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); - uint8_t oldMux = mux; - sock_connected = at->modemConnect(host, port, &mux, false, timeout_s); - if (mux != oldMux) { + uint8_t oldMux = mux; + sock_connected = at->modemConnect(host, port, &mux, false, timeout_s); + if (mux != oldMux) { DBG("WARNING: Mux number changed from", oldMux, "to", mux); at->sockets[oldMux] = NULL; + } + at->sockets[mux] = this; + at->maintain(); + + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 120); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 120); + } + + void stop(uint32_t maxWaitMs) { + uint32_t startMillis = millis(); + dumpModemBuffer(maxWaitMs); + // We want to use an async socket close because the syncrhonous close of + // an open socket is INCREDIBLY SLOW and the modem can freeze up. But we + // only attempt the async close if we already KNOW the socket is open + // because calling the async close on a closed socket and then attempting + // opening a new socket causes the board to lock up for 2-3 minutes and + // then finally return with a "new" socket that is immediately closed. + // Attempting to close a socket that is already closed with a synchronous + // close quickly returns an error. + if (at->supportsAsyncSockets && sock_connected) { + DBG("### Closing socket asynchronously! Socket might remain open " + "until arrival of +UUSOCL:", + mux); + // 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))); + // We set the sock as disconnected right away because it can no longer + // be used + sock_connected = false; + } else { + // 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; + } } - at->sockets[mux] = this; - at->maintain(); - - return sock_connected; - } - virtual int connect(IPAddress ip, uint16_t port, int timeout_s) { - String host; - host.reserve(16); - host += ip[0]; - host += "."; - host += ip[1]; - host += "."; - host += ip[2]; - host += "."; - host += ip[3]; - return connect(host.c_str(), port, timeout_s); - } - virtual int connect(const char* host, uint16_t port) { - return connect(host, port, 120); - } - virtual int connect(IPAddress ip, uint16_t port) { - return connect(ip, port, 120); - } - - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() - // We want to use an async socket close because the syncrhonous close of an - // open socket is INCREDIBLY SLOW and the modem can freeze up. But we only - // attempt the async close if we already KNOW the socket is open because - // calling the async close on a closed socket and then attempting opening a - // new socket causes the board to lock up for 2-3 minutes and then finally - // return with a "new" socket that is immediately closed. - // Attempting to close a socket that is already closed with a synchronous - // close quickly returns an error. - if (at->supportsAsyncSockets && sock_connected) { - DBG("### Closing socket asynchronously! Socket might remain open " - "until arrival of +UUSOCL:", - mux); - // 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))); - // We set the sock as disconnected right away because it can no longer - // be used - sock_connected = false; - } else { - // 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; + void stop() override { + stop(135000L); } - } - virtual void stop() { stop(135000L); } + /* + * Extended API + */ -TINY_GSM_CLIENT_WRITE() - -TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() - -TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() - -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; /* - * Extended API + * Inner Secure Client */ + public: + class GsmClientSecureR4 : public GsmClientSaraR4 { + public: + GsmClientSecureR4() {} - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; - -private: - TinyGsmSaraR4* at; - uint8_t mux; - uint16_t sock_available; - uint32_t prev_check; - bool sock_connected; - bool got_data; - RxFifo rx; -}; - - -class GsmClientSecure : public GsmClient -{ -public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmSaraR4& modem, uint8_t mux = 1) - : GsmClient(modem, mux) - {} - - virtual ~GsmClientSecure(){} + explicit GsmClientSecureR4(TinyGsmSaraR4& modem, uint8_t mux = 1) + : GsmClientSaraR4(modem, mux) {} -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - uint8_t oldMux = mux; - sock_connected = at->modemConnect(host, port, &mux, true, timeout_s); - if (mux != oldMux) { + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + uint8_t oldMux = mux; + sock_connected = at->modemConnect(host, port, &mux, true, timeout_s); + if (mux != oldMux) { DBG("WARNING: Mux number changed from", oldMux, "to", mux); at->sockets[oldMux] = NULL; + } + at->sockets[mux] = this; + at->maintain(); + return sock_connected; } - at->sockets[mux] = this; - at->maintain(); - return sock_connected; - } -}; - + }; -public: - - TinyGsmSaraR4(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmSaraR4(Stream& stream) + : stream(stream), + has2GFallback(false), + supportsAsyncSockets(false) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { return init(pin); } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } + if (!testAT()) { return false; } sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } #ifdef TINY_GSM_DEBUG sendAT(GF("+CMEE=2")); // turn on verbose error codes @@ -241,33 +204,33 @@ public: supportsAsyncSockets = true; } + // Enable automatic time zome update + sendAT(GF("+CTZU=1")); + if (waitResponse(10000L) != 1) { return false; } + 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 - // return true - else { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { + // only difference in implementation is the warning on the wrong type + String getModemNameImpl() { sendAT(GF("+CGMI")); String res1; - if (waitResponse(1000L, res1) != 1) { - return "u-blox Cellular Modem"; - } + if (waitResponse(1000L, res1) != 1) { return "u-blox Cellular Modem"; } res1.replace(GSM_NL "OK" GSM_NL, ""); res1.trim(); sendAT(GF("+GMM")); String res2; - if (waitResponse(1000L, res2) != 1) { - return "u-blox Cellular Modem"; - } + if (waitResponse(1000L, res2) != 1) { return "u-blox Cellular Modem"; } res2.replace(GSM_NL "OK" GSM_NL, ""); res2.trim(); @@ -281,108 +244,65 @@ public: return name; } -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() - - bool factoryDefault() { + bool factoryDefaultImpl() { sendAT(GF("&F")); // Resets the current profile, other NVM not affected return waitResponse() == 1; } -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { + bool thisHasSSL() { return true; } - bool hasWifi() { + bool thisHasWifi() { return false; } - bool hasGPRS() { + bool thisHasGPRS() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } + protected: + // using +CFUN=15 instead of the more common CFUN=1,1 + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("+CFUN=15")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000); // TODO: Verify delay timing here + if (waitResponse(10000L) != 1) { return false; } + delay(3000); // TODO(?): Verify delay timing here return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+CPWROFF")); return waitResponse(40000L) == 1; } - bool radioOff() { - sendAT(GF("+CFUN=0")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000); - return true; - } - - bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE; /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - -TINY_GSM_MODEM_GET_SIMCCID_CCID() - - String getIMEI() { + protected: + // This uses "CGSN" instead of "GSN" + String getIMEIImpl() { sendAT(GF("+CGSN")); - if (waitResponse(GF(GSM_NL)) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL)) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); return res; } - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - 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")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; - } - + /* + * Generic network functions + */ + public: RegStatus getRegistrationStatus() { // Check first for EPS registration sendAT(GF("+CEREG?")); - if (waitResponse(GF(GSM_NL "+CEREG:")) != 1) { - return REG_UNKNOWN; - } + if (waitResponse(GF(GSM_NL "+CEREG:")) != 1) { return REG_UNKNOWN; } streamSkipUntil(','); /* Skip format (0) */ int status = stream.readStringUntil('\n').toInt(); waitResponse(); @@ -394,64 +314,43 @@ TINY_GSM_MODEM_GET_SIMCCID_CCID() } else { // Otherwise, check generic network status sendAT(GF("+CREG?")); - if (waitResponse(GF(GSM_NL "+CREG:")) != 1) { - return REG_UNKNOWN; - } + if (waitResponse(GF(GSM_NL "+CREG:")) != 1) { return REG_UNKNOWN; } streamSkipUntil(','); /* Skip format (0) */ - int status = stream.readStringUntil('\n').toInt(); + status = stream.readStringUntil('\n').toInt(); waitResponse(); return (RegStatus)status; } } - TINY_GSM_MODEM_GET_OPERATOR_COPS() - - /* - * Generic network functions - */ - -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); - if (s == REG_OK_HOME || s == REG_OK_ROAMING) - return true; - // else if (s == REG_UNKNOWN) // for some reason, it can hang at unknown.. - // return isGprsConnected(); - else return false; + return (s == REG_OK_HOME || s == REG_OK_ROAMING); } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - + public: bool setURAT(uint8_t urat) { // AT+URAT=[,[,<2ndPreferredAct>]] sendAT(GF("+COPS=2")); // Deregister from network - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } sendAT(GF("+URAT="), urat); // Radio Access Technology (RAT) selection - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } sendAT(GF("+COPS=0")); // Auto-register to the network - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return restart(); } /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { // gprsDisconnect(); sendAT(GF("+CGATT=1")); // attach to GPRS - if (waitResponse(360000L) != 1) { - return false; - } + if (waitResponse(360000L) != 1) { return false; } // Using CGDCONT sets up an "external" PCP context, i.e. a data connection // using the external IP stack (e.g. Windows dial up) and PPP link over the @@ -468,21 +367,17 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() waitResponse(); sendAT(GF("+CGACT=1,1")); // activate PDP profile/context 1 - if (waitResponse(150000L) != 1) { - return false; - } + if (waitResponse(150000L) != 1) { return false; } return true; } - bool gprsDisconnect() { + bool gprsDisconnectImpl() { // Mark all the sockets as closed // This ensures that asynchronously closed sockets are marked closed for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { - GsmClient* sock = sockets[mux]; - if (sock && sock->sock_connected) { - sock->sock_connected = false; - } + GsmClientSaraR4* sock = sockets[mux]; + if (sock && sock->sock_connected) { sock->sock_connected = false; } } // sendAT(GF("+CGACT=0,1")); // Deactivate PDP context 1 @@ -492,81 +387,44 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } sendAT(GF("+CGATT=0")); // detach from GPRS - if (waitResponse(360000L) != 1) { - return false; - } + if (waitResponse(360000L) != 1) { return false; } return true; } -TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() - /* * IP Address functions */ - - String getLocalIP() { - sendAT(GF("+CGPADDR")); - if (waitResponse(GF(GSM_NL "+CGPADDR:")) != 1) { - return ""; - } - streamSkipUntil(','); // Skip context id - String res = stream.readStringUntil('\r'); - if (waitResponse() != 1) { - return ""; - } - return res; - } - - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } + protected: + // Can follow the template in all function /* * Phone Call functions */ - - bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE; - - bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED; + protected: + // While the AT commands for call answer and hang-up are nominally supported, + // no voice calls are supported rendering them meaningless + bool callAnswerImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool callNumberImpl(const String& number) TINY_GSM_ATTR_NOT_AVAILABLE; + bool callHangupImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool + dtmfSendImpl(char cmd, int duration_ms = 100) TINY_GSM_ATTR_NOT_AVAILABLE; /* * Messaging functions */ - - String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool sendSMS(const String& number, const String& text) { - sendAT(GF("+CSCS=\"GSM\"")); // Set GSM default alphabet - waitResponse(); - sendAT(GF("+CMGF=1")); // Set preferred message format to text mode - waitResponse(); - sendAT(GF("+CMGS=\""), number, GF("\"")); // set the phone number - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); // Actually send the message - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - - bool sendSMS_UTF16(const String& number, const void* text, size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED; - + protected: + String sendUSSDImpl(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool sendSMS_UTF16Impl(const String& number, const void* text, + size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED; /* * Location functions */ - - String getGsmLocation() { + protected: + String getGsmLocationImpl() { sendAT(GF("+ULOC=2,3,0,120,1")); - if (waitResponse(30000L, GF(GSM_NL "+UULOC:")) != 1) { - return ""; - } + if (waitResponse(30000L, GF(GSM_NL "+UULOC:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -574,68 +432,71 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() } /* - * Battery & temperature functions + * GPS location functions */ + public: + // No functions of this type supported - uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * Time functions + */ + protected: + // Can follow the standard CCLK function in the template - int8_t getBattPercent() { + /* + * Battery & temperature functions + */ + protected: + uint16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + + int8_t getBattPercentImpl() { sendAT(GF("+CIND?")); - if (waitResponse(GF(GSM_NL "+CIND:")) != 1) { - return 0; - } + if (waitResponse(GF(GSM_NL "+CIND:")) != 1) { return 0; } - int res = stream.readStringUntil(',').toInt(); - int8_t percent = res*20; // return is 0-5 + int res = stream.readStringUntil(',').toInt(); + int8_t percent = res * 20; // return is 0-5 // Wait for final OK waitResponse(); return percent; } - uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; + uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { - percent = getBattPercent(); + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) { chargeState = 0; - milliVolts = 0; + percent = getBattPercent(); + milliVolts = 0; return true; } - float getTemperature() { + float getTemperatureImpl() { // First make sure the temperature is set to be in celsius sendAT(GF("+UTEMP=0")); // Would use 1 for Fahrenheit - if (waitResponse() != 1) { - return (float)-9999; - } + if (waitResponse() != 1) { return static_cast(-9999); } sendAT(GF("+UTEMP?")); if (waitResponse(GF(GSM_NL "+UTEMP:")) != 1) { - return (float)-9999; - } - int16_t res = stream.readStringUntil('\n').toInt(); - float temp = -9999; - if (res != -1) { - temp = ((float)res)/10; + return static_cast(-9999); } + int16_t res = stream.readStringUntil('\n').toInt(); + float temp = -9999; + if (res != -1) { temp = (static_cast(res)) / 10; } return temp; } /* * Client related functions */ - -protected: - + protected: bool modemConnect(const char* host, uint16_t port, uint8_t* mux, bool ssl = false, int timeout_s = 120) { - uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; - unsigned long startMillis = millis(); + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + uint32_t startMillis = millis(); // create a socket sendAT(GF("+USOCR=6")); // reply is +USOCR: ## of socket created - if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) { - return false; - } + if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) { return false; } *mux = stream.readStringUntil('\n').toInt(); waitResponse(); @@ -668,35 +529,33 @@ protected: DBG("### Opening socket asynchronously! Socket cannot be used until " "the URC '+UUSOCO' appears."); sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port, ",1"); - if (waitResponse(timeout_ms, GF(GSM_NL "+UUSOCO:")) == 1) { + if (waitResponse(timeout_ms - (millis() - startMillis), + GF(GSM_NL "+UUSOCO:")) == 1) { stream.readStringUntil(',').toInt(); // skip repeated mux int connection_status = stream.readStringUntil('\n').toInt(); DBG("### Waited", millis() - startMillis, "ms for socket to open"); return (0 == connection_status); } else { - DBG("### Waited", millis() - startMillis, "but never got socket open notice"); + DBG("### Waited", millis() - startMillis, + "but never got socket open notice"); return false; } } else { // use synchronous open sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port); - int rsp = waitResponse(timeout_ms); + int rsp = waitResponse(timeout_ms - (millis() - startMillis)); return (1 == rsp); } } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { sendAT(GF("+USOWR="), mux, ',', (uint16_t)len); - if (waitResponse(GF("@")) != 1) { - return 0; - } + if (waitResponse(GF("@")) != 1) { return 0; } // 50ms delay, see AT manual section 25.10.4 delay(50); - stream.write((uint8_t*)buff, len); + stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) { - return 0; - } + if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) { return 0; } streamSkipUntil(','); // Skip mux int sent = stream.readStringUntil('\n').toInt(); waitResponse(); // sends back OK after the confirmation of number sent @@ -705,16 +564,12 @@ protected: size_t modemRead(size_t size, uint8_t mux) { sendAT(GF("+USORD="), mux, ',', (uint16_t)size); - if (waitResponse(GF(GSM_NL "+USORD:")) != 1) { - return 0; - } + if (waitResponse(GF(GSM_NL "+USORD:")) != 1) { return 0; } streamSkipUntil(','); // Skip mux int len = stream.readStringUntil(',').toInt(); streamSkipUntil('\"'); - for (int i=0; isock_connected = modemGetConnected(mux); - } + if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); } DBG("### AVAILABLE:", result, "on", mux); return result; } @@ -746,7 +599,7 @@ protected: // NOTE: Querying a closed socket gives an error "operation not allowed" sendAT(GF("+USOCTL="), mux, ",10"); uint8_t res = waitResponse(GF(GSM_NL "+USOCTL:")); - if (res != 1) return false; + if (res != 1) { return false; } streamSkipUntil(','); // Skip mux streamSkipUntil(','); // Skip type @@ -767,20 +620,16 @@ protected: return (result != 0); } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -788,15 +637,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -819,7 +668,7 @@ TINY_GSM_MODEM_STREAM_UTILITIES() int mux = stream.readStringUntil(',').toInt(); int len = stream.readStringUntil('\n').toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; + sockets[mux]->got_data = true; sockets[mux]->sock_available = len; } data = ""; @@ -832,7 +681,7 @@ TINY_GSM_MODEM_STREAM_UTILITIES() data = ""; DBG("### URC Sock Closed: ", mux); } else if (data.endsWith(GF("+UUSOCO:"))) { - int mux = stream.readStringUntil('\n').toInt(); + int mux = stream.readStringUntil('\n').toInt(); int socket_error = stream.readStringUntil('\n').toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux] && socket_error == 0) { @@ -843,12 +692,10 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } data = ""; } // data.replace(GSM_NL, "/"); @@ -856,8 +703,7 @@ finish: return index; } - uint8_t waitResponse(uint32_t timeout_ms, - GsmConstStr r1 = GFP(GSM_OK), + 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) { @@ -865,20 +711,19 @@ finish: 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; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; - bool has2GFallback; - bool supportsAsyncSockets; + protected: + Stream& stream; + GsmClientSaraR4* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; + bool has2GFallback; + bool supportsAsyncSockets; }; -#endif +#endif // SRC_TINYGSMCLIENTSARAR4_H_ diff --git a/src/TinyGsmClientSequansMonarch.h b/src/TinyGsmClientSequansMonarch.h index c333ed1..31dfcae 100644 --- a/src/TinyGsmClientSequansMonarch.h +++ b/src/TinyGsmClientSequansMonarch.h @@ -6,30 +6,22 @@ * @date Jan 2019 */ -#ifndef TinyGsmClientSequansMonarch_h -#define TinyGsmClientSequansMonarch_h +#ifndef SRC_TINYGSMCLIENTSEQUANSMONARCH_H_ +#define SRC_TINYGSMCLIENTSEQUANSMONARCH_H_ -//#define TINY_GSM_DEBUG Serial - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 64 -#endif +// #define TINY_GSM_DEBUG Serial #define TINY_GSM_MUX_COUNT 6 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -48,156 +40,144 @@ enum SocketStatus { SOCK_OPENING = 6, }; - class TinyGsmSequansMonarch -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmSequansMonarch; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} - - GsmClient(TinyGsmSequansMonarch& modem, uint8_t mux = 1) { - init(&modem, mux); - } + : public TinyGsmModem { + friend class TinyGsmModem; - virtual ~GsmClient() {} - - bool init(TinyGsmSequansMonarch* modem, uint8_t mux = 1) { - this->at = modem; - this->mux = mux; - sock_available = 0; - prev_check = 0; - sock_connected = false; - got_data = false; + /* + * Inner Client + */ + public: + class GsmClientSequansMonarch : public GsmClient { + friend class TinyGsmSequansMonarch; - // adjust for zero indexed socket array vs Sequans' 1 indexed mux numbers - // using modulus will force 6 back to 0 - at->sockets[mux % TINY_GSM_MUX_COUNT] = this; + public: + GsmClientSequansMonarch() {} - return true; - } - -public: - virtual int connect(const char* host, uint16_t port, int timeout_s) { - if (sock_connected) stop(); - TINY_GSM_YIELD(); - rx.clear(); - sock_connected = at->modemConnect(host, port, mux, false, timeout_s); - return sock_connected; - } + explicit GsmClientSequansMonarch(TinyGsmSequansMonarch& modem, + uint8_t mux = 1) { + init(&modem, mux); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + bool init(TinyGsmSequansMonarch* modem, uint8_t mux = 1) { + this->at = modem; + this->mux = mux; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() - at->sendAT(GF("+SQNSH="), mux); - sock_connected = false; - at->waitResponse(); - } + // adjust for zero indexed socket array vs Sequans' 1 indexed mux numbers + // using modulus will force 6 back to 0 + at->sockets[mux % TINY_GSM_MUX_COUNT] = this; - virtual void stop() { stop(15000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + int connect(const char* host, uint16_t port, int timeout_s) { + if (sock_connected) stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() + void stop(uint32_t maxWaitMs) { + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+SQNSH="), mux); + sock_connected = false; + at->waitResponse(); + } + void stop() override { + stop(15000L); + } -TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() + /* + * Extended API + */ -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; /* - * Extended API + * Inner Secure Client */ + public: + class GsmClientSecureSequansMonarch : public GsmClientSequansMonarch { + public: + GsmClientSecureSequansMonarch() {} - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + explicit GsmClientSecureSequansMonarch(TinyGsmSequansMonarch& modem, + uint8_t mux = 1) + : GsmClientSequansMonarch(modem, mux) {} -private: - TinyGsmSequansMonarch* at; - uint8_t mux; - uint16_t sock_available; - uint32_t prev_check; - bool sock_connected; - bool got_data; - RxFifo rx; -}; - - class GsmClientSecure : public GsmClient { -public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmSequansMonarch& modem, uint8_t mux = 1) - : GsmClient(modem, mux) - {} - - virtual ~GsmClientSecure() {} - -protected: + protected: bool strictSSL = false; -public: - virtual int connect(const char* host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - - // configure security profile 1 with parameters: - if (strictSSL) { - // require minimum of TLS 1.2 (3) - // only support cipher suite 0x3D: TLS_RSA_WITH_AES_256_CBC_SHA256 - // verify server certificate against imported CA certs 0 and enforce - // validity period (3) - at->sendAT(GF("+SQNSPCFG=1,3,\"0x3D\",3,0,,,\"\",\"\"")); - } else { - // use TLS 1.0 or higher (1) - // support wider variety of cipher suites - // do not verify server certificate (0) - at->sendAT(GF("+SQNSPCFG=1,1,\"0x2F;0x35;0x3C;0x3D\",0,,,,\"\",\"\"")); - } - if (at->waitResponse() != 1) { - DBG("failed to configure security profile"); - return false; - } - - sock_connected = at->modemConnect(host, port, mux, true, timeout_s); - return sock_connected; - } + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + + // configure security profile 1 with parameters: + if (strictSSL) { + // require minimum of TLS 1.2 (3) + // only support cipher suite 0x3D: TLS_RSA_WITH_AES_256_CBC_SHA256 + // verify server certificate against imported CA certs 0 and enforce + // validity period (3) + at->sendAT(GF("+SQNSPCFG=1,3,\"0x3D\",3,0,,,\"\",\"\"")); + } else { + // use TLS 1.0 or higher (1) + // support wider variety of cipher suites + // do not verify server certificate (0) + at->sendAT(GF("+SQNSPCFG=1,1,\"0x2F;0x35;0x3C;0x3D\",0,,,,\"\",\"\"")); + } + if (at->waitResponse() != 1) { + DBG("failed to configure security profile"); + return false; + } - void setStrictSSL(bool strict) { - strictSSL = strict; - } + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } -}; + void setStrictSSL(bool strict) { + strictSSL = strict; + } + }; -public: - TinyGsmSequansMonarch(Stream& stream) : stream(stream) { + /* + * Constructor + */ + public: + explicit TinyGsmSequansMonarch(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + protected: + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } + if (!testAT()) { return false; } sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } #ifdef TINY_GSM_DEBUG sendAT(GF("+CMEE=2")); // turn on verbose error codes @@ -209,133 +189,87 @@ public: DBG(GF("### Modem:"), getModemName()); // Make sure the module is enabled. Unlike others, the VZN20Q powers on - // with CFUN=0 not CFUN=1 (that is, at minimum functionality instead of full functionality - // The module cannot even detect the sim card if the cellular functionality is disabled so - // unless we explicitly enable the functionality the init will fail. + // with CFUN=0 not CFUN=1 (that is, at minimum functionality instead of full + // functionality The module cannot even detect the sim card if the cellular + // functionality is disabled so unless we explicitly enable the + // functionality the init will fail. sendAT(GF("+CFUN=1")); waitResponse(); + // Enable automatic time zome update + sendAT(GF("+CTZU=1")); + if (waitResponse(10000L) != 1) { return false; } + 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 { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { - // return "Sequans Monarch"; - sendAT(GF("+CGMI")); - String res1; - if (waitResponse(1000L, res1) != 1) { - return "u-blox Cellular Modem"; - } - res1.replace(GSM_NL "OK" GSM_NL, ""); - res1.trim(); - - sendAT(GF("+CGMM")); - String res2; - if (waitResponse(1000L, res2) != 1) { - return "u-blox Cellular Modem"; - } - res2.replace(GSM_NL "OK" GSM_NL, ""); - res2.trim(); - - String name = res1 + String(' ') + res2; - return name; - } - -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - - void maintain() { + void maintainImpl() { for (int mux = 1; mux <= TINY_GSM_MUX_COUNT; mux++) { - GsmClient* sock = sockets[mux % TINY_GSM_MUX_COUNT]; + GsmClientSequansMonarch* sock = sockets[mux % TINY_GSM_MUX_COUNT]; if (sock && sock->got_data) { - sock->got_data = false; + sock->got_data = false; sock->sock_available = modemGetAvailable(mux); // modemGetConnected() always checks the state of ALL socks modemGetConnected(); } } - while (stream.available()) { - waitResponse(15, NULL, NULL); - } + while (stream.available()) { waitResponse(15, NULL, NULL); } } - bool factoryDefault() { - sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write - waitResponse(); - sendAT(GF("+IPR=0")); // Auto-baud - waitResponse(); - sendAT(GF("+IFC=0,0")); // No Flow Control - waitResponse(); - sendAT(GF("+ICF=3,3")); // 8 data 0 parity 1 stop - waitResponse(); - sendAT(GF("+CSCLK=0")); // Disable Slow Clock - waitResponse(); - sendAT(GF("&W")); // Write configuration - return waitResponse() == 1; + bool thisHasGPRS() { + return true; } -TINY_GSM_MODEM_GET_INFO_ATI() + bool thisHasWifi() { + return false; + } - bool hasSSL() { + bool thisHasSSL() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } + protected: + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("+CFUN=0")); - int res = waitResponse(20000L, GFP(GSM_OK), GFP(GSM_ERROR), GF("+SYSSTART")) ; - if (res != 1 && res != 3) { - return false; - } + int res = + waitResponse(20000L, GFP(GSM_OK), GFP(GSM_ERROR), GF("+SYSSTART")); + if (res != 1 && res != 3) { return false; } sendAT(GF("+CFUN=1,1")); res = waitResponse(20000L, GF("+SYSSTART"), GFP(GSM_ERROR)); - if (res != 1 && res != 3) { - return false; - } + if (res != 1 && res != 3) { return false; } delay(1000); return init(); } - bool poweroff() { + bool powerOffImpl() { + // NOTE: The only way to turn the modem back on after this shutdown is with + // a hard reset sendAT(GF("+SQNSSHDN")); return waitResponse(); } - bool radioOff() { - sendAT(GF("+CFUN=0")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000); - return true; - } - - /* - When power saving is enabled, UART0 interface is activated with sleep mode support. - Module power state is controlled by RTS0 line. When no activity on UART, CTS line - will be set to OFF state (driven high level) milliseconds (100ms to 10s, - default 5s) after the last sent character, then module will go to sleep mode as soon - as DTE set RTS line to OFF state (driver high level). - */ - bool sleepEnable(bool enable = true) { + // When power saving is enabled, UART0 interface is activated with sleep mode + // support. Module power state is controlled by RTS0 line. When no activity + // on UART, CTS line will be set to OFF state (driven high level) + // milliseconds (100ms to 10s, default 5s) after the last sent character, + // then module will go to sleep mode as soon as DTE set RTS line to OFF state + // (driver high level). + bool sleepEnableImpl(bool enable = true) { sendAT(GF("+SQNIPSCFG="), enable); return waitResponse() == 1; } @@ -343,68 +277,36 @@ TINY_GSM_MODEM_GET_INFO_ATI() /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - - String getSimCCID() { + protected: + String getSimCCIDImpl() { sendAT(GF("+SQNCCID")); - if (waitResponse(GF(GSM_NL "+SQNCCID:")) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL "+SQNCCID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); return res; } -TINY_GSM_MODEM_GET_IMEI_GSN() - - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - 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")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; - } - -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CEREG) - -TINY_GSM_MODEM_GET_OPERATOR_COPS() - /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CEREG"); + } -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); - if (s == REG_OK_HOME || s == REG_OK_ROAMING) { - // DBG(F("connected with status:"), s); - return true; - } else { - return false; - } + return (s == REG_OK_HOME || s == REG_OK_ROAMING); } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { gprsDisconnect(); // Define the PDP context (This uses context #3!) @@ -423,123 +325,89 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() // Attach to GPRS sendAT(GF("+CGATT=1")); - if (waitResponse(60000L) != 1) - return false; + if (waitResponse(60000L) != 1) { return false; } return true; } - bool gprsDisconnect() { + bool gprsDisconnectImpl() { sendAT(GF("+CGATT=0")); - if (waitResponse(60000L) != 1) - return false; + if (waitResponse(60000L) != 1) { return false; } return true; } - bool isGprsConnected() { - sendAT(GF("+CGATT?")); - if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { - return false; - } - int res = stream.readStringUntil('\n').toInt(); - waitResponse(); - if (res != 1) - return false; - - return true; - } - - /* * IP Address functions */ - - String getLocalIP() { + protected: + String getLocalIPImpl() { sendAT(GF("+CGPADDR=3")); - if (waitResponse(10000L, GF("+CGPADDR: 3,\"")) != 1) { - return ""; - } + if (waitResponse(10000L, GF("+CGPADDR: 3,\"")) != 1) { return ""; } String res = stream.readStringUntil('\"'); waitResponse(); return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - - /* * Phone Call functions */ + protected: + bool callAnswerImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool + dtmfSendImpl(char cmd, int duration_ms = 100) TINY_GSM_ATTR_NOT_AVAILABLE; - bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE; - - bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED; - - String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool sendSMS(const String& number, const String& text) { - sendAT(GF("+CMGF=1")); - waitResponse(); - // Set GSM 7 bit default alphabet (3GPP TS 23.038) - sendAT(GF("+CSCS=\"GSM\"")); - waitResponse(); - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - - bool sendSMS_UTF16(const String& number, const void* text, size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED; + /* + * Messaging functions + */ + protected: + // Follows all messaging functions per template /* * Location functions */ + protected: + String getGsmLocationImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * GPS location functions + */ + public: + // No functions of this type supported + /* + * Time functions + */ + protected: + // Can follow the standard CCLK function in the template /* * Battery & temperature functions */ - - uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; - 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; - - float getTemperature() { + protected: + uint16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + int8_t getBattPercentImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE; + + float getTemperatureImpl() { sendAT(GF("+SMDTH")); if (waitResponse(10000L, GF("+SMDTH: ")) != 1) { - return (float)-9999; + return static_cast(-9999); } String res; - if (waitResponse(1000L, res) != 1) { - return (float)-9999; - } - if (res.indexOf("ERROR") >= 0) { - return (float)-9999; - } + if (waitResponse(1000L, res) != 1) { return static_cast(-9999); } + if (res.indexOf("ERROR") >= 0) { return static_cast(-9999); } return res.toFloat(); } -protected: + protected: bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false, int timeout_s = 75) { - int rsp; - unsigned long startMillis = millis(); - uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + int rsp; + uint32_t startMillis = millis(); + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; if (ssl) { // enable SSl and use security profile 1 @@ -552,24 +420,28 @@ protected: } // Socket configuration - //AT+SQNSCFG:, , , , , + // AT+SQNSCFG:, , , , , // = Connection ID = mux - // = PDP context ID = 3 - this is number set up above in the GprsConnect function + // = PDP context ID = 3 - this is number set up above in the + // GprsConnect function // = Packet Size, used for online data mode only = 300 (default) // = Max timeout in seconds = 90 (default) - // = Connection timeout in hundreds of milliseconds = 600 (default) - // = Data sending timeout in hundreds of milliseconds, used for online data mode only = 50 (default) + // = Connection timeout in hundreds of milliseconds + // = 600 (default) + // = Data sending timeout in hundreds of milliseconds, + // used for online data mode only = 50 (default) sendAT(GF("+SQNSCFG="), mux, GF(",3,300,90,600,50")); waitResponse(5000L); // Socket configuration extended - //AT+SQNSCFGEXT:, , , , , + // AT+SQNSCFGEXT:, , , , + // , // = Connection ID = mux // = Send/Receive URC model = 1 - data amount mode - // = Receive data mode = 0 - data as text (1 would be as hex) + // = Receive data mode = 0 - data as text (1 for hex) // = unused = 0 // = Listen auto-response mode = 0 - deactivated - // = Send data mode = 0 - data as text (1 would be as hex) + // = Send data mode = 0 - data as text (1 for hex) sendAT(GF("+SQNSCFGEXT="), mux, GF(",1,0,0,0,0")); waitResponse(5000L); @@ -578,20 +450,20 @@ protected: // = Connection ID = mux // = Transmission protocol = 0 - TCP (1 for UDP) // = Remote host port to contact - // = Any valid IP address in the format “xxx.xxx.xxx.xxx” or any host name solved with a DNS query - // = Socket closure behaviour for TCP, has no effect for UDP = 0 - local port closes when remote does (default) + // = Any valid IP address in the format “xxx.xxx.xxx.xxx” or any + // host name solved with a DNS query + // = Socket closure behaviour for TCP, has no effect for UDP + // = 0 - local port closes when remote does (default) // = UDP connection local port, has no effect for TCP connections. // = Connection mode = 1 - command mode connection // = Applies to UDP only - sendAT(GF("+SQNSD="), mux, ",0,", port, ',', GF("\""), host, GF("\""), ",0,0,1"); - rsp = waitResponse((timeout_ms - (millis() - startMillis)), - GFP(GSM_OK), - GFP(GSM_ERROR), - GF("NO CARRIER" GSM_NL) - ); + sendAT(GF("+SQNSD="), mux, ",0,", port, ',', GF("\""), host, GF("\""), + ",0,0,1"); + rsp = waitResponse((timeout_ms - (millis() - startMillis)), GFP(GSM_OK), + GFP(GSM_ERROR), GF("NO CARRIER" GSM_NL)); // creation of socket failed immediately. - if (rsp != 1) return false; + if (rsp != 1) { return false; } // wait until we get a good status bool connected = false; @@ -602,7 +474,6 @@ protected: return connected; } - int modemSend(const void* buff, size_t len, uint8_t mux) { if (sockets[mux % TINY_GSM_MUX_COUNT]->sock_connected == false) { DBG("### Sock closed, cannot send data!"); @@ -611,7 +482,7 @@ protected: sendAT(GF("+SQNSSENDEXT="), mux, ',', (uint16_t)len); waitResponse(10000L, GF(GSM_NL "> ")); - stream.write((uint8_t*)buff, len); + stream.write(reinterpret_cast(buff), len); stream.flush(); if (waitResponse() != 1) { DBG("### no OK after send"); @@ -630,8 +501,8 @@ protected: // delay(50); // } // if (gotPrompt) { - // stream.write((uint8_t*)buff, len); - // stream.write((char)0x1A); + // stream.write(reinterpret_cast(buff), len); + // stream.write(reinterpret_cast0x1A); // stream.flush(); // if (waitResponse() != 1) { // DBG("### no OK after send"); @@ -642,12 +513,9 @@ protected: // return 0; } - size_t modemRead(size_t size, uint8_t mux) { sendAT(GF("+SQNSRECV="), mux, ',', (uint16_t)size); - if (waitResponse(GF("+SQNSRECV: ")) != 1) { - return 0; - } + if (waitResponse(GF("+SQNSRECV: ")) != 1) { return 0; } streamSkipUntil(','); // Skip mux int len = stream.readStringUntil('\n').toInt(); for (int i = 0; i < len; i++) { @@ -670,9 +538,9 @@ protected: sendAT(GF("+SQNSI="), mux); size_t result = 0; if (waitResponse(GF("+SQNSI:")) == 1) { - streamSkipUntil(','); // Skip mux - streamSkipUntil(','); // Skip total sent - streamSkipUntil(','); // Skip total received + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip total sent + streamSkipUntil(','); // Skip total received result = stream.readStringUntil(',').toInt(); // keep data not yet read waitResponse(); } @@ -685,9 +553,7 @@ protected: // six possible sockets. sendAT(GF("+SQNSS")); for (int muxNo = 1; muxNo <= TINY_GSM_MUX_COUNT; muxNo++) { - if (waitResponse(GFP(GSM_OK), GF(GSM_NL "+SQNSS: ")) != 2) { - break; - }; + if (waitResponse(GFP(GSM_OK), GF(GSM_NL "+SQNSS: ")) != 2) { break; } uint8_t status = 0; // if (stream.readStringUntil(',').toInt() != muxNo) { // check the mux no // DBG("### Warning: misaligned mux numbers!"); @@ -704,26 +570,24 @@ protected: // SOCK_LISTENING = 4, // SOCK_INCOMING = 5, // SOCK_OPENING = 6, - sockets[muxNo % TINY_GSM_MUX_COUNT]->sock_connected = \ - ((status != SOCK_CLOSED) && (status != SOCK_INCOMING) && (status != SOCK_OPENING)); + sockets[muxNo % TINY_GSM_MUX_COUNT]->sock_connected = + ((status != SOCK_CLOSED) && (status != SOCK_INCOMING) && + (status != SOCK_OPENING)); } waitResponse(); // Should be an OK at the end return sockets[mux % TINY_GSM_MUX_COUNT]->sock_connected; } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL, - GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -731,15 +595,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -760,7 +624,7 @@ TINY_GSM_MODEM_STREAM_UTILITIES() int len = stream.readStringUntil('\n').toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux % TINY_GSM_MUX_COUNT]) { - sockets[mux % TINY_GSM_MUX_COUNT]->got_data = true; + sockets[mux % TINY_GSM_MUX_COUNT]->got_data = true; sockets[mux % TINY_GSM_MUX_COUNT]->sock_available = len; } data = ""; @@ -776,12 +640,10 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } data = ""; } // data.replace(GSM_NL, "/"); @@ -790,23 +652,24 @@ finish: } uint8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL, + 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 = NULL, - 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; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientSequansMonarch* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTSEQUANSMONARCH_H_ diff --git a/src/TinyGsmClientUBLOX.h b/src/TinyGsmClientUBLOX.h index c35f10f..de27737 100644 --- a/src/TinyGsmClientUBLOX.h +++ b/src/TinyGsmClientUBLOX.h @@ -6,32 +6,23 @@ * @date Nov 2016 */ -#ifndef TinyGsmClientUBLOX_h -#define TinyGsmClientUBLOX_h -//#pragma message("TinyGSM: TinyGsmClientUBLOX") +#ifndef SRC_TINYGSMCLIENTUBLOX_H_ +#define SRC_TINYGSMCLIENTUBLOX_H_ +// #pragma message("TinyGSM: TinyGsmClientUBLOX") -//#define TINY_GSM_DEBUG Serial - -#if !defined(TINY_GSM_RX_BUFFER) - #define TINY_GSM_RX_BUFFER 64 -#endif +// #define TINY_GSM_DEBUG Serial #define TINY_GSM_MUX_COUNT 7 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; - enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -40,127 +31,115 @@ enum RegStatus { REG_UNKNOWN = 4, }; - class TinyGsmUBLOX -{ + : public TinyGsmModem { + friend class TinyGsmModem; -public: - -class GsmClient : public Client -{ - friend class TinyGsmUBLOX; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} + /* + * Inner Client + */ + public: + class GsmClientUBLOX : public GsmClient { + friend class TinyGsmUBLOX; - GsmClient(TinyGsmUBLOX& modem, uint8_t mux = 0) { - init(&modem, mux); - } + public: + GsmClientUBLOX() {} - virtual ~GsmClient(){} + explicit GsmClientUBLOX(TinyGsmUBLOX& modem, uint8_t mux = 0) { + init(&modem, mux); + } - bool init(TinyGsmUBLOX* modem, uint8_t mux = 0) { - this->at = modem; - this->mux = mux; - sock_available = 0; - prev_check = 0; - sock_connected = false; - got_data = false; + bool init(TinyGsmUBLOX* modem, uint8_t mux = 0) { + this->at = modem; + this->mux = mux; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; - at->sockets[mux] = this; + at->sockets[mux] = this; - return true; - } + return true; + } -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); - uint8_t oldMux = mux; - sock_connected = at->modemConnect(host, port, &mux, false, timeout_s); - if (mux != oldMux) { + uint8_t oldMux = mux; + sock_connected = at->modemConnect(host, port, &mux, false, timeout_s); + if (mux != oldMux) { DBG("WARNING: Mux number changed from", oldMux, "to", mux); at->sockets[oldMux] = NULL; - } - at->sockets[mux] = this; - at->maintain(); - - return sock_connected; - } - -TINY_GSM_CLIENT_CONNECT_OVERLOADS() - - virtual void stop(uint32_t maxWaitMs) { - TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() - at->sendAT(GF("+USOCL="), mux); - at->waitResponse(); // should return within 1s - sock_connected = false; - } - - virtual void stop() { stop(15000L); } + } + at->sockets[mux] = this; + at->maintain(); -TINY_GSM_CLIENT_WRITE() + return sock_connected; + } + int connect(IPAddress ip, uint16_t port, int timeout_s) { + return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 75); + } -TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() + void stop(uint32_t maxWaitMs) { + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+USOCL="), mux); + at->waitResponse(); // should return within 1s + sock_connected = false; + } + void stop() override { + stop(15000L); + } -TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() + /* + * Extended API + */ -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; /* - * Extended API + * Inner Secure Client */ + public: + class GsmClientSecureUBLOX : public GsmClientUBLOX { + public: + GsmClientSecureUBLOX() {} - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; - -private: - TinyGsmUBLOX* at; - uint8_t mux; - uint16_t sock_available; - uint32_t prev_check; - bool sock_connected; - bool got_data; - RxFifo rx; -}; - - -class GsmClientSecure : public GsmClient -{ -public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmUBLOX& modem, uint8_t mux = 1) - : GsmClient(modem, mux) - {} + explicit GsmClientSecureUBLOX(TinyGsmUBLOX& modem, uint8_t mux = 1) + : GsmClientUBLOX(modem, mux) {} - virtual ~GsmClientSecure(){} - -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - stop(); - TINY_GSM_YIELD(); - rx.clear(); - uint8_t oldMux = mux; - sock_connected = at->modemConnect(host, port, &mux, true, timeout_s); - if (mux != oldMux) { + public: + int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + uint8_t oldMux = mux; + sock_connected = at->modemConnect(host, port, &mux, true, timeout_s); + if (mux != oldMux) { DBG("WARNING: Mux number changed from", oldMux, "to", mux); at->sockets[oldMux] = NULL; + } + at->sockets[mux] = this; + at->maintain(); + return sock_connected; } - at->sockets[mux] = this; - at->maintain(); - return sock_connected; - } -}; + }; - -public: - - TinyGsmUBLOX(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmUBLOX(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } @@ -168,21 +147,13 @@ public: * Basic functions */ - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - if (!testAT()) { - return false; - } + if (!testAT()) { return false; } - sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } #ifdef TINY_GSM_DEBUG sendAT(GF("+CMEE=2")); // turn on verbose error codes @@ -193,176 +164,127 @@ public: getModemName(); + // Enable automatic time zome update + sendAT(GF("+CTZU=1")); + if (waitResponse(10000L) != 1) { return false; } + 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 { + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true return (ret == SIM_READY || ret == SIM_LOCKED); } } - String getModemName() { + String getModemNameImpl() { sendAT(GF("+CGMI")); String res1; - if (waitResponse(1000L, res1) != 1) { - return "u-blox Cellular Modem"; - } + if (waitResponse(1000L, res1) != 1) { return "u-blox Cellular Modem"; } res1.replace(GSM_NL "OK" GSM_NL, ""); res1.trim(); sendAT(GF("+GMM")); String res2; - if (waitResponse(1000L, res2) != 1) { - return "u-blox Cellular Modem"; - } + if (waitResponse(1000L, res2) != 1) { return "u-blox Cellular Modem"; } res2.replace(GSM_NL "OK" GSM_NL, ""); res2.trim(); 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!"); - } - else if (name.startsWith("u-blox SARA-N2")) { + } else if (name.startsWith("u-blox SARA-N2")) { DBG("### SARA N2 NB-IoT modems not supported!"); } return name; } -TINY_GSM_MODEM_SET_BAUD_IPR() - -TINY_GSM_MODEM_TEST_AT() - -TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() - - bool factoryDefault() { + bool factoryDefaultImpl() { sendAT(GF("+UFACTORY=0,1")); // No factory restore, erase NVM waitResponse(); - sendAT(GF("+CFUN=16")); // Reset + sendAT(GF("+CFUN=16")); // Reset return waitResponse() == 1; } -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { + bool thisHasSSL() { return true; } - bool hasWifi() { + bool thisHasWifi() { return false; } - bool hasGPRS() { + bool thisHasGPRS() { return true; } /* * Power functions */ - - bool restart() { - if (!testAT()) { - return false; - } + protected: + bool restartImpl() { + if (!testAT()) { return false; } sendAT(GF("+CFUN=16")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000); // TODO: Verify delay timing here + if (waitResponse(10000L) != 1) { return false; } + delay(3000); // TODO(?): Verify delay timing here return init(); } - bool poweroff() { + bool powerOffImpl() { sendAT(GF("+CPWROFF")); return waitResponse(40000L) == 1; } - bool radioOff() { - sendAT(GF("+CFUN=0")); - if (waitResponse(10000L) != 1) { - return false; - } - delay(3000); - return true; - } - - bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; /* * SIM card functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - -TINY_GSM_MODEM_GET_SIMCCID_CCID() - - String getIMEI() { + protected: + String getIMEIImpl() { sendAT(GF("+CGSN")); - if (waitResponse(GF(GSM_NL)) != 1) { - return ""; - } + if (waitResponse(GF(GSM_NL)) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); return res; } - SimStatus getSimStatus(unsigned long timeout_ms = 10000L) { - 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")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; - } - -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG) - -TINY_GSM_MODEM_GET_OPERATOR_COPS() - /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CGREG"); + } -TINY_GSM_MODEM_GET_CSQ() - - bool isNetworkConnected() { + protected: + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); if (s == REG_OK_HOME || s == REG_OK_ROAMING) return true; else if (s == REG_UNKNOWN) // for some reason, it can hang at unknown.. return isGprsConnected(); - else return false; + else + return false; } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) { + protected: + bool gprsConnectImpl(const char* apn, const char* user = NULL, + const char* pwd = NULL) { gprsDisconnect(); sendAT(GF("+CGATT=1")); // attach to GPRS - if (waitResponse(360000L) != 1) { - return false; - } + if (waitResponse(360000L) != 1) { return false; } // Setting up the PSD profile/PDP context with the UPSD commands sets up an // "internal" PDP context, i.e. a data connection using the internal IP @@ -388,15 +310,16 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() waitResponse(); } - sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP on PSD profile 0 + sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP on PSD profile 0 waitResponse(); // Packet switched data action // AT+UPSDA=, // profile_id = 0: PSD profile identifier, in range 0-6 (NOT PDP context) - // action = 3: activate; it activates a PDP context with the specified profile, - // using the current parameters - sendAT(GF("+UPSDA=0,3")); // Activate the PDP context associated with profile 0 + // action = 3: activate; it activates a PDP context with the specified + // profile, using the current parameters + sendAT(GF( + "+UPSDA=0,3")); // Activate the PDP context associated with profile 0 if (waitResponse(360000L) != 1) { // Should return ok return false; } @@ -406,12 +329,13 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() // for the active PDP context associated with the specified PSD profile. // AT+UPSND=, // profile_id = 0: PSD profile identifier, in range 0-6 (NOT PDP context) - // param_tag = 8: PSD profile status: if the profile is active the return value is 1, 0 otherwise - sendAT(GF("+UPSND=0,8")); // Check if PSD profile 0 is now active + // param_tag = 8: PSD profile status: if the profile is active the return + // value is 1, 0 otherwise + sendAT(GF("+UPSND=0,8")); // Check if PSD profile 0 is now active int res = waitResponse(GF(",8,1"), GF(",8,0")); waitResponse(); // Should return another OK if (res == 1) { - return true; // It's now active + return true; // It's now active } else if (res == 2) { // If it's not active yet, wait for the +UUPSDA URC if (waitResponse(180000L, GF("+UUPSDA: 0")) != 1) { // 0=successful return false; @@ -424,89 +348,50 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() return true; } - bool gprsDisconnect() { - sendAT(GF("+UPSDA=0,4")); // Deactivate the PDP context associated with profile 0 - if (waitResponse(360000L) != 1) { - return false; - } + bool gprsDisconnectImpl() { + sendAT(GF( + "+UPSDA=0,4")); // Deactivate the PDP context associated with profile 0 + if (waitResponse(360000L) != 1) { return false; } sendAT(GF("+CGATT=0")); // detach from GPRS - if (waitResponse(360000L) != 1) { - return false; - } + if (waitResponse(360000L) != 1) { return false; } return true; } -TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() - /* * IP Address functions */ - - String getLocalIP() { + protected: + String getLocalIPImpl() { sendAT(GF("+UPSND=0,0")); - if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) { - return ""; - } - streamSkipUntil(','); // Skip PSD profile - streamSkipUntil('\"'); // Skip request type + if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) { return ""; } + streamSkipUntil(','); // Skip PSD profile + streamSkipUntil('\"'); // Skip request type String res = stream.readStringUntil('\"'); - if (waitResponse() != 1) { - return ""; - } + if (waitResponse() != 1) { return ""; } return res; } - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * Phone Call functions */ - - bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE; - - bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED; + protected: + // Can follow all of the phone call functions from the template /* * Messaging functions */ - - String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; - - bool sendSMS(const String& number, const String& text) { - sendAT(GF("+CSCS=\"GSM\"")); // Set GSM default alphabet - waitResponse(); - sendAT(GF("+CMGF=1")); // Set preferred message format to text mode - waitResponse(); - sendAT(GF("+CMGS=\""), number, GF("\"")); // set the phone number - if (waitResponse(GF(">")) != 1) { - return false; - } - stream.print(text); // Actually send the message - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - - bool sendSMS_UTF16(const String& number, const void* text, size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED; - + protected: + // Can follow all template functions /* * Location functions */ - - String getGsmLocation() { + protected: + String getGsmLocationImpl() { sendAT(GF("+ULOC=2,3,0,120,1")); - if (waitResponse(30000L, GF(GSM_NL "+UULOC:")) != 1) { - return ""; - } + if (waitResponse(30000L, GF(GSM_NL "+UULOC:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -514,48 +399,58 @@ TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() } /* - * Battery & temperature functions + * GPS location functions */ + public: + // No functions of this type supported - uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * Time functions + */ + protected: + // Can follow the standard CCLK function in the template + + /* + * Battery & temperature functions + */ + protected: + uint16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - int8_t getBattPercent() { + int8_t getBattPercentImpl() { sendAT(GF("+CIND?")); - if (waitResponse(GF(GSM_NL "+CIND:")) != 1) { - return 0; - } + if (waitResponse(GF(GSM_NL "+CIND:")) != 1) { return 0; } - int res = stream.readStringUntil(',').toInt(); - int8_t percent = res*20; // return is 0-5 + int res = stream.readStringUntil(',').toInt(); + int8_t percent = res * 20; // return is 0-5 // Wait for final OK waitResponse(); return percent; } - uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; + uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) { chargeState = 0; - percent = getBattPercent(); - milliVolts = 0; + percent = getBattPercent(); + milliVolts = 0; return true; } - // This would only available for a small number of modules in this group (TOBY-L) - float getTemperature() TINY_GSM_ATTR_NOT_IMPLEMENTED; + // This would only available for a small number of modules in this group + // (TOBY-L) + float getTemperatureImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; /* * Client related functions */ - -protected: - + protected: bool modemConnect(const char* host, uint16_t port, uint8_t* mux, - bool ssl = false, int timeout_s = 120) - { - uint32_t timeout_ms = ((uint32_t)timeout_s)*1000; + 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 + if (waitResponse(GF(GSM_NL "+USOCR:")) != + 1) { // reply is +USOCR: ## of socket created return false; } *mux = stream.readStringUntil('\n').toInt(); @@ -571,8 +466,8 @@ 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); @@ -582,17 +477,13 @@ protected: int16_t modemSend(const void* buff, size_t len, uint8_t mux) { sendAT(GF("+USOWR="), mux, ',', (uint16_t)len); - if (waitResponse(GF("@")) != 1) { - return 0; - } + if (waitResponse(GF("@")) != 1) { return 0; } // 50ms delay, see AT manual section 25.10.4 delay(50); - stream.write((uint8_t*)buff, len); + stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip mux + if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) { return 0; } + streamSkipUntil(','); // Skip mux int sent = stream.readStringUntil('\n').toInt(); waitResponse(); // sends back OK after the confirmation of number sent return sent; @@ -600,16 +491,12 @@ protected: size_t modemRead(size_t size, uint8_t mux) { sendAT(GF("+USORD="), mux, ',', (uint16_t)size); - if (waitResponse(GF(GSM_NL "+USORD:")) != 1) { - return 0; - } - streamSkipUntil(','); // Skip mux + if (waitResponse(GF(GSM_NL "+USORD:")) != 1) { return 0; } + streamSkipUntil(','); // Skip mux int len = stream.readStringUntil(',').toInt(); streamSkipUntil('\"'); - for (int i=0; isock_connected = modemGetConnected(mux); - } + if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); } DBG("### AVAILABLE:", result, "on", mux); return result; } @@ -641,11 +526,10 @@ protected: // NOTE: Querying a closed socket gives an error "operation not allowed" sendAT(GF("+USOCTL="), mux, ",10"); uint8_t res = waitResponse(GF(GSM_NL "+USOCTL:")); - if (res != 1) - return false; + if (res != 1) { return false; } - streamSkipUntil(','); // Skip mux - streamSkipUntil(','); // Skip type + streamSkipUntil(','); // Skip mux + streamSkipUntil(','); // Skip type int result = stream.readStringUntil('\n').toInt(); // 0: the socket is in INACTIVE status (it corresponds to CLOSED status // defined in RFC793 "TCP Protocol Specification" [112]) @@ -663,19 +547,16 @@ protected: return (result != 0); } -public: - /* - Utilities + * Utilities */ - -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + public: + // TODO(vshymanskyy): Optimize this! + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -683,15 +564,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(64); - int index = 0; - unsigned long startMillis = millis(); + int index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -714,7 +595,7 @@ TINY_GSM_MODEM_STREAM_UTILITIES() int mux = stream.readStringUntil(',').toInt(); int len = stream.readStringUntil('\n').toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; + sockets[mux]->got_data = true; sockets[mux]->sock_available = len; } data = ""; @@ -729,38 +610,36 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); - } + if (data.length()) { DBG("### Unhandled:", data); } 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) - { + 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) { 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; - -protected: - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientUBLOX* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; }; -#endif +#endif // SRC_TINYGSMCLIENTUBLOX_H_ diff --git a/src/TinyGsmClientXBee.h b/src/TinyGsmClientXBee.h index 79e743e..fa2eb4f 100644 --- a/src/TinyGsmClientXBee.h +++ b/src/TinyGsmClientXBee.h @@ -2,48 +2,45 @@ * @file TinyGsmClientXBee.h * @author Volodymyr Shymanskyy * @license LGPL-3.0 - * @copyright Copyright (c) 2016 Volodymyr Shymanskyy, XBee module by Sara Damiano + * @copyright Copyright (c) 2016 Volodymyr Shymanskyy, XBee module by Sara + * Damiano * @date Nov 2016 */ -#ifndef TinyGsmClientXBee_h -#define TinyGsmClientXBee_h -//#pragma message("TinyGSM: TinyGsmClientXBee") +#ifndef SRC_TINYGSMCLIENTXBEE_H_ +#define SRC_TINYGSMCLIENTXBEE_H_ +// #pragma message("TinyGSM: TinyGsmClientXBee") -//#define TINY_GSM_DEBUG Serial +// #define TINY_GSM_DEBUG Serial // XBee's do not support multi-plexing in transparent/command mode // The much more complicated API mode is needed for multi-plexing #define TINY_GSM_MUX_COUNT 1 -// XBee's have a default guard time of 1 second (1000ms, 10 extra for safety here) +// XBee's have a default guard time of 1 second (1000ms, 10 extra for safety +// here) #define TINY_GSM_XBEE_GUARD_TIME 1010 -#include +#include "TinyGsmCommon.h" #define GSM_NL "\r" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; // Use this to avoid too many entrances and exits from command mode. // The cellular Bee's often freeze up and won't respond when attempting // to enter command mode too many times. -#define XBEE_COMMAND_START_DECORATOR(nAttempts, failureReturn) \ - bool wasInCommandMode = inCommandMode; \ - if (!wasInCommandMode) { /* don't re-enter command mode if already in it */ \ - if (!commandMode(nAttempts)) return failureReturn; /* Return immediately if fails */ \ +#define XBEE_COMMAND_START_DECORATOR(nAttempts, failureReturn) \ + bool wasInCommandMode = inCommandMode; \ + if (!wasInCommandMode) { /* don't re-enter command mode if already in it */ \ + if (!commandMode(nAttempts)) \ + return failureReturn; /* Return immediately if fails */ \ } -#define XBEE_COMMAND_END_DECORATOR \ - if (!wasInCommandMode) { /* only exit if we weren't in command mode */ \ - exitCommand(); \ +#define XBEE_COMMAND_END_DECORATOR \ + if (!wasInCommandMode) { /* only exit if we weren't in command mode */ \ + exitCommand(); \ } - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; - enum RegStatus { REG_OK = 0, REG_UNREGISTERED = 1, @@ -54,7 +51,7 @@ enum RegStatus { // These are responses to the HS command to get "hardware series" enum XBeeType { - XBEE_UNKNOWN = 0, + XBEE_UNKNOWN = 0, XBEE_S6B_WIFI = 0x601, // Digi XBee® Wi-Fi XBEE_LTE1_VZN = 0xB01, // Digi XBee® Cellular LTE Cat 1 XBEE_3G = 0xB02, // Digi XBee® Cellular 3G @@ -62,240 +59,242 @@ enum XBeeType { XBEE3_LTEM_ATT = 0xB08, // Digi XBee3™ Cellular LTE-M }; - class TinyGsmXBee -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmXBee; - // typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} + : public TinyGsmModem { + friend class TinyGsmModem; - GsmClient(TinyGsmXBee& modem, uint8_t mux = 0) { - init(&modem, mux); - } + /* + * Inner Client + */ + public: + class GsmClientXBee : public GsmClient { + friend class TinyGsmXBee; - virtual ~GsmClient(){} + public: + GsmClientXBee() {} - bool init(TinyGsmXBee* modem, uint8_t mux = 0) { - this->at = modem; - this->mux = mux; - sock_connected = false; + explicit GsmClientXBee(TinyGsmXBee& modem, uint8_t mux = 0) { + init(&modem, mux); + } - at->sockets[mux] = this; + bool init(TinyGsmXBee* modem, uint8_t mux = 0) { + this->at = modem; + this->mux = mux; + sock_connected = false; - return true; - } + at->sockets[mux] = this; -public: - // NOTE: The XBee saves all connection information (ssid/pwd or apn AND last used IP address) - // in flash (NVM). When you turn it on it immediately prepares to re-connect to whatever was - // last set. The TCP connection itself is not opened until you attempt to send data. - // Because all settings are saved to flash, it is possible (or likely) that - // you could send data even if you haven't "made" any connection. - virtual int connect(const char *host, uint16_t port, int timeout_s) { - // NOTE: Not caling stop() or yeild() here - at->streamClear(); // Empty anything in the buffer before starting - sock_connected = at->modemConnect(host, port, mux, false, timeout_s); - return sock_connected; - } - virtual int connect(const char *host, uint16_t port) { - return connect(host, port, 75); - } - - virtual int connect(IPAddress ip, uint16_t port, int timeout_s) { - if (timeout_s != 0) { - DBG("Timeout [", timeout_s, "] doesn't apply here."); + return true; } - // NOTE: Not caling stop() or yeild() here - at->streamClear(); // Empty anything in the buffer before starting - sock_connected = at->modemConnect(ip, port, mux, false); - return sock_connected; - } - virtual int connect(IPAddress ip, uint16_t port) { - return connect(ip, port, 0); - } - virtual void stop(uint32_t maxWaitMs) { - at->streamClear(); // Empty anything 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() - // will return false after a stop has been ordered. This makes it play - // much more nicely with libraries like PubSubClient. - } + public: + // NOTE: The XBee saves all connection information (ssid/pwd or apn AND + // last used IP address) in flash (NVM). When you turn it on it immediately + // prepares to re-connect to whatever was last set. The TCP connection + // itself is not opened until you attempt to send data. Because all settings + // are saved to flash, it is possible (or likely) that you could send data + // even if you haven't "made" any connection. + int connect(const char* host, uint16_t port, int timeout_s) { + // NOTE: Not caling stop() or yeild() here + at->streamClear(); // Empty anything in the buffer before starting + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + int connect(const char* host, uint16_t port) override { + return connect(host, port, 75); + } - virtual void stop() { stop(5000L); } + 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, false); + return sock_connected; + } + int connect(IPAddress ip, uint16_t port) override { + return connect(ip, port, 0); + } - virtual size_t write(const uint8_t *buf, size_t size) { - TINY_GSM_YIELD(); - return at->modemSend(buf, size, mux); - } + void stop(uint32_t maxWaitMs) { + at->streamClear(); // Empty anything 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() + // will return false after a stop has been ordered. This makes it play + // much more nicely with libraries like PubSubClient. + } + void stop() override { + stop(5000L); + } - virtual size_t write(uint8_t c) { - return write(&c, 1); - } + size_t write(const uint8_t* buf, size_t size) override { + TINY_GSM_YIELD(); + return at->modemSend(buf, size, mux); + } - virtual size_t write(const char *str) { - if (str == NULL) return 0; - return write((const uint8_t *)str, strlen(str)); - } + size_t write(uint8_t c) override { + return write(&c, 1); + } - virtual int available() { - TINY_GSM_YIELD(); - return at->stream.available(); - /* - if (!rx.size() || at->stream.available()) { - at->maintain(); + size_t write(const char* str) { + if (str == NULL) return 0; + return write((const uint8_t*)str, strlen(str)); } - return at->stream.available() + rx.size(); - */ - } - virtual int read(uint8_t *buf, size_t size) { - TINY_GSM_YIELD(); - return at->stream.readBytes((char *)buf, size); - /* - size_t cnt = 0; - uint32_t _startMillis = millis(); - while (cnt < size && millis() - _startMillis < _timeout) { - size_t chunk = TinyGsmMin(size-cnt, rx.size()); - if (chunk > 0) { - rx.get(buf, chunk); - buf += chunk; - cnt += chunk; - continue; - } - // TODO: Read directly into user buffer? + int available() override { + TINY_GSM_YIELD(); + return at->stream.available(); + /* if (!rx.size() || at->stream.available()) { at->maintain(); } + return at->stream.available() + rx.size(); + */ } - return cnt; - */ - } - virtual int read() { - TINY_GSM_YIELD(); - return at->stream.read(); - /* - uint8_t c; - if (read(&c, 1) == 1) { - return c; + int read(uint8_t* buf, size_t size) override { + TINY_GSM_YIELD(); + return at->stream.readBytes(reinterpret_cast(buf), size); + /* + size_t cnt = 0; + uint32_t _startMillis = millis(); + while (cnt < size && millis() - _startMillis < _timeout) { + size_t chunk = TinyGsmMin(size-cnt, rx.size()); + if (chunk > 0) { + rx.get(buf, chunk); + buf += chunk; + cnt += chunk; + continue; + } + // TODO(vshymanskyy): Read directly into user buffer? + if (!rx.size() || at->stream.available()) { + at->maintain(); + } + } + return cnt; + */ } - return -1; - */ - } - - virtual int peek() { return at->stream.peek(); } - virtual void flush() { at->stream.flush(); } - virtual uint8_t connected() { - if (available()) { - return true; - // if we never got an IP, it can't be connected - } else if (at->savedIP == IPAddress(0, 0, 0, 0)){ - return false; + int read() override { + TINY_GSM_YIELD(); + return at->stream.read(); + /* + uint8_t c; + if (read(&c, 1) == 1) { + return c; + } + return -1; + */ } - return sock_connected; - // NOTE: We don't check or return - // modemGetConnected() because we don't - // want to go into command mode. - // return at->modemGetConnected(); - } - virtual operator bool() { return connected(); } - - /* - * Extended API - */ - - String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; - -private: - TinyGsmXBee* at; - uint8_t mux; - bool sock_connected; - // RxFifo rx; -}; - -class GsmClientSecure : public GsmClient -{ -public: - GsmClientSecure() {} + int peek() override { + return at->stream.peek(); + } + void flush() override { + at->stream.flush(); + } - GsmClientSecure(TinyGsmXBee& modem, uint8_t mux = 0) - : GsmClient(modem, mux) - {} + uint8_t connected() override { + if (available()) { + return true; + // if we never got an IP, it can't be connected + } else if (at->savedIP == IPAddress(0, 0, 0, 0)) { + return false; + } + return sock_connected; + // NOTE: We don't check or return + // modemGetConnected() because we don't + // want to go into command mode. + // return at->modemGetConnected(); + } + operator bool() override { + return connected(); + } - virtual ~GsmClientSecure(){} + /* + * Extended API + */ -public: - virtual int connect(const char *host, uint16_t port, int timeout_s) { - // NOTE: Not caling stop() or yeild() here - at->streamClear(); // Empty anything in the buffer before starting - sock_connected = at->modemConnect(host, port, mux, true, timeout_s); - return sock_connected; - } + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; - virtual int connect(IPAddress ip, uint16_t port, int timeout_s) { - if (timeout_s != 0) { - DBG("Timeout [", timeout_s, "] doesn't apply here."); + /* + * Inner Secure Client + */ + public: + class GsmClientSecureXBee : public GsmClientXBee { + public: + GsmClientSecureXBee() {} + + explicit GsmClientSecureXBee(TinyGsmXBee& modem, uint8_t mux = 0) + : GsmClientXBee(modem, mux) {} + + public: + int connect(const char* host, uint16_t port, int timeout_s) { + // NOTE: Not caling stop() or yeild() here + at->streamClear(); // Empty anything in the buffer before starting + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; } - // NOTE: Not caling stop() or yeild() here - at->streamClear(); // Empty anything in the buffer before starting - sock_connected = at->modemConnect(ip, port, mux, true); - return sock_connected; - } -}; - -public: + 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); + return sock_connected; + } + }; - TinyGsmXBee(Stream& stream) - : stream(stream) - { - beeType = XBEE_UNKNOWN; // Start not knowing what kind of bee it is - guardTime = TINY_GSM_XBEE_GUARD_TIME; // Start with the default guard time of 1 second - resetPin = -1; - savedIP = IPAddress(0,0,0,0); - savedHost = ""; - savedHostIP = IPAddress(0,0,0,0); - inCommandMode = false; - memset(sockets, 0, sizeof(sockets)); + /* + * Constructor + */ + public: + explicit TinyGsmXBee(Stream& stream) + : stream(stream), + guardTime(TINY_GSM_XBEE_GUARD_TIME), + beeType(XBEE_UNKNOWN), + resetPin(-1), + savedIP(IPAddress(0, 0, 0, 0)), + savedHost(""), + savedHostIP(IPAddress(0, 0, 0, 0)), + savedOperatingIP(IPAddress(0, 0, 0, 0)), + inCommandMode(false), + lastCommandModeMillis(0) { + // Start not knowing what kind of bee it is + // Start with the default guard time of 1 second + memset(sockets, 0, sizeof(sockets)); } TinyGsmXBee(Stream& stream, int8_t resetPin) - : stream(stream) - { - beeType = XBEE_UNKNOWN; // Start not knowing what kind of bee it is - guardTime = TINY_GSM_XBEE_GUARD_TIME; // Start with the default guard time of 1 second - this->resetPin = resetPin; - savedIP = IPAddress(0,0,0,0); - savedHost = ""; - savedHostIP = IPAddress(0,0,0,0); - inCommandMode = false; - memset(sockets, 0, sizeof(sockets)); + : stream(stream), + guardTime(TINY_GSM_XBEE_GUARD_TIME), + beeType(XBEE_UNKNOWN), + resetPin(resetPin), + savedIP(IPAddress(0, 0, 0, 0)), + savedHost(""), + savedHostIP(IPAddress(0, 0, 0, 0)), + savedOperatingIP(IPAddress(0, 0, 0, 0)), + inCommandMode(false), + lastCommandModeMillis(0) { + // Start not knowing what kind of bee it is + // Start with the default guard time of 1 second + memset(sockets, 0, sizeof(sockets)); } /* * Basic functions */ - bool begin(const char* pin = NULL) { - return init(pin); - } - - bool init(const char* pin = NULL) { + bool initImpl(const char* pin = NULL) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); if (resetPin >= 0) { @@ -312,13 +311,13 @@ public: sendAT(GF("AP0")); // Put in transparent mode bool ret_val = waitResponse() == 1; - sendAT(GF("GT64")); // shorten the guard time to 100ms + sendAT(GF("GT64")); // shorten the guard time to 100ms ret_val &= waitResponse() == 1; if (ret_val) guardTime = 110; - // Make sure the command mode drop-out time is long enough that we won't fall - // out of command mode without intentionally leaving it. This is the default - // drop out time of 0x64 x 100ms (10 seconds) + // Make sure the command mode drop-out time is long enough that we won't + // fall out of command mode without intentionally leaving it. This is the + // default drop out time of 0x64 x 100ms (10 seconds) sendAT(GF("CT64")); ret_val &= waitResponse() == 1; ret_val &= writeChanges(); @@ -330,14 +329,13 @@ public: return ret_val; } - String getModemName() { + String getModemNameImpl() { return getBeeName(); } - void setBaud(unsigned long baud) { + void setBaudImpl(uint32_t baud) { XBEE_COMMAND_START_DECORATOR(5, ) - switch(baud) - { + switch (baud) { case 2400: sendAT(GF("BD1")); break; case 4800: sendAT(GF("BD2")); break; case 9600: sendAT(GF("BD3")); break; @@ -349,9 +347,9 @@ public: case 460800: sendAT(GF("BD9")); break; case 921600: sendAT(GF("BDA")); break; default: { - DBG(GF("Specified baud rate is unsupported! Setting to 9600 baud.")); - sendAT(GF("BD3")); // Set to default of 9600 - break; + DBG(GF("Specified baud rate is unsupported! Setting to 9600 baud.")); + sendAT(GF("BD3")); // Set to default of 9600 + break; } } waitResponse(); @@ -359,28 +357,28 @@ public: XBEE_COMMAND_END_DECORATOR } - bool testAT(unsigned long timeout_ms = 10000L) { - unsigned long start = millis(); - bool success = false; + bool testATImpl(uint32_t timeout_ms = 10000L) { + uint32_t start = millis(); + bool success = false; while (!success && millis() - start < timeout_ms) { if (!inCommandMode) { success = commandMode(); if (success) exitCommand(); - } - else { - sendAT(); - if (waitResponse(200) == 1) { - success = true; - } + } else { + sendAT(); + if (waitResponse(200) == 1) { + success = true; + } else { // if we didn't respond to the AT, assume we're not in command mode - else inCommandMode = false; + inCommandMode = false; + } } delay(250); } return success; } - void maintain() { + void maintainImpl() { // this only happens OUTSIDE command mode, so if we're getting characters // they should be data received from the TCP connection // TINY_GSM_YIELD(); @@ -392,7 +390,7 @@ public: // } } - bool factoryDefault() { + bool factoryDefaultImpl() { XBEE_COMMAND_START_DECORATOR(5, false) sendAT(GF("RE")); bool ret_val = waitResponse() == 1; @@ -404,44 +402,51 @@ public: return ret_val; } - String getModemInfo() { + String getModemInfoImpl() { return sendATGetString(GF("HS")); } - bool hasSSL() { - if (beeType == XBEE_S6B_WIFI) return false; - else return true; + bool thisHasSSL() { + if (beeType == XBEE_S6B_WIFI) + return false; + else + return true; } - bool hasWifi() { - if (beeType == XBEE_S6B_WIFI) return true; - else return false; + bool thisHasWifi() { + if (beeType == XBEE_S6B_WIFI) + return true; + else + return false; } - bool hasGPRS() { - if (beeType == XBEE_S6B_WIFI) return false; - else return true; + bool thisHasGPRS() { + if (beeType == XBEE_S6B_WIFI) + return false; + else + return true; } + public: XBeeType getBeeType() { return beeType; } String getBeeName() { - switch (beeType){ + switch (beeType) { case XBEE_S6B_WIFI: return "Digi XBee Wi-Fi"; case XBEE_LTE1_VZN: return "Digi XBee Cellular LTE Cat 1"; case XBEE_3G: return "Digi XBee Cellular 3G"; case XBEE3_LTE1_ATT: return "Digi XBee3 Cellular LTE CAT 1"; case XBEE3_LTEM_ATT: return "Digi XBee3 Cellular LTE-M"; - default: return "Digi XBee"; + default: return "Digi XBee"; } } /* * Power functions */ - + protected: // The XBee's have a bad habit of getting into an unresponsive funk // This uses the board's hardware reset pin to force it to reset void pinReset() { @@ -453,28 +458,32 @@ public: } } - bool restart() { - - if (!commandMode()) return false; // Return immediately + bool restartImpl() { + if (!commandMode()) { return false; } // Return immediately if (beeType == XBEE_UNKNOWN) getSeries(); // how we restart depends on this if (beeType != XBEE_S6B_WIFI) { - sendAT(GF("AM1")); // Digi suggests putting cellular modules into airplane mode before restarting - // This allows the sockets and connections to close cleanly + sendAT(GF("AM1")); // Digi suggests putting cellular modules into + // airplane mode before restarting This allows the + // sockets and connections to close cleanly if (waitResponse() != 1) return exitAndFail(); if (!writeChanges()) return exitAndFail(); } sendAT(GF("FR")); - if (waitResponse() != 1) return exitAndFail(); - else inCommandMode = false; // Reset effectively exits command mode - - if (beeType == XBEE_S6B_WIFI) delay(2000); // Wifi module actually resets about 2 seconds later - else delay(100); // cellular modules wait 100ms before reset happens - - // Wait until reboot complete and responds to command mode call again - for (unsigned long start = millis(); millis() - start < 60000L; ) { + if (waitResponse() != 1) + return exitAndFail(); + else + inCommandMode = false; // Reset effectively exits command mode + + if (beeType == XBEE_S6B_WIFI) + delay(2000); // Wifi module actually resets about 2 seconds later + else + delay(100); // cellular modules wait 100ms before reset happens + + // Wait until reboot completes and XBee responds to command mode call again + for (uint32_t start = millis(); millis() - start < 60000L;) { if (commandMode(1)) break; delay(250); // wait a litle before trying again } @@ -495,102 +504,117 @@ public: if (beeType == XBEE_UNKNOWN) getSeries(); // Command depends on series - sendAT(GF("SM"),1); // Pin sleep + sendAT(GF("SM"), 1); // Pin sleep waitResponse(); if (beeType == XBEE_S6B_WIFI && !maintainAssociation) { - sendAT(GF("SO"),200); // For lowest power, dissassociated deep sleep - waitResponse(); - } - - else if (!maintainAssociation){ - sendAT(GF("SO"),1); // For supported cellular modules, maintain association - // Not supported by all modules, will return "ERROR" - waitResponse(); + sendAT(GF("SO"), 200); // For lowest power, dissassociated deep sleep + waitResponse(); + } else if (!maintainAssociation) { + sendAT(GF("SO"), + 1); // For supported cellular modules, maintain association + // Not supported by all modules, will return "ERROR" + waitResponse(); } writeChanges(); XBEE_COMMAND_END_DECORATOR } - bool poweroff() { // NOTE: Not supported for WiFi or older cellular firmware + bool + powerOffImpl() { // 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"); - } + if (ret_val) { ret_val &= (sendATGetString(GF("AI")) == "2D"); } XBEE_COMMAND_END_DECORATOR return ret_val; } - bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED; + // Enable airplane mode + bool radioOffImpl() { + XBEE_COMMAND_START_DECORATOR(5, false) + sendAT(GF("AM1")); + int8_t res = (1 == waitResponse(5000)); + writeChanges(); + XBEE_COMMAND_END_DECORATOR + return res; + } - bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; /* * SIM card functions */ - - bool simUnlock(const char *pin) { // Not supported + protected: + bool simUnlockImpl(const char* pin) { // Not supported if (pin && strlen(pin) > 0) { DBG("XBee's do not support SIMs that require an unlock pin!"); } return false; } - String getSimCCID() { + String getSimCCIDImpl() { return sendATGetString(GF("S#")); } - String getIMEI() { + String getIMEIImpl() { return sendATGetString(GF("IM")); } - SimStatus getSimStatus() { + SimStatus getSimStatusImpl(uint32_t) { return SIM_READY; // unsupported } + /* + * Generic network functions + */ + public: RegStatus getRegistrationStatus() { - XBEE_COMMAND_START_DECORATOR(5, REG_UNKNOWN) if (!inCommandMode) return REG_UNKNOWN; // Return immediately - if (beeType == XBEE_UNKNOWN) getSeries(); // Need to know the bee type to interpret response + if (beeType == XBEE_UNKNOWN) + getSeries(); // Need to know the bee type to interpret response sendAT(GF("AI")); - int16_t intRes = readResponseInt(10000L); - RegStatus stat = REG_UNKNOWN; + int16_t intRes = readResponseInt(10000L); + RegStatus stat = REG_UNKNOWN; - switch (beeType){ + switch (beeType) { case XBEE_S6B_WIFI: { switch (intRes) { - case 0x00: // 0x00 Successfully joined an access point, established IP addresses and IP listening sockets + case 0x00: // 0x00 Successfully joined an access point, established + // IP addresses and IP listening sockets stat = REG_OK; break; case 0x01: // 0x01 Wi-Fi transceiver initialization in progress. - case 0x02: // 0x02 Wi-Fi transceiver initialized, but not yet scanning for access point. + case 0x02: // 0x02 Wi-Fi transceiver initialized, but not yet + // scanning for access point. case 0x40: // 0x40 Waiting for WPA or WPA2 Authentication. - case 0x41: // 0x41 Device joined a network and is waiting for IP configuration to complete - case 0x42: // 0x42 Device is joined, IP is configured, and listening sockets are being set up. - case 0xFF: // 0xFF Device is currently scanning for the configured SSID. + case 0x41: // 0x41 Device joined a network and is waiting for IP + // configuration to complete + case 0x42: // 0x42 Device is joined, IP is configured, and listening + // sockets are being set up. + case 0xFF: // 0xFF Device is currently scanning for the configured + // SSID. stat = REG_SEARCHING; break; - case 0x13: // 0x13 Disconnecting from access point. - restart(); // Restart the device; the S6B tends to get stuck "disconnecting" + case 0x13: // 0x13 Disconnecting from access point. + restart(); // Restart the device; the S6B tends to get stuck + // "disconnecting" stat = REG_UNREGISTERED; break; case 0x23: // 0x23 SSID not configured. stat = REG_UNREGISTERED; break; - case 0x24: // 0x24 Encryption key invalid (either NULL or invalid length for WEP). + case 0x24: // 0x24 Encryption key invalid (either NULL or invalid + // length for WEP). case 0x27: // 0x27 SSID was found, but join failed. stat = REG_DENIED; break; - default: - stat = REG_UNKNOWN; - break; + default: stat = REG_UNKNOWN; break; } break; } @@ -603,8 +627,9 @@ public: case 0x23: // 0x23 Connecting to the Internet. case 0xFF: // 0xFF Initializing. stat = REG_SEARCHING; - break; - case 0x24: // 0x24 The cellular component is missing, corrupt, or otherwise in error. + break; + case 0x24: // 0x24 The cellular component is missing, corrupt, or + // otherwise in error. case 0x2B: // 0x2B USB Direct active. case 0x2C: // 0x2C Cellular component is in PSM (power save mode). stat = REG_UNKNOWN; @@ -612,21 +637,19 @@ public: case 0x25: // 0x25 Cellular network registration denied. stat = REG_DENIED; break; - case 0x2A: // 0x2A Airplane mode. + case 0x2A: // 0x2A Airplane mode. sendAT(GF("AM0")); // Turn off airplane mode waitResponse(); writeChanges(); stat = REG_UNKNOWN; break; - case 0x2F: // 0x2F Bypass mode active. + case 0x2F: // 0x2F Bypass mode active. sendAT(GF("AP0")); // Set back to transparent mode waitResponse(); writeChanges(); stat = REG_UNKNOWN; break; - default: - stat = REG_UNKNOWN; - break; + default: stat = REG_UNKNOWN; break; } break; } @@ -636,40 +659,36 @@ public: return stat; } - String getOperator() { - return sendATGetString(GF("MN")); - } - - /* - * Generic network functions - */ - - int16_t getSignalQuality() { - + protected: + int16_t getSignalQualityImpl() { XBEE_COMMAND_START_DECORATOR(5, 0); - if (beeType == XBEE_UNKNOWN) getSeries(); // Need to know what type of bee so we know how to ask + if (beeType == XBEE_UNKNOWN) + getSeries(); // Need to know what type of bee so we know how to ask - if (beeType == XBEE_S6B_WIFI) sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity - else sendAT(GF("DB")); // ask for the cell strength in dBm + if (beeType == XBEE_S6B_WIFI) + sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity + else + sendAT(GF("DB")); // ask for the cell strength in dBm int16_t intRes = readResponseInt(); XBEE_COMMAND_END_DECORATOR - if (beeType == XBEE3_LTEM_ATT && intRes == 105) intRes = 0; // tends to reply with "69" when signal is unknown + if (beeType == XBEE3_LTEM_ATT && intRes == 105) + intRes = 0; // tends to reply with "69" when signal is unknown if (beeType == XBEE_S6B_WIFI) { if (intRes == 0xFF) { - return 0; // 0xFF returned for unknown + return 0; // 0xFF returned for unknown } else { return -93 + intRes; // the maximum sensitivity is -93dBm } } else { - return -1*intRes; // need to convert to negative number + return -1 * intRes; // need to convert to negative number } } - bool isNetworkConnected() { + bool isNetworkConnectedImpl() { RegStatus s = getRegistrationStatus(); if (s == REG_OK) { IPAddress ip = localIP(); @@ -683,10 +702,10 @@ public: } } - bool waitForNetwork(unsigned long timeout_ms = 60000L) { + bool waitForNetworkImpl(uint32_t timeout_ms = 60000L) { bool retVal = false; XBEE_COMMAND_START_DECORATOR(5, false) - for (unsigned long start = millis(); millis() - start < timeout_ms; ) { + for (uint32_t start = millis(); millis() - start < timeout_ms;) { if (isNetworkConnected()) { retVal = true; break; @@ -700,17 +719,15 @@ public: /* * WiFi functions */ - - bool networkConnect(const char* ssid, const char* pwd) { - + protected: + bool networkConnectImpl(const char* ssid, const char* pwd) { bool retVal = true; XBEE_COMMAND_START_DECORATOR(5, false) - //nh For no pwd don't set setscurity or pwd - if (ssid == NULL) retVal = false;; + // nh For no pwd don't set set security or pwd + if (ssid == NULL) retVal = false; - if (pwd && strlen(pwd) > 0) - { + if (pwd && strlen(pwd) > 0) { sendAT(GF("EE"), 2); // Set security to WPA2 if (waitResponse() != 1) retVal = false; sendAT(GF("PK"), pwd); @@ -729,7 +746,7 @@ public: return retVal; } - bool networkDisconnect() { + bool networkDisconnectImpl() { XBEE_COMMAND_START_DECORATOR(5, false) sendAT(GF("NR0")); // Do a network reset in order to disconnect // WARNING: On wifi modules, using a network reset will not @@ -740,31 +757,12 @@ public: return res; } - /* - * IP Address functions - */ - - String getLocalIP() { - XBEE_COMMAND_START_DECORATOR(5, "") - sendAT(GF("MY")); - String IPaddr; IPaddr.reserve(16); - // wait for the response - this response can be very slow - IPaddr = readResponseString(30000); - XBEE_COMMAND_END_DECORATOR - IPaddr.trim(); - return IPaddr; - } - - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - /* * GPRS functions */ - - bool gprsConnect(const char* apn, const char* user = NULL, - const char* pwd = NULL) { + protected: + bool gprsConnectImpl(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!"); } @@ -781,9 +779,10 @@ public: return success; } - bool gprsDisconnect() { + bool gprsDisconnectImpl() { XBEE_COMMAND_START_DECORATOR(5, false) - sendAT(GF("AM1")); // Cheating and disconnecting by turning on airplane mode + sendAT( + GF("AM1")); // Cheating and disconnecting by turning on airplane mode int8_t res = (1 == waitResponse(5000)); writeChanges(); // sendAT(GF("AM0")); // Airplane mode off @@ -793,32 +792,64 @@ public: return res; } - bool isGprsConnected() { + bool isGprsConnectedImpl() { return isNetworkConnected(); } + String getOperatorImpl() { + return sendATGetString(GF("MN")); + } + /* - * Messaging functions + * IP Address functions + */ + protected: + String getLocalIPImpl() { + XBEE_COMMAND_START_DECORATOR(5, "") + sendAT(GF("MY")); + String IPaddr; + IPaddr.reserve(16); + // wait for the response - this response can be very slow + IPaddr = readResponseString(30000); + XBEE_COMMAND_END_DECORATOR + IPaddr.trim(); + return IPaddr; + } + + /* + * Phone Call functions */ + protected: + bool callAnswerImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool callNumberImpl(const String& number) TINY_GSM_ATTR_NOT_AVAILABLE; + bool callHangupImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + bool + dtmfSendImpl(char cmd, int duration_ms = 100) TINY_GSM_ATTR_NOT_AVAILABLE; - String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; + /* + * Messaging functions + */ + protected: + String sendUSSDImpl(const String& code) TINY_GSM_ATTR_NOT_AVAILABLE; - bool sendSMS(const String& number, const String& text) { - if (!commandMode()) return false; // Return immediately + bool sendSMSImpl(const String& number, const String& text) { + if (!commandMode()) { return false; } // Return immediately sendAT(GF("IP"), 2); // Put in text messaging mode - if (waitResponse() !=1) return exitAndFail(); + if (waitResponse() != 1) return exitAndFail(); sendAT(GF("PH"), number); // Set the phone number - if (waitResponse() !=1) return exitAndFail(); - sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriage return) - if (waitResponse() !=1) return exitAndFail(); + if (waitResponse() != 1) return exitAndFail(); + sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriage + // return) + if (waitResponse() != 1) return exitAndFail(); if (!writeChanges()) return exitAndFail(); // Get out of command mode to actually send the text exitCommand(); streamWrite(text); - stream.write((char)0x0D); // close off with the carriage return + stream.write( + static_cast(0x0D)); // close off with the carriage return return true; } @@ -826,15 +857,28 @@ public: /* * Location functions */ + protected: + String getGsmLocationImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * GPS location functions + */ + public: + // No functions of this type supported /* - * Battery & temperature functions + * Time functions */ + protected: + String getGSMDateTimeImpl(TinyGSMDateTimeFormat format) + TINY_GSM_ATTR_NOT_IMPLEMENTED; + /* + * Battery & temperature functions + */ + protected: // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() { + uint16_t getBattVoltageImpl() { int16_t intRes = 0; XBEE_COMMAND_START_DECORATOR(5, false) if (beeType == XBEE_UNKNOWN) getSeries(); @@ -846,35 +890,36 @@ public: return intRes; } - int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE; - uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE; + int8_t getBattPercentImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) { chargeState = 0; - percent = 0; - milliVolts = getBattVoltage(); + percent = 0; + milliVolts = getBattVoltage(); return true; } - float getTemperature() { - XBEE_COMMAND_START_DECORATOR(5, (float)-9999) + float getTemperatureImpl() { + XBEE_COMMAND_START_DECORATOR(5, static_cast(-9999)) String res = sendATGetString(GF("TP")); - if (res == "") { - return (float)-9999; - } - char buf[5] = {0,}; + if (res == "") { return static_cast(-9999); } + 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. + 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; + return static_cast(intRes); } /* * Client related functions */ - protected: - int16_t getConnectionIndicator() { XBEE_COMMAND_START_DECORATOR(5, false) sendAT(GF("CI")); @@ -895,23 +940,27 @@ public: if (strIP != "" && strIP != GF("ERROR")) { return TinyGsmIpFromString(strIP); - } else + } 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; - XBEE_COMMAND_START_DECORATOR(5, IPAddress(0,0,0,0)) + uint32_t startMillis = millis(); + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + bool gotIP = false; + XBEE_COMMAND_START_DECORATOR(5, IPAddress(0, 0, 0, 0)) // XBee's require a numeric IP address for connection, but do provide the - // functionality to look up the IP address from a fully qualified domain name - while ((millis() - startMillis) < timeout_ms) // the lookup can take a while - { + // functionality to look up the IP address from a fully qualified domain + // name + // NOTE: the lookup can take a while + while ((millis() - startMillis) < timeout_ms) { sendAT(GF("LA"), host); - while (stream.available() < 4 && (millis() - startMillis < timeout_ms)) {TINY_GSM_YIELD()}; + while (stream.available() < 4 && (millis() - startMillis < timeout_ms)) { + TINY_GSM_YIELD() + } strIP = stream.readStringUntil('\r'); // read result strIP.trim(); if (strIP != "" && strIP != GF("ERROR")) { @@ -925,29 +974,32 @@ public: if (gotIP) { return TinyGsmIpFromString(strIP); + } else { + return IPAddress(0, 0, 0, 0); } - else return IPAddress(0,0,0,0); } bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, - bool ssl = false, int timeout_s = 75) - { + bool ssl = false, int timeout_s = 75) { bool retVal = 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 is a new host name, replace the saved host and wipe out the saved + // host IP if (this->savedHost != String(host)) { this->savedHost = String(host); - savedHostIP = IPAddress(0,0,0,0); + savedHostIP = IPAddress(0, 0, 0, 0); } // 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 = lookupHostIP(host, timeout_s); // This will return 0.0.0.0 if lookup fails + if (savedHostIP == IPAddress(0, 0, 0, 0)) { + // This will return 0.0.0.0 if lookup fails + savedHostIP = lookupHostIP(host, timeout_s); } // 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 + if (savedHostIP != IPAddress(0, 0, 0, 0)) { + // Only re-set connection information if we have an IP address retVal = modemConnect(savedHostIP, port, mux, ssl); } @@ -956,8 +1008,8 @@ public: return retVal; } - bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, - bool ssl = false) { + bool + modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false) { bool success = true; if (mux != 0) { @@ -971,8 +1023,9 @@ public: 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); + savedIP = ip; // Set the newly requested IP address + String host; + host.reserve(16); host += ip[0]; host += "."; host += ip[1]; @@ -1003,9 +1056,7 @@ public: success &= (ci == 0x00 || ci == 0xFF || ci == 0x28); } - if (success) { - sockets[mux]->sock_connected = true; - } + if (success) { sockets[mux]->sock_connected = true; } XBEE_COMMAND_END_DECORATOR @@ -1026,7 +1077,7 @@ public: // 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 + sendAT(GF("TM0")); // Set socket timeout to 0 waitResponse(maxWaitMs); // This response can be slow writeChanges(); } @@ -1035,7 +1086,7 @@ public: // 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 + waitResponse(maxWaitMs); // This response can be slow writeChanges(); XBEE_COMMAND_END_DECORATOR @@ -1046,17 +1097,16 @@ public: if (mux != 0) { DBG("XBee only supports 1 IP channel in transparent mode!"); } - stream.write((uint8_t*)buff, len); + stream.write(reinterpret_cast(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) { + } else if (len > 5) { + // After sending several characters, also re-check + // NOTE: I'm intentionally not checking after every single character! modemGetConnected(); } } @@ -1064,20 +1114,27 @@ public: return len; } + size_t modemRead(size_t, uint8_t) { + return 0; + } + size_t modemGetAvailable(uint8_t) { + return 0; + } + // NOTE: The CI command returns the status of the TCP connection as open only // after data has been sent on the socket. If it returns 0xFF the socket may - // really be open, but no data has yet been sent. We return this unknown value - // as true so there's a possibility it's wrong. + // really be open, but no data has yet been sent. We return this unknown + // value as true so there's a possibility it's wrong. bool modemGetConnected() { // If the IP address is 0, it's not valid so we can't be connected - if (savedIP == IPAddress(0,0,0,0)) return false; - - XBEE_COMMAND_START_DECORATOR(5, false) + if (savedIP == IPAddress(0, 0, 0, 0)) { return false; } - if (beeType == XBEE_UNKNOWN) getSeries(); // Need to know the bee type to interpret response + XBEE_COMMAND_START_DECORATOR(5, false) - switch (beeType){ + 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 case XBEE_S6B_WIFI: { RegStatus s = getRegistrationStatus(); @@ -1095,8 +1152,7 @@ public: IPAddress od = getOperatingIP(); XBEE_COMMAND_END_DECORATOR - switch(ci) { - + switch (ci) { // 0x00 = The socket is definitely open case 0x00: { savedOperatingIP = od; @@ -1112,28 +1168,26 @@ public: // 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; + // 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) { + } else if (od != IPAddress(0, 0, 0, 0) && od != savedIP) { + // else if the operating destination exists, but is wrong + // we need to close and re-open 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) { + } else if (od != IPAddress(0, 0, 0, 0) && od == savedIP) { + // else if the operating destination exists and matches, we're + // good to go 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 { + } else { + // If we never had an operating destination, then sock may be open + // but data never sent - this is the dreaded "we don't know" savedOperatingIP = od; return true; } @@ -1167,12 +1221,12 @@ public: // 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 : { + 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 + waitResponse(5000L); // This response can be slow } // 0x02 = Invalid parameters (bad IP/host) @@ -1190,7 +1244,7 @@ public: // fall through default: { sockets[0]->sock_connected = false; - savedOperatingIP = od; + savedOperatingIP = od; return false; } } @@ -1198,12 +1252,10 @@ public: } } -public: - /* - Utilities + * Utilities */ - + public: void streamClear(void) { while (stream.available()) { stream.read(); @@ -1211,16 +1263,15 @@ public: } } -TINY_GSM_MODEM_STREAM_UTILITIES() - - // TODO: Optimize this! + // TODO(vshymanskyy): Optimize this! // NOTE: This function is used while INSIDE command mode, so we're only // waiting for requested responses. The XBee has no unsoliliced responses // (URC's) when in command mode. - uint8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR), - GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + uint8_t + waitResponse(uint32_t timeout_ms, String& data, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), + GsmConstStr r3 = GFP(GSM_CME_ERROR), GsmConstStr r4 = NULL, + GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -1228,15 +1279,15 @@ TINY_GSM_MODEM_STREAM_UTILITIES() String r5s(r5); r5s.trim(); DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ data.reserve(16); // Should never be getting much here for the XBee - int8_t index = 0; - unsigned long startMillis = millis(); + int8_t index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { TINY_GSM_YIELD(); int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -1255,7 +1306,7 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } } } while (millis() - startMillis < timeout_ms); -finish: + finish: if (!index) { data.trim(); data.replace(GSM_NL GSM_NL, GSM_NL); @@ -1269,44 +1320,43 @@ finish: data.trim(); data.replace(GSM_NL GSM_NL, GSM_NL); data.replace(GSM_NL, "\r\n "); - if (data.length()) { - } } - //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=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) - { + 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) { 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=NULL, 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); } bool commandMode(uint8_t retries = 5) { - // If we're already in command mode, move on - if (inCommandMode && (millis() - lastCommandModeMillis) < 10000L) return true; + if (inCommandMode && (millis() - lastCommandModeMillis) < 10000L) + return true; - uint8_t triesMade = 0; + uint8_t triesMade = 0; uint8_t triesUntilReset = 4; // only reset after 4 failures - bool success = false; + bool success = false; streamClear(); // Empty everything in the buffer before starting - while (!success and triesMade < retries) { + while (!success && triesMade < retries) { // Cannot send anything for 1 "guard time" before entering command mode // Default guard time is 1s, but the init fxn decreases it to 100 ms delay(guardTime + 10); streamWrite(GF("+++")); // enter command mode - int res = waitResponse(guardTime*2); + int res = waitResponse(guardTime * 2); success = (1 == res); if (0 == res) { triesUntilReset--; @@ -1314,14 +1364,14 @@ finish: triesUntilReset = 4; pinReset(); // if it's unresponsive, reset delay(250); // a short delay to allow it to come back up - // TODO-optimize this + // TODO(SRGDamia1) optimize this } } - triesMade ++; + triesMade++; } if (success) { - inCommandMode = true; + inCommandMode = true; lastCommandModeMillis = millis(); } return success; @@ -1329,9 +1379,9 @@ finish: bool writeChanges(void) { sendAT(GF("WR")); // Write changes to flash - if (1 != waitResponse()) return false; + if (1 != waitResponse()) { return false; } sendAT(GF("AC")); // Apply changes - if (1 != waitResponse()) return false; + if (1 != waitResponse()) { return false; } return true; } @@ -1351,23 +1401,27 @@ finish: void getSeries(void) { sendAT(GF("HS")); // Get the "Hardware Series"; int16_t intRes = readResponseInt(); - beeType = (XBeeType)intRes; + beeType = (XBeeType)intRes; DBG(GF("### Modem: "), getModemName()); } String readResponseString(uint32_t timeout_ms = 1000) { TINY_GSM_YIELD(); - unsigned long startMillis = millis(); - while (!stream.available() && millis() - startMillis < timeout_ms) {}; - String res = stream.readStringUntil('\r'); // lines end with carriage returns + uint32_t startMillis = millis(); + while (!stream.available() && millis() - startMillis < timeout_ms) {} + String res = + stream.readStringUntil('\r'); // lines end with carriage returns res.trim(); return res; } int16_t readResponseInt(uint32_t timeout_ms = 1000) { - String res = readResponseString(timeout_ms); // it just works better reading a string first + String res = readResponseString( + timeout_ms); // it just works better reading a string first if (res == "") res = "FF"; - char buf[5] = {0,}; + char buf[5] = { + 0, + }; res.toCharArray(buf, 5); int16_t intRes = strtol(buf, 0, 16); return intRes; @@ -1382,24 +1436,25 @@ finish: } bool gotIPforSavedHost() { - if (savedHost != "" && savedHostIP != IPAddress(0,0,0,0)) return true; - else return false; + if (savedHost != "" && savedHostIP != IPAddress(0, 0, 0, 0)) + return true; + else + return false; } -public: - Stream& stream; - -protected: - int16_t guardTime; - int8_t resetPin; - XBeeType beeType; - IPAddress savedIP; - String savedHost; - IPAddress savedHostIP; - IPAddress savedOperatingIP; - bool inCommandMode; - uint32_t lastCommandModeMillis; - GsmClient* sockets[TINY_GSM_MUX_COUNT]; + protected: + Stream& stream; + GsmClientXBee* sockets[TINY_GSM_MUX_COUNT]; + const char* gsmNL = GSM_NL; + int16_t guardTime; + XBeeType beeType; + int8_t resetPin; + IPAddress savedIP; + String savedHost; + IPAddress savedHostIP; + IPAddress savedOperatingIP; + bool inCommandMode; + uint32_t lastCommandModeMillis; }; -#endif +#endif // SRC_TINYGSMCLIENTXBEE_H_ diff --git a/src/TinyGsmCommon.h b/src/TinyGsmCommon.h index f3a2ea3..0f97f33 100644 --- a/src/TinyGsmCommon.h +++ b/src/TinyGsmCommon.h @@ -6,105 +6,110 @@ * @date Nov 2016 */ -#ifndef TinyGsmCommon_h -#define TinyGsmCommon_h +#ifndef SRC_TINYGSMCOMMON_H_ +#define SRC_TINYGSMCOMMON_H_ // The current library version number #define TINYGSM_VERSION "0.9.19" #if defined(SPARK) || defined(PARTICLE) - #include "Particle.h" +#include "Particle.h" #elif defined(ARDUINO) - #if ARDUINO >= 100 - #include "Arduino.h" - #else - #include "WProgram.h" - #endif +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif #endif #if defined(ARDUINO_DASH) - #include +#include #else - #include +#include #endif -#include +#include "TinyGsmFifo.h" #ifndef TINY_GSM_YIELD_MS - #define TINY_GSM_YIELD_MS 0 +#define TINY_GSM_YIELD_MS 0 #endif #ifndef TINY_GSM_YIELD - #define TINY_GSM_YIELD() { delay(TINY_GSM_YIELD_MS); } +#define TINY_GSM_YIELD() \ + { delay(TINY_GSM_YIELD_MS); } +#endif + +#if !defined(TINY_GSM_RX_BUFFER) +#define TINY_GSM_RX_BUFFER 64 #endif -#define TINY_GSM_ATTR_NOT_AVAILABLE __attribute__((error("Not available on this modem type"))) +#define TINY_GSM_ATTR_NOT_AVAILABLE \ + __attribute__((error("Not available on this modem type"))) #define TINY_GSM_ATTR_NOT_IMPLEMENTED __attribute__((error("Not implemented"))) #if defined(__AVR__) - #define TINY_GSM_PROGMEM PROGMEM - typedef const __FlashStringHelper* GsmConstStr; - #define GFP(x) (reinterpret_cast(x)) - #define GF(x) F(x) +#define TINY_GSM_PROGMEM PROGMEM +typedef const __FlashStringHelper* GsmConstStr; +#define GFP(x) (reinterpret_cast(x)) +#define GF(x) F(x) #else - #define TINY_GSM_PROGMEM - typedef const char* GsmConstStr; - #define GFP(x) x - #define GF(x) x +#define TINY_GSM_PROGMEM +typedef const char* GsmConstStr; +#define GFP(x) x +#define GF(x) x #endif #ifdef TINY_GSM_DEBUG namespace { - template - static void DBG_PLAIN(T last) { - TINY_GSM_DEBUG.println(last); - } +template +static void DBG_PLAIN(T last) { + TINY_GSM_DEBUG.println(last); +} - template - static void DBG_PLAIN(T head, Args... tail) { - TINY_GSM_DEBUG.print(head); - TINY_GSM_DEBUG.print(' '); - DBG_PLAIN(tail...); - } +template +static void DBG_PLAIN(T head, Args... tail) { + TINY_GSM_DEBUG.print(head); + TINY_GSM_DEBUG.print(' '); + DBG_PLAIN(tail...); +} - template - static void DBG(Args... args) { - TINY_GSM_DEBUG.print(GF("[")); - TINY_GSM_DEBUG.print(millis()); - TINY_GSM_DEBUG.print(GF("] ")); - DBG_PLAIN(args...); - } +template +static void DBG(Args... args) { + TINY_GSM_DEBUG.print(GF("[")); + TINY_GSM_DEBUG.print(millis()); + TINY_GSM_DEBUG.print(GF("] ")); + DBG_PLAIN(args...); } +} // namespace #else - #define DBG_PLAIN(...) - #define DBG(...) +#define DBG_PLAIN(...) +#define DBG(...) #endif -template -const T& TinyGsmMin(const T& a, const T& b) -{ - return (b < a) ? b : a; +template +const T& TinyGsmMin(const T& a, const T& b) { + return (b < a) ? b : a; } -template -const T& TinyGsmMax(const T& a, const T& b) -{ - return (b < a) ? a : b; +template +const T& TinyGsmMax(const T& a, const T& b) { + return (b < a) ? a : b; } -template -uint32_t TinyGsmAutoBaud(T& SerialAT, uint32_t minimum = 9600, uint32_t maximum = 115200) -{ - static uint32_t rates[] = { 115200, 57600, 38400, 19200, 9600, 74400, 74880, 230400, 460800, 2400, 4800, 14400, 28800 }; +template +uint32_t TinyGsmAutoBaud(T& SerialAT, uint32_t minimum = 9600, + uint32_t maximum = 115200) { + static uint32_t rates[] = {115200, 57600, 38400, 19200, 9600, 74400, 74880, + 230400, 460800, 2400, 4800, 14400, 28800}; - for (unsigned i = 0; i < sizeof(rates)/sizeof(rates[0]); i++) { + for (unsigned i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) { uint32_t rate = rates[i]; if (rate < minimum || rate > maximum) continue; DBG("Trying baud rate", rate, "..."); SerialAT.begin(rate); delay(10); - for (int i=0; i<10; i++) { + for (int j = 0; j < 10; j++) { SerialAT.print("AT\r\n"); String input = SerialAT.readString(); if (input.indexOf("OK") >= 0) { @@ -116,560 +121,908 @@ uint32_t TinyGsmAutoBaud(T& SerialAT, uint32_t minimum = 9600, uint32_t maximum return 0; } -static inline -IPAddress TinyGsmIpFromString(const String& strIP) { - int Parts[4] = {0, }; - int Part = 0; - for (uint8_t i=0; i 3) { - return IPAddress(0,0,0,0); +enum modemInternalBuffferType { + NO_MODEM_BUFFER = + 0, // For modules that do not store incoming data in any sort of buffer + READ_NO_CHECK = 1, // Data is stored in a buffer, but we can only read from + // the buffer, not check how much data is stored in it + READ_AND_CHECK_SIZE = 2, // Data is stored in a buffer and we can both read + // and check the size of the buffer +}; + +enum SimStatus { + SIM_ERROR = 0, + SIM_READY = 1, + SIM_LOCKED = 2, + SIM_ANTITHEFT_LOCKED = 3, +}; + +enum TinyGSMDateTimeFormat { DATE_FULL = 0, DATE_TIME = 1, DATE_DATE = 2 }; + +template +class TinyGsmModem { + public: + /* + * Basic functions + */ + bool begin(const char* pin = NULL) { + return thisModem().initImpl(pin); + } + bool init(const char* pin = NULL) { + return thisModem().initImpl(pin); + } + template + void sendAT(Args... cmd) { + thisModem().streamWrite("AT", cmd..., thisModem().gsmNL); + thisModem().stream.flush(); + TINY_GSM_YIELD(); /* DBG("### AT:", cmd...); */ + } + void setBaud(uint32_t baud) { + thisModem().setBaudImpl(baud); + } + // Test response to AT commands + bool testAT(uint32_t timeout_ms = 10000L) { + return thisModem().testATImpl(timeout_ms); + } + + // Asks for modem information via the V.25TER standard ATI command + // NOTE: The actual value and style of the response is quite varied + String getModemInfo() { + return thisModem().getModemInfoImpl(); + } + // Gets the modem name (as it calls itself) + String getModemName() { + return thisModem().getModemNameImpl(); + } + void maintain() { + return thisModem().maintainImpl(); + } + bool factoryDefault() { + return thisModem().factoryDefaultImpl(); + } + bool hasSSL() { + return thisModem().thisHasSSL(); + } + bool hasWifi() { + return thisModem().thisHasWifi(); + } + bool hasGPRS() { + return thisModem().thisHasGPRS(); + } + + /* + * Power functions + */ + bool restart() { + return thisModem().restartImpl(); + } + bool poweroff() { + return thisModem().powerOffImpl(); + } + bool radioOff() { + return thisModem().radioOffImpl(); + } + bool sleepEnable(bool enable = true) { + return thisModem().sleepEnableImpl(enable); + } + + /* + * SIM card functions + */ + // Unlocks the SIM + bool simUnlock(const char* pin) { + return thisModem().simUnlockImpl(pin); + } + // Gets the CCID of a sim card via AT+CCID + String getSimCCID() { + return thisModem().getSimCCIDImpl(); + } + // Asks for TA Serial Number Identification (IMEI) + String getIMEI() { + return thisModem().getIMEIImpl(); + } + SimStatus getSimStatus(uint32_t timeout_ms = 10000L) { + return thisModem().getSimStatusImpl(timeout_ms); + } + + /* + * Generic network functions + */ + // RegStatus getRegistrationStatus() {} + bool isNetworkConnected() { + return thisModem().isNetworkConnectedImpl(); + } + // Waits for network attachment + bool waitForNetwork(uint32_t timeout_ms = 60000L) { + return thisModem().waitForNetworkImpl(timeout_ms); + } + // Gets signal quality report + int16_t getSignalQuality() { + return thisModem().getSignalQualityImpl(); + } + + /* + * GPRS functions + */ + bool gprsConnect(const char* apn, const char* user = NULL, + const char* pwd = NULL) { + return thisModem().gprsConnectImpl(apn, user, pwd); + } + bool gprsDisconnect() { + return thisModem().gprsDisconnectImpl(); + } + // Checks if current attached to GPRS/EPS service + bool isGprsConnected() { + return thisModem().isGprsConnectedImpl(); + } + // Gets the current network operator + String getOperator() { + return thisModem().getOperatorImpl(); + } + + /* + * WiFi functions + */ + bool networkConnect(const char* ssid, const char* pwd) { + return thisModem().networkConnectImpl(ssid, pwd); + } + bool networkDisconnect() { + return thisModem().networkDisconnectImpl(); + } + + /* + * GPRS functions + */ + String getLocalIP() { + return thisModem().getLocalIPImpl(); + } + IPAddress localIP() { + return thisModem().TinyGsmIpFromString(thisModem().getLocalIP()); + } + + /* + * Phone Call functions + */ + bool callAnswer() { + return thisModem().callAnswerImpl(); + } + bool callNumber(const String& number) { + return thisModem().callNumberImpl(number); + } + bool callHangup() { + return thisModem().callHangupImpl(); + } + bool dtmfSend(char cmd, int duration_ms = 100) { + return thisModem().dtmfSendImpl(cmd, duration_ms); + } + + /* + * Messaging functions + */ + String sendUSSD(const String& code) { + return thisModem().sendUSSDImpl(code); + } + bool sendSMS(const String& number, const String& text) { + return thisModem().sendSMSImpl(number, text); + } + bool sendSMS_UTF16(const char* const number, const void* text, size_t len) { + return thisModem().sendSMS_UTF16Impl(number, text, len); + } + + /* + * Location functions + */ + String getGsmLocation() { + return thisModem().getGsmLocationImpl(); + } + + /* + * GPS location functions + */ + // No template interface or implementation of these functions + + /* + * Time functions + */ + String getGSMDateTime(TinyGSMDateTimeFormat format) { + return thisModem().getGSMDateTimeImpl(format); + } + + /* + * Battery & temperature functions + */ + uint16_t getBattVoltage() { + return thisModem().getBattVoltageImpl(); + } + int8_t getBattPercent() { + return thisModem().getBattPercentImpl(); + } + uint8_t getBattChargeState() { + return thisModem().getBattChargeStateImpl(); + } + bool + getBattStats(uint8_t& chargeState, int8_t& percent, uint16_t& milliVolts) { + return thisModem().getBattStatsImpl(chargeState, percent, milliVolts); + } + float getTemperature() { + return thisModem().getTemperatureImpl(); + } + + /* + * CRTP Helper + */ + protected: + const modemType& thisModem() const { + return static_cast(*this); + } + modemType& thisModem() { + return static_cast(*this); + } + + /* + * Inner Client + */ + public: + class GsmClient : public Client { + // Make all classes created from the modem template friends + friend class TinyGsmModem; + typedef TinyGsmFifo RxFifo; + + public: + // bool init(modemType* modem, uint8_t); + // int connect(const char* host, uint16_t port, int timeout_s); + + // Connect to a IP address given as an IPAddress object by + // converting said IP address to text + // int connect(IPAddress ip, uint16_t port, int timeout_s) { + // return connect(TinyGsmStringFromIp(ip).c_str(), port, + // timeout_s); + // } + // int connect(const char* host, uint16_t port) override { + // return connect(host, port, 75); + // } + // int connect(IPAddress ip, uint16_t port) override { + // return connect(ip, port, 75); + // } + + static String TinyGsmStringFromIp(IPAddress ip) { + String host; + host.reserve(16); + host += ip[0]; + host += "."; + host += ip[1]; + host += "."; + host += ip[2]; + host += "."; + host += ip[3]; + return host; + } + + // void stop(uint32_t maxWaitMs); + // void stop() override { + // stop(15000L); + // } + + // Writes data out on the client using the modem send functionality + size_t write(const uint8_t* buf, size_t size) override { + TINY_GSM_YIELD(); + at->maintain(); + return at->modemSend(buf, size, mux); + } + + size_t write(uint8_t c) override { + return write(&c, 1); + } + + size_t write(const char* str) { + if (str == NULL) return 0; + return write((const uint8_t*)str, strlen(str)); + } + + int available() override { + TINY_GSM_YIELD(); + switch (bufType) { + // Returns the number of characters available in the TinyGSM fifo + case NO_MODEM_BUFFER: + if (!rx.size() && sock_connected) { at->maintain(); } + return rx.size(); + + // Returns the combined number of characters available in the TinyGSM + // fifo and the modem chips internal fifo. + case READ_NO_CHECK: + if (!rx.size()) { at->maintain(); } + return rx.size() + sock_available; + + // Returns the combined number of characters available in the TinyGSM + // fifo and the modem chips internal fifo, doing an extra check-in + // with the modem to see if anything has arrived without a UURC. + case READ_AND_CHECK_SIZE: + if (!rx.size()) { + if (millis() - prev_check > 500) { + got_data = true; + prev_check = millis(); + } + at->maintain(); + } + return rx.size() + sock_available; + } + } + + int read(uint8_t* buf, size_t size) override { + TINY_GSM_YIELD(); + size_t cnt = 0; + uint32_t _startMillis = millis(); + + switch (bufType) { + // Reads characters out of the TinyGSM fifo, waiting for any URC's + // from the modem for new data if there's nothing in the fifo. + case NO_MODEM_BUFFER: + while (cnt < size && millis() - _startMillis < _timeout) { + size_t chunk = TinyGsmMin(size - cnt, rx.size()); + if (chunk > 0) { + rx.get(buf, chunk); + buf += chunk; + cnt += chunk; + continue; + } /* TODO: Read directly into user buffer? */ + if (!rx.size() && sock_connected) { at->maintain(); } + } + return cnt; + + // Reads characters out of the TinyGSM fifo, and from the modem chip's + // internal fifo if avaiable. + case READ_NO_CHECK: + at->maintain(); + while (cnt < size) { + size_t chunk = TinyGsmMin(size - cnt, rx.size()); + if (chunk > 0) { + rx.get(buf, chunk); + buf += chunk; + cnt += chunk; + continue; + } /* TODO: Read directly into user buffer? */ + at->maintain(); + if (sock_available > 0) { + int n = at->modemRead( + TinyGsmMin((uint16_t)rx.free(), sock_available), mux); + if (n == 0) break; + } else { + break; + } + } + return cnt; + + // Reads characters out of the TinyGSM fifo, and from the modem chips + // internal fifo if avaiable, also double checking with the modem if + // data has arrived without issuing a UURC. + case READ_AND_CHECK_SIZE: + at->maintain(); + while (cnt < size) { + size_t chunk = TinyGsmMin(size - cnt, rx.size()); + if (chunk > 0) { + rx.get(buf, chunk); + buf += chunk; + cnt += chunk; + continue; + } + // Workaround: Some modules "forget" to notify about data arrival + if (millis() - prev_check > 500) { + got_data = true; + prev_check = millis(); + } + // TODO(vshymanskyy): Read directly into user buffer? + at->maintain(); + if (sock_available > 0) { + int n = at->modemRead( + TinyGsmMin((uint16_t)rx.free(), sock_available), mux); + if (n == 0) break; + } else { + break; + } + } + return cnt; + } + } + + int read() override { + uint8_t c; + if (read(&c, 1) == 1) { return c; } + return -1; + } + + // TODO(SRGDamia1): Implement peek + int peek() override { + return -1; + } + + void flush() override { + at->stream.flush(); + } + + uint8_t connected() override { + if (available()) { return true; } + return sock_connected; + } + operator bool() override { + return connected(); + } + + /* + * Extended API + */ + + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + + protected: + // Read and dump anything remaining in the modem's internal buffer. + // Using this in the client stop() function. + // The socket will appear open in response to connected() even after it + // closes until all data is read from the buffer. + // Doing it this way allows the external mcu to find and get all of the + // data that it wants from the socket even if it was closed externally. + void dumpModemBuffer(uint32_t maxWaitMs) { + TINY_GSM_YIELD(); + rx.clear(); + at->maintain(); + uint32_t startMillis = millis(); + while (sock_available > 0 && (millis() - startMillis < maxWaitMs)) { + at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); + rx.clear(); + at->maintain(); + } + } + + modemType* at; + uint8_t mux; + uint16_t sock_available; + uint32_t prev_check; + bool sock_connected; + bool got_data; + RxFifo rx; + }; + + /* + * Inner Secure Client + */ + + /* + * Constructor + */ + + protected: + /* + * Basic functions + */ + + void setBaudImpl(uint32_t baud) { + thisModem().sendAT(GF("+IPR="), baud); + thisModem().waitResponse(); + } + + bool testATImpl(uint32_t timeout_ms = 10000L) { + for (uint32_t start = millis(); millis() - start < timeout_ms;) { + thisModem().sendAT(GF("")); + if (thisModem().waitResponse(200) == 1) { return true; } + delay(100); + } + return false; + } + + String getModemInfoImpl() { + thisModem().sendAT(GF("I")); + String res; + if (thisModem().waitResponse(1000L, res) != 1) { return ""; } + // Do the replaces twice so we cover both \r and \r\n type endings + res.replace("\r\nOK\r\n", ""); + res.replace("\rOK\r", ""); + res.replace("\r\n", " "); + res.replace("\r", " "); + res.trim(); + return res; + } + + String getModemNameImpl() { + thisModem().sendAT(GF("+CGMI")); + String res1; + if (thisModem().waitResponse(1000L, res1) != 1) { return "unknown"; } + res1.replace("\r\nOK\r\n", ""); + res1.replace("\rOK\r", ""); + res1.trim(); + + thisModem().sendAT(GF("+GMM")); + String res2; + if (thisModem().waitResponse(1000L, res2) != 1) { return "unknown"; } + res1.replace("\r\nOK\r\n", ""); + res1.replace("\rOK\r", ""); + res2.trim(); + + String name = res1 + String(' ') + res2; + DBG("### Modem:", name); + return name; + } + + void maintainImpl() { + switch (bufType) { + case READ_AND_CHECK_SIZE: + // Keep listening for modem URC's and proactively iterate through + // sockets asking if any data is avaiable + for (int mux = 0; mux < muxCount; mux++) { + GsmClient* sock = thisModem().sockets[mux]; + if (sock && sock->got_data) { + sock->got_data = false; + sock->sock_available = thisModem().modemGetAvailable(mux); + } + } + while (thisModem().stream.available()) { + thisModem().waitResponse(15, NULL, NULL); + } + break; + default: + // Just listen for any URC's + thisModem().waitResponse(100, NULL, NULL); + break; + } + } + + bool factoryDefaultImpl() { + thisModem().sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write + thisModem().waitResponse(); + thisModem().sendAT(GF("+IPR=0")); // Auto-baud + thisModem().waitResponse(); + thisModem().sendAT(GF("&W")); // Write configuration + return thisModem().waitResponse() == 1; + } + + /* + * Power functions + */ + protected: + bool radioOffImpl() { + thisModem().sendAT(GF("+CFUN=0")); + if (thisModem().waitResponse(10000L) != 1) { return false; } + delay(3000); + return true; + } + + bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; + + /* + * SIM card functions + */ + protected: + // Unlocks a sim via the 3GPP TS command AT+CPIN + bool simUnlockImpl(const char* pin) { + if (pin && strlen(pin) > 0) { + thisModem().sendAT(GF("+CPIN=\""), pin, GF("\"")); + return thisModem().waitResponse() == 1; + } + return true; + } + + // Gets the CCID of a sim card via AT+CCID + String getSimCCIDImpl() { + thisModem().sendAT(GF("+CCID")); + if (thisModem().waitResponse(GF("+CCID:")) != 1) { return ""; } + String res = thisModem().stream.readStringUntil('\n'); + thisModem().waitResponse(); + res.trim(); + return res; + } + + // Asks for TA Serial Number Identification (IMEI) via the V.25TER standard + // AT+GSN command + String getIMEIImpl() { + thisModem().sendAT(GF("+GSN")); + String res = thisModem().stream.readStringUntil('\n'); + thisModem().waitResponse(); + res.trim(); + return res; + } + + SimStatus getSimStatusImpl(uint32_t timeout_ms = 10000L) { + for (uint32_t start = millis(); millis() - start < timeout_ms;) { + thisModem().sendAT(GF("+CPIN?")); + if (thisModem().waitResponse(GF("+CPIN:")) != 1) { + delay(1000); + continue; + } + int status = + thisModem().waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), + GF("NOT INSERTED"), GF("NOT READY")); + thisModem().waitResponse(); + switch (status) { + case 2: + case 3: return SIM_LOCKED; + case 1: return SIM_READY; + default: return SIM_ERROR; + } + } + return SIM_ERROR; + } + + /* + * Generic network functions + */ + protected: + // Gets the modem's registration status via CREG/CGREG/CEREG + // CREG = Generic network registration + // CGREG = GPRS service registration + // CEREG = EPS registration for LTE modules + int getRegistrationStatusXREG(const char* regCommand) { + thisModem().sendAT('+', regCommand, '?'); + // check for any of the three for simplicity + int resp = + thisModem().waitResponse(GF("+CREG:"), GF("+CGREG:"), GF("+CEREG:")); + if (resp != 1 && resp != 2 && resp != 3) { return -1; } + thisModem().streamSkipUntil(','); /* Skip format (0) */ + int status = thisModem().stream.readStringUntil('\n').toInt(); + thisModem().waitResponse(); + return status; + } + + bool waitForNetworkImpl(uint32_t timeout_ms = 60000L) { + for (uint32_t start = millis(); millis() - start < timeout_ms;) { + if (thisModem().isNetworkConnected()) { return true; } + delay(250); + } + return false; + } + + // Gets signal quality report according to 3GPP TS command AT+CSQ + int16_t getSignalQualityImpl() { + thisModem().sendAT(GF("+CSQ")); + if (thisModem().waitResponse(GF("+CSQ:")) != 1) { return 99; } + int res = thisModem().stream.readStringUntil(',').toInt(); + thisModem().waitResponse(); + return res; + } + + /* + * GPRS functions + */ + protected: + // Checks if current attached to GPRS/EPS service + bool isGprsConnectedImpl() { + thisModem().sendAT(GF("+CGATT?")); + if (thisModem().waitResponse(GF("+CGATT:")) != 1) { return false; } + int res = thisModem().stream.readStringUntil('\n').toInt(); + thisModem().waitResponse(); + if (res != 1) { return false; } + + return thisModem().localIP() != IPAddress(0, 0, 0, 0); + } + + // Gets the current network operator via the 3GPP TS command AT+COPS + String getOperatorImpl() { + thisModem().sendAT(GF("+COPS?")); + if (thisModem().waitResponse(GF("+COPS:")) != 1) { return ""; } + thisModem().streamSkipUntil('"'); /* Skip mode and format */ + String res = thisModem().stream.readStringUntil('"'); + thisModem().waitResponse(); + return res; + } + + /* + * WiFi functions + */ + + bool networkConnectImpl(const char* ssid, const char* pwd) { + return false; + } + bool networkDisconnectImpl() { + return thisModem().gprsConnectImpl(); + } + + /* + * IP Address functions + */ + protected: + String getLocalIPImpl() { + thisModem().sendAT(GF("+CGPADDR=1")); + if (thisModem().waitResponse(GF("+CGPADDR:")) != 1) { return ""; } + thisModem().streamSkipUntil(','); // Skip context id + String res = thisModem().stream.readStringUntil('\r'); + if (thisModem().waitResponse() != 1) { return ""; } + return res; + } + + static IPAddress TinyGsmIpFromString(const String& strIP) { + int Parts[4] = { + 0, + }; + int Part = 0; + for (uint8_t i = 0; i < strIP.length(); i++) { + char c = strIP[i]; + if (c == '.') { + Part++; + if (Part > 3) { return IPAddress(0, 0, 0, 0); } + continue; + } else if (c >= '0' && c <= '9') { + Parts[Part] *= 10; + Parts[Part] += c - '0'; + } else { + if (Part == 3) break; } - continue; - } else if (c >= '0' && c <= '9') { - Parts[Part] *= 10; - Parts[Part] += c - '0'; + } + return IPAddress(Parts[0], Parts[1], Parts[2], Parts[3]); + } + + /* + * Phone Call functions + */ + protected: + bool callAnswerImpl() { + thisModem().sendAT(GF("A")); + return thisModem().waitResponse() == 1; + } + + // Returns true on pick-up, false on error/busy + bool callNumberImpl(const String& number) { + if (number == GF("last")) { + thisModem().sendAT(GF("DL")); } else { - if (Part == 3) break; + thisModem().sendAT(GF("D"), number, ";"); + } + int status = thisModem().waitResponse(60000L, GF("OK"), GF("BUSY"), + GF("NO ANSWER"), GF("NO CARRIER")); + switch (status) { + case 1: return true; + case 2: + case 3: return false; + default: return false; } } - return IPAddress(Parts[0], Parts[1], Parts[2], Parts[3]); -} -static inline -String TinyGsmDecodeHex7bit(String &instr) { - String result; - byte reminder = 0; - int bitstate = 7; - for (unsigned i=0; i> bitstate; - bitstate--; - if (bitstate == 0) { - char c = reminder; + bool callHangupImpl() { + thisModem().sendAT(GF("H")); + return thisModem().waitResponse() == 1; + } + + // 0-9,*,#,A,B,C,D + bool dtmfSendImpl(char cmd, int duration_ms = 100) { + duration_ms = constrain(duration_ms, 100, 1000); + + thisModem().sendAT(GF("+VTD="), + duration_ms / 100); // VTD accepts in 1/10 of a second + thisModem().waitResponse(); + + thisModem().sendAT(GF("+VTS="), cmd); + return thisModem().waitResponse(10000L) == 1; + } + + /* + * Messaging functions + */ + protected: + static inline String TinyGsmDecodeHex7bit(String& instr) { + String result; + byte reminder = 0; + int bitstate = 7; + for (unsigned i = 0; i < instr.length(); i += 2) { + char buf[4] = { + 0, + }; + buf[0] = instr[i]; + buf[1] = instr[i + 1]; + byte b = strtol(buf, NULL, 16); + + byte bb = b << (7 - bitstate); + char c = (bb + reminder) & 0x7F; result += c; - reminder = 0; - bitstate = 7; + reminder = b >> bitstate; + bitstate--; + if (bitstate == 0) { + char cc = reminder; + result += cc; + reminder = 0; + bitstate = 7; + } } + return result; } - return result; -} -static inline -String TinyGsmDecodeHex8bit(String &instr) { - String result; - for (unsigned i=0; imaintain(); \ - return at->modemSend(buf, size, mux); \ - } \ - \ - virtual size_t write(uint8_t c) {\ - return write(&c, 1); \ - }\ - \ - virtual size_t write(const char *str) { \ - if (str == NULL) return 0; \ - return write((const uint8_t *)str, strlen(str)); \ - } - - -// Returns the combined number of characters available in the TinyGSM fifo -// and the modem chips internal fifo, doing an extra check-in with the -// modem to see if anything has arrived without a UURC. -#define TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() \ - virtual int available() { \ - TINY_GSM_YIELD(); \ - if (!rx.size()) { \ - /* Workaround: sometimes module forgets to notify about data arrival. - TODO: Currently we ping the module periodically, - but maybe there's a better indicator that we need to poll */ \ - if (millis() - prev_check > 500) { \ - got_data = true; \ - prev_check = millis(); \ - } \ - at->maintain(); \ - } \ - return rx.size() + sock_available; \ - } - - -// Returns the combined number of characters available in the TinyGSM fifo and -// the modem chips internal fifo. Use this if you don't expect to miss any URC's. -#define TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() \ - virtual int available() { \ - TINY_GSM_YIELD(); \ - if (!rx.size()) { \ - at->maintain(); \ - } \ - return rx.size() + sock_available; \ - } - - -// Returns the number of characters available in the TinyGSM fifo -// Assumes the modem chip has no internal fifo -#define TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() \ - virtual int available() { \ - TINY_GSM_YIELD(); \ - if (!rx.size() && sock_connected) { \ - at->maintain(); \ - } \ - return rx.size(); \ - } - - -#define TINY_GSM_CLIENT_READ_OVERLOAD() \ - virtual int read() { \ - uint8_t c; \ - if (read(&c, 1) == 1) { \ - return c; \ - } \ - return -1; \ - } - -// Reads characters out of the TinyGSM fifo, and from the modem chips internal -// fifo if avaiable, also double checking with the modem if data has arrived -// without issuing a UURC. -#define TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() \ - virtual int read(uint8_t *buf, size_t size) { \ - TINY_GSM_YIELD(); \ - at->maintain(); \ - size_t cnt = 0; \ - while (cnt < size) { \ - size_t chunk = TinyGsmMin(size-cnt, rx.size()); \ - if (chunk > 0) { \ - rx.get(buf, chunk); \ - buf += chunk; \ - cnt += chunk; \ - continue; \ - } \ - /* Workaround: sometimes module forgets to notify about data arrival. - TODO: Currently we ping the module periodically, - but maybe there's a better indicator that we need to poll */ \ - if (millis() - prev_check > 500) { \ - got_data = true; \ - prev_check = millis(); \ - } \ - /* TODO: Read directly into user buffer? */ \ - at->maintain(); \ - if (sock_available > 0) { \ - int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); \ - if (n == 0) break; \ - } else { \ - break; \ - } \ - } \ - return cnt; \ - } \ - TINY_GSM_CLIENT_READ_OVERLOAD() - - -// Reads characters out of the TinyGSM fifo, and from the modem chips internal -// fifo if avaiable. Use this if you don't expect to miss any URC's. -#define TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() \ - virtual int read(uint8_t *buf, size_t size) { \ - TINY_GSM_YIELD(); \ - at->maintain(); \ - size_t cnt = 0; \ - while (cnt < size) { \ - size_t chunk = TinyGsmMin(size-cnt, rx.size()); \ - if (chunk > 0) { \ - rx.get(buf, chunk); \ - buf += chunk; \ - cnt += chunk; \ - continue; \ - } \ - /* TODO: Read directly into user buffer? */ \ - at->maintain(); \ - if (sock_available > 0) { \ - int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); \ - if (n == 0) break; \ - } else { \ - break; \ - } \ - } \ - return cnt; \ - } \ - TINY_GSM_CLIENT_READ_OVERLOAD() - - -// Reads characters out of the TinyGSM fifo, waiting for any URC's from the -// modem for new data if there's nothing in the fifo. This assumes the -//modem chip itself has no fifo. -#define TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() \ - virtual int read(uint8_t *buf, size_t size) { \ - TINY_GSM_YIELD(); \ - size_t cnt = 0; \ - uint32_t _startMillis = millis(); \ - while (cnt < size && millis() - _startMillis < _timeout) { \ - size_t chunk = TinyGsmMin(size-cnt, rx.size()); \ - if (chunk > 0) { \ - rx.get(buf, chunk); \ - buf += chunk; \ - cnt += chunk; \ - continue; \ - } \ - /* TODO: Read directly into user buffer? */ \ - if (!rx.size() && sock_connected) { \ - at->maintain(); \ - } \ - } \ - return cnt; \ - } \ - \ - virtual int read() { \ - uint8_t c; \ - if (read(&c, 1) == 1) { \ - return c; \ - } \ - return -1; \ - } - - -// Read and dump anything remaining in the modem's internal buffer. -// Using this in the client stop() function. -// The socket will appear open in response to connected() even after it -// closes until all data is read from the buffer. -// Doing it this way allows the external mcu to find and get all of the data -// that it wants from the socket even if it was closed externally. -#define TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() \ - TINY_GSM_YIELD(); \ - rx.clear(); \ - at->maintain(); \ - unsigned long startMillis = millis(); \ - while (sock_available > 0 && (millis() - startMillis < maxWaitMs)) { \ - at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); \ - rx.clear(); \ - at->maintain(); \ + String sendUSSDImpl(const String& code) { + // Set preferred message format to text mode + thisModem().sendAT(GF("+CMGF=1")); + thisModem().waitResponse(); + // Set 8-bit hexadecimal alphabet (3GPP TS 23.038) + thisModem().sendAT(GF("+CSCS=\"HEX\"")); + thisModem().waitResponse(); + // Send the message + thisModem().sendAT(GF("+CUSD=1,\""), code, GF("\"")); + if (thisModem().waitResponse() != 1) { return ""; } + if (thisModem().waitResponse(10000L, GF("+CUSD:")) != 1) { return ""; } + thisModem().stream.readStringUntil('"'); + String hex = thisModem().stream.readStringUntil('"'); + thisModem().stream.readStringUntil(','); + int dcs = thisModem().stream.readStringUntil('\n').toInt(); + + if (dcs == 15) { + return TinyGsmDecodeHex8bit(hex); + } else if (dcs == 72) { + return TinyGsmDecodeHex16bit(hex); + } else { + return hex; } + } + bool sendSMSImpl(const String& number, const String& text) { + // Set preferred message format to text mode + thisModem().sendAT(GF("+CMGF=1")); + thisModem().waitResponse(); + // Set GSM 7 bit default alphabet (3GPP TS 23.038) + thisModem().sendAT(GF("+CSCS=\"GSM\"")); + thisModem().waitResponse(); + thisModem().sendAT(GF("+CMGS=\""), number, GF("\"")); + if (thisModem().waitResponse(GF(">")) != 1) { return false; } + thisModem().stream.print(text); // Actually send the message + thisModem().stream.write(static_cast(0x1A)); // Terminate the message + thisModem().stream.flush(); + return thisModem().waitResponse(60000L) == 1; + } -// The peek, flush, and connected functions -#define TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() \ - virtual int peek() { return -1; } /* TODO */ \ - \ - virtual void flush() { at->stream.flush(); } \ - \ - virtual uint8_t connected() { \ - if (available()) { \ - return true; \ - } \ - return sock_connected; \ - } \ - virtual operator bool() { return connected(); } - - -// Set baud rate via the V.25TER standard IPR command -#define TINY_GSM_MODEM_SET_BAUD_IPR() \ - void setBaud(unsigned long baud) { \ - sendAT(GF("+IPR="), baud); \ - } - - -// Test response to AT commands -#define TINY_GSM_MODEM_TEST_AT() \ - bool testAT(unsigned long timeout_ms = 10000L) { \ - for (unsigned long start = millis(); millis() - start < timeout_ms; ) { \ - sendAT(GF("")); \ - if (waitResponse(200) == 1) return true; \ - delay(100); \ - } \ - return false; \ - } - - -// Keeps listening for modem URC's and iterates through sockets -// to see if any data is avaiable -#define TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() \ - void maintain() { \ - for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { \ - GsmClient* sock = sockets[mux]; \ - if (sock && sock->got_data) { \ - sock->got_data = false; \ - sock->sock_available = modemGetAvailable(mux); \ - } \ - } \ - while (stream.available()) { \ - waitResponse(15, NULL, NULL); \ - } \ - } - - -// Keeps listening for modem URC's - doesn't check socks because -// modem has no internal fifo -#define TINY_GSM_MODEM_MAINTAIN_LISTEN() \ - void maintain() { \ - waitResponse(100, NULL, NULL); \ - } - - -// Asks for modem information via the V.25TER standard ATI command -// NOTE: The actual value and style of the response is quite varied -#define TINY_GSM_MODEM_GET_INFO_ATI() \ - String getModemInfo() { \ - sendAT(GF("I")); \ - String res; \ - if (waitResponse(1000L, res) != 1) { \ - return ""; \ - } \ - res.replace(GSM_NL "OK" GSM_NL, ""); \ - res.replace(GSM_NL, " "); \ - res.trim(); \ - return res; \ - } - - -// Unlocks a sim via the 3GPP TS command AT+CPIN -#define TINY_GSM_MODEM_SIM_UNLOCK_CPIN() \ - bool simUnlock(const char *pin) { \ - if (pin && strlen(pin) > 0) { \ - sendAT(GF("+CPIN=\""), pin, GF("\"")); \ - return waitResponse() == 1; \ - } \ - return true; \ - } - - -// Gets the CCID of a sim card via AT+CCID -#define TINY_GSM_MODEM_GET_SIMCCID_CCID() \ - String getSimCCID() { \ - sendAT(GF("+CCID")); \ - if (waitResponse(GF(GSM_NL "+CCID:")) != 1) { \ - return ""; \ - } \ - String res = stream.readStringUntil('\n'); \ - waitResponse(); \ - res.trim(); \ - return res; \ - } - - -// Asks for TA Serial Number Identification (IMEI) via the V.25TER standard AT+GSN command -#define TINY_GSM_MODEM_GET_IMEI_GSN() \ - String getIMEI() { \ - sendAT(GF("+GSN")); \ - if (waitResponse(GF(GSM_NL)) != 1) { \ - return ""; \ - } \ - String res = stream.readStringUntil('\n'); \ - waitResponse(); \ - res.trim(); \ - return res; \ - } - - -// Gets the modem's registration status via CREG/CGREG/CEREG -// CREG = Generic network registration -// CGREG = GPRS service registration -// CEREG = EPS registration for LTE modules -#define TINY_GSM_MODEM_GET_REGISTRATION_XREG(regCommand) \ - RegStatus getRegistrationStatus() { \ - sendAT(GF("+" #regCommand "?")); \ - if (waitResponse(GF(GSM_NL "+" #regCommand ":")) != 1) { \ - return REG_UNKNOWN; \ - } \ - streamSkipUntil(','); /* Skip format (0) */ \ - int status = stream.readStringUntil('\n').toInt(); \ - waitResponse(); \ - return (RegStatus)status; \ - } - - -// Gets the current network operator via the 3GPP TS command AT+COPS -#define TINY_GSM_MODEM_GET_OPERATOR_COPS() \ - String getOperator() { \ - sendAT(GF("+COPS?")); \ - if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { \ - return ""; \ - } \ - streamSkipUntil('"'); /* Skip mode and format */ \ - String res = stream.readStringUntil('"'); \ - waitResponse(); \ - return res; \ - } - - -// Waits for network attachment -#define TINY_GSM_MODEM_WAIT_FOR_NETWORK() \ - bool waitForNetwork(unsigned long timeout_ms = 60000L) { \ - for (unsigned long start = millis(); millis() - start < timeout_ms; ) { \ - if (isNetworkConnected()) { \ - return true; \ - } \ - delay(250); \ - } \ - return false; \ - } - - -// Checks if current attached to GPRS/EPS service -#define TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() \ - bool isGprsConnected() { \ - sendAT(GF("+CGATT?")); \ - if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { \ - return false; \ - } \ - int res = stream.readStringUntil('\n').toInt(); \ - waitResponse(); \ - if (res != 1) \ - return false; \ - \ - return localIP() != IPAddress(0,0,0,0); \ - } - - -// Gets signal quality report according to 3GPP TS command AT+CSQ -#define TINY_GSM_MODEM_GET_CSQ() \ - int16_t getSignalQuality() { \ - sendAT(GF("+CSQ")); \ - if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { \ - return 99; \ - } \ - int res = stream.readStringUntil(',').toInt(); \ - waitResponse(); \ - return res; \ - } - - -// Yields up to a time-out period and then reads a character from the stream into the mux FIFO -// TODO: Do we need to wait two _timeout periods for no character return? Will wait once in the first -// "while !stream.available()" and then will wait again in the stream.read() function. -#define TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT \ - uint32_t startMillis = millis(); \ - while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } \ - char c = stream.read(); \ - sockets[mux]->rx.put(c); - - -// Utility templates for writing/skipping characters on a stream -#define TINY_GSM_MODEM_STREAM_UTILITIES() \ - template \ - void streamWrite(T last) { \ - stream.print(last); \ - } \ - \ - template \ - void streamWrite(T head, Args... tail) { \ - stream.print(head); \ - streamWrite(tail...); \ - } \ - \ - template \ - void sendAT(Args... cmd) { \ - streamWrite("AT", cmd..., GSM_NL); \ - stream.flush(); \ - TINY_GSM_YIELD(); \ - /* DBG("### AT:", cmd...); */ \ - } \ - \ - bool streamSkipUntil(const char c, const unsigned long timeout_ms = 1000L) { \ - unsigned long startMillis = millis(); \ - while (millis() - startMillis < timeout_ms) { \ - while (millis() - startMillis < timeout_ms && !stream.available()) { \ - TINY_GSM_YIELD(); \ - } \ - if (stream.read() == c) { \ - return true; \ - } \ - } \ - return false; \ - } - - -//Common methods for UTF8/UTF16 SMS. -//Supported by: BG96, M95, MC60, SIM5360, SIM7000, SIM7600, SIM800 -template -class TinyGsmUTFSMS { -public: - + // Common methods for UTF8/UTF16 SMS. + // Supported by: BG96, M95, MC60, SIM5360, SIM7000, SIM7600, SIM800 class UTF8Print : public Print { - public: - UTF8Print(Print& p) : p(p) {} - virtual size_t write(const uint8_t c) override { - if(prv < 0xC0) { - if(c < 0xC0) printHex(c); + public: + explicit UTF8Print(Print& p) : p(p) {} + size_t write(const uint8_t c) override { + if (prv < 0xC0) { + if (c < 0xC0) printHex(c); prv = c; } else { - uint16_t v = uint16_t(prv)<<8 | c; - v -= (v>>8 == 0xD0)? 0xCC80 : 0xCD40; + uint16_t v = uint16_t(prv) << 8 | c; + v -= (v >> 8 == 0xD0) ? 0xCC80 : 0xCD40; printHex(v); prv = 0; } return 1; } - private: - Print& p; + + private: + Print& p; uint8_t prv = 0; - void printHex(const uint16_t v) { + void printHex(const uint16_t v) { uint8_t c = v >> 8; if (c < 0x10) p.print('0'); p.print(c, HEX); @@ -680,42 +1033,185 @@ public: }; bool sendSMS_UTF8_begin(const char* const number) { - static_cast(this)->sendAT(GF("+CMGF=1")); - static_cast(this)->waitResponse(); - static_cast(this)->sendAT(GF("+CSCS=\"HEX\"")); - static_cast(this)->waitResponse(); - static_cast(this)->sendAT(GF("+CSMP=17,167,0,8")); - static_cast(this)->waitResponse(); - - static_cast(this)->sendAT(GF("+CMGS=\""), number, GF("\"")); - return static_cast(this)->waitResponse(GF(">")) == 1; + thisModem().sendAT(GF("+CMGF=1")); + thisModem().waitResponse(); + thisModem().sendAT(GF("+CSCS=\"HEX\"")); + thisModem().waitResponse(); + thisModem().sendAT(GF("+CSMP=17,167,0,8")); + thisModem().waitResponse(); + + thisModem().sendAT(GF("+CMGS=\""), number, GF("\"")); + return thisModem().waitResponse(GF(">")) == 1; } bool sendSMS_UTF8_end() { - static_cast(this)->stream.write((char)0x1A); - static_cast(this)->stream.flush(); - return static_cast(this)->waitResponse(60000L) == 1; + thisModem().stream.write(static_cast(0x1A)); + thisModem().stream.flush(); + return thisModem().waitResponse(60000L) == 1; } UTF8Print sendSMS_UTF8_stream() { - return UTF8Print(static_cast(this)->stream); + return UTF8Print(thisModem().stream); } - bool sendSMS_UTF16(const char* const number, const void* text, size_t len) { - if (!sendSMS_UTF8_begin(number)) { - return false; - } + bool + sendSMS_UTF16Impl(const char* const number, const void* text, size_t len) { + if (!sendSMS_UTF8_begin(number)) { return false; } - uint16_t* t = (uint16_t*)text; - for (size_t i=0; i(text); + for (size_t i = 0; i < len; i++) { uint8_t c = t[i] >> 8; - if (c < 0x10) { static_cast(this)->stream.print('0'); } - static_cast(this)->stream.print(c, HEX); + if (c < 0x10) { thisModem().stream.print('0'); } + thisModem().stream.print(c, HEX); c = t[i] & 0xFF; - if (c < 0x10) { static_cast(this)->stream.print('0'); } - static_cast(this)->stream.print(c, HEX); + if (c < 0x10) { thisModem().stream.print('0'); } + thisModem().stream.print(c, HEX); } - + return sendSMS_UTF8_end(); } + + /* + * Location functions + */ + protected: + String getGsmLocationImpl() { + thisModem().sendAT(GF("+CIPGSMLOC=1,1")); + if (thisModem().waitResponse(10000L, GF("+CIPGSMLOC:")) != 1) { return ""; } + String res = thisModem().stream.readStringUntil('\n'); + thisModem().waitResponse(); + res.trim(); + return res; + } + + /* + * GPS location functions + */ + public: + /* + * Time functions + */ + protected: + String getGSMDateTimeImpl(TinyGSMDateTimeFormat format) { + thisModem().sendAT(GF("+CCLK?")); + if (thisModem().waitResponse(2000L, GF("+CCLK: \"")) != 1) { return ""; } + + String res; + + switch (format) { + case DATE_FULL: res = thisModem().stream.readStringUntil('"'); break; + case DATE_TIME: + thisModem().streamSkipUntil(','); + res = thisModem().stream.readStringUntil('"'); + break; + case DATE_DATE: res = thisModem().stream.readStringUntil(','); break; + } + return res; + } + + /* + * Battery & temperature functions + */ + protected: + // Use: float vBatt = modem.getBattVoltage() / 1000.0; + uint16_t getBattVoltageImpl() { + thisModem().sendAT(GF("+CBC")); + if (thisModem().waitResponse(GF("+CBC:")) != 1) { return 0; } + thisModem().streamSkipUntil(','); // Skip battery charge status + thisModem().streamSkipUntil(','); // Skip battery charge level + // return voltage in mV + uint16_t res = thisModem().stream.readStringUntil(',').toInt(); + // Wait for final OK + thisModem().waitResponse(); + return res; + } + + int8_t getBattPercentImpl() { + thisModem().sendAT(GF("+CBC")); + if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; } + thisModem().streamSkipUntil(','); // Skip battery charge status + // Read battery charge level + int res = thisModem().stream.readStringUntil(',').toInt(); + // Wait for final OK + thisModem().waitResponse(); + return res; + } + + uint8_t getBattChargeStateImpl() { + thisModem().sendAT(GF("+CBC?")); + if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; } + // Read battery charge status + int res = thisModem().stream.readStringUntil(',').toInt(); + // Wait for final OK + thisModem().waitResponse(); + return res; + } + + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) { + thisModem().sendAT(GF("+CBC?")); + if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; } + chargeState = thisModem().stream.readStringUntil(',').toInt(); + percent = thisModem().stream.readStringUntil(',').toInt(); + milliVolts = thisModem().stream.readStringUntil('\n').toInt(); + // Wait for final OK + thisModem().waitResponse(); + return true; + } + + float getTemperatureImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + + /* + * Client related functions + */ + protected: + /* + Utilities + */ + + // Utility templates for writing/skipping characters on a stream + template + void streamWrite(T last) { + thisModem().stream.print(last); + } + + template + void streamWrite(T head, Args... tail) { + thisModem().stream.print(head); + thisModem().streamWrite(tail...); + } + + // template void sendAT(Args... cmd) { + // thisModem().streamWrite("AT", cmd..., thisModem().gsmNL); + // thisModem().stream.flush(); + // TINY_GSM_YIELD(); /* DBG("### AT:", cmd...); */ + // } + + bool streamSkipUntil(const char c, const uint32_t timeout_ms = 1000L) { + uint32_t startMillis = millis(); + while (millis() - startMillis < timeout_ms) { + while (millis() - startMillis < timeout_ms && + !thisModem().stream.available()) { + TINY_GSM_YIELD(); + } + if (thisModem().stream.read() == c) { return true; } + } + return false; + } + + // Yields up to a time-out period and then reads a character from the stream + // into the mux FIFO + // TODO(SRGDamia1): Do we need to wait two _timeout periods for no + // character return? Will wait once in the first "while + // !stream.available()" and then will wait again in the stream.read() + // function. + void moveCharFromStreamToFifo(uint8_t mux) { + uint32_t startMillis = millis(); + while (!thisModem().stream.available() && + (millis() - startMillis < thisModem().sockets[mux]->_timeout)) { + TINY_GSM_YIELD(); + } + char c = thisModem().stream.read(); + thisModem().sockets[mux]->rx.put(c); + } }; -#endif +#endif // SRC_TINYGSMCOMMON_H_ diff --git a/tools/test_build/test_build.ino b/tools/test_build/test_build.ino index 296f623..1f8d80b 100644 --- a/tools/test_build/test_build.ino +++ b/tools/test_build/test_build.ino @@ -25,23 +25,35 @@ void setup() { void loop() { - // Test the start/restart functions - modem.restart(); + // Test the basic functions + modem.init(); modem.begin(); + modem.setBaud(115200); modem.testAT(); modem.factoryDefault(); + modem.getModemInfo(); + modem.getModemName(); + modem.maintain(); + modem.hasSSL(); + modem.hasWifi(); + modem.hasGPRS(); + + // Test Power functions + modem.restart(); + // modem.sleepEnable(); + modem.radioOff(); + modem.poweroff(); // Test the SIM card functions #if defined(TINY_GSM_MODEM_HAS_GPRS) modem.getSimCCID(); modem.getIMEI(); modem.getSimStatus(); - modem.getRegistrationStatus(); modem.getOperator(); #endif - // Test the Networking functions + modem.getRegistrationStatus(); modem.getSignalQuality(); modem.localIP(); @@ -61,7 +73,7 @@ void loop() { client.print(String("Host: ") + server + "\r\n"); client.print("Connection: close\r\n\r\n"); - unsigned long timeout = millis(); + uint32_t timeout = millis(); while (client.connected() && millis() - timeout < 10000L) { while (client.available()) { client.read(); @@ -77,4 +89,9 @@ void loop() { #if defined(TINY_GSM_MODEM_HAS_WIFI) modem.networkDisconnect(); #endif + + // Test battery and temperature functions + // modem.getBattVoltage(); + // modem.getBattPercent(); + // modem.getTemperature(); }