diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..1329b59 --- /dev/null +++ b/.clang-format @@ -0,0 +1,112 @@ +--- +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 + - Regex: '.*.tpp' + Priority: 4 +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: 25 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 600 +PenaltyReturnTypeOnItsOwnLine: 50 +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/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index fc39447..ef24062 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -22,7 +22,7 @@ with your board before submitting any issues. Main processor board: Modem: -TinyGSM version: +TinyGSM version: Code: ### Scenario, steps to reproduce diff --git a/.gitignore b/.gitignore index 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/README.md b/README.md index 4ab951a..59bc900 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ TinyGSM also pulls data gently from the modem (whenever possible), so it can ope - Neoway M590 - u-blox 2G, 3G, 4G, and LTE Cat1 Cellular Modems (many modules including LEON-G100, LISA-U2xx, SARA-G3xx, SARA-U2xx, TOBY-L2xx, LARA-R2xx, MPCI-L2xx) - u-blox LTE-M Modems (SARA-R4xx, SARA-N4xx, _but NOT SARA-N2xx_) -- Sequans Monarch LTE Cat M1/NB1 +- Sequans Monarch LTE Cat M1/NB1 (VZM20Q) - Quectel BG96 - Quectel M95 - Quectel MC60 ***(alpha)*** @@ -83,26 +83,29 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - SIM800, u-Blox, XBee _cellular_, ESP8266, and Sequans Monarch - Note: only some device models or firmware revisions have this feature (SIM8xx R14.18, A7, etc.) - Not yet supported on: - - Quectel modems, SIM7000, SIM5360/5320/7100/7500/7600 + - Quectel modems, SIM7000, SIM5360/5320, SIM7100/7500/7600 - Not possible on: - - SIM900, A6/A7, M560, XBee _WiFi_ + - SIM900, A6/A7, Neoway M590, XBee _WiFi_ **USSD** - Sending USSD requests and decoding 7,8,16-bit responses - Supported on: - - SIM800/SIM900, SIM7000 - - Not yet supported on: - - Quectel modems, SIM5360/5320/7100/7500/7600, XBee + - All SIMCom modems, Quectel modems, most u-blox + - Not possible on: + - XBee, u-blox SARA R4/N4, ESP8266 (obviously) **SMS** - Only _sending_ SMS is supported, not receiving - - Supported on: - - SIM800/SIM900, SIM7000, XBee - - Not yet supported on: - - Quectel modems, SIM5360/5320/7100/7500/7600 + - Supported on all cellular modules **Voice Calls** -- Only Supported on SIM800 and A6/A7/A20 +- Supported on: + - SIM800/SIM900, A6/A7, Quectel modems, u-blox +- Not yet supported on: + - SIM7000, SIM5360/5320/7100, SIM7500/7600/7800, VZM20Q (Monarch) +- Not possible on: + - XBee (any type), u-blox SARA R4/N4, Neoway M590, ESP8266 (obviously) +- Functions: - Dial, hangup - Receiving calls - Incoming event (RING) @@ -111,9 +114,9 @@ Watch this repo for new updates! And of course, contributions are welcome ;) **Location** - GPS/GNSS - - SIM808 and SIM7000 only + - SIM808, SIM7000, SIM7500/7600/7800 only - GSM location service - - SIM800, SIM and SIM7000 only + - SIM800, SIM7000, Quectel, u-blox **Credits** - Primary Authors/Contributors: 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 7a8fb99..7857cb3 100644 --- a/examples/AllFunctions/AllFunctions.ino +++ b/examples/AllFunctions/AllFunctions.ino @@ -39,28 +39,33 @@ // or Software Serial on Uno, Nano //#include -//SoftwareSerial SerialAT(2, 3); // RX, TX +// 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 // Range to attempt to autobaud #define GSM_AUTOBAUD_MIN 9600 -#define GSM_AUTOBAUD_MAX 38400 +#define GSM_AUTOBAUD_MAX 115200 /* * Tests enabled */ #define TINY_GSM_TEST_GPRS true #define TINY_GSM_TEST_WIFI false +#define TINY_GSM_TEST_TCP true +#define TINY_GSM_TEST_SSL true #define TINY_GSM_TEST_CALL true #define TINY_GSM_TEST_SMS true #define TINY_GSM_TEST_USSD true #define TINY_GSM_TEST_BATTERY true -#define TINY_GSM_TEST_GPS false +#define TINY_GSM_TEST_TEMPERATURE true +#define TINY_GSM_TEST_GSM_LOCATION true +#define TINY_GSM_TEST_TIME true +#define TINY_GSM_TEST_GPS true // powerdown modem after tests #define TINY_GSM_POWERDOWN false @@ -68,30 +73,28 @@ #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"; +const char apn[] = "YourAPN"; const char gprsUser[] = ""; const char gprsPass[] = ""; // Your WiFi connection credentials, if applicable -const char wifiSSID[] = "YourSSID"; +const char wifiSSID[] = "YourSSID"; const char wifiPass[] = "YourWiFiPass"; +// Server details to test TCP/SSL +const char server[] = "vsh.pp.ua"; +const char resource[] = "/TinyGSM/logo.txt"; + #include #if TINY_GSM_TEST_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS #undef TINY_GSM_TEST_GPRS -#undef TINY_GSM_TEST_CALL -#undef TINY_GSM_TEST_SMS -#undef TINY_GSM_TEST_USSD #undef TINY_GSM_TEST_WIFI #define TINY_GSM_TEST_GPRS false -#define TINY_GSM_TEST_CALL false -#define TINY_GSM_TEST_SMS false -#define TINY_GSM_TEST_USSD false #define TINY_GSM_TEST_WIFI true #endif #if TINY_GSM_TEST_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI @@ -100,10 +103,6 @@ const char wifiPass[] = "YourWiFiPass"; #define TINY_GSM_USE_GPRS true #define TINY_GSM_USE_WIFI false #endif -#if TINY_GSM_TEST_GPS && not defined TINY_GSM_MODEM_HAS_GPS -#undef TINY_GSM_TEST_GPS -#define TINY_GSM_TEST_GPS false -#endif #ifdef DUMP_AT_COMMANDS #include @@ -120,28 +119,28 @@ void setup() { // !!!!!!!!!!! // Set your reset, enable, power pins here + pinMode(23, OUTPUT); + digitalWrite(23, HIGH); // !!!!!!!!!!! DBG("Wait..."); + delay(6000); // Set GSM module baud rate - TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); - //SerialAT.begin(9600); - delay(3000); + TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX); + // SerialAT.begin(9600); } void loop() { - // Restart takes quite some time // To skip it, call init() instead of restart() DBG("Initializing modem..."); if (!modem.restart()) { - // if (!modem.init()) { + // if (!modem.init()) { DBG("Failed to restart modem, delaying 10s and retrying"); - delay(3000); + delay(10000L); // restart autobaud in case GSM just rebooted - TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX); - delay(10000); + // TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX); return; } @@ -153,7 +152,7 @@ void loop() { #if TINY_GSM_TEST_GPRS // Unlock your SIM card with a PIN if needed - if ( GSM_PIN && modem.getSimStatus() != 3 ) { + if (GSM_PIN && modem.getSimStatus() != 3) { modem.simUnlock(GSM_PIN); } #endif @@ -174,7 +173,7 @@ void loop() { #endif DBG("Waiting for network..."); - if (!modem.waitForNetwork()) { + if (!modem.waitForNetwork(600000L)) { delay(10000); return; } @@ -199,6 +198,9 @@ void loop() { String imei = modem.getIMEI(); DBG("IMEI:", imei); + String imsi = modem.getIMSI(); + DBG("IMSI:", imsi); + String cop = modem.getOperator(); DBG("Operator:", cop); @@ -207,19 +209,9 @@ void loop() { int csq = modem.getSignalQuality(); DBG("Signal quality:", csq); - - // This is only supported on SIMxxx series - // String gsmLoc = modem.getGsmLocation(); - // DBG("GSM location:", gsmLoc); - - // This is only supported on SIMxxx series - // String gsmTime = modem.getGSMDateTime(DATE_TIME); - // DBG("GSM Time:", gsmTime); - // String gsmDate = modem.getGSMDateTime(DATE_DATE); - // DBG("GSM Date:", gsmDate); #endif -#if TINY_GSM_TEST_USSD +#if TINY_GSM_TEST_USSD && defined TINY_GSM_MODEM_HAS_SMS String ussd_balance = modem.sendUSSD("*111#"); DBG("Balance (USSD):", ussd_balance); @@ -227,23 +219,70 @@ void loop() { DBG("Phone number (USSD):", ussd_phone_num); #endif -#if TINY_GSM_TEST_GPS - modem.enableGPS(); - String gps_raw = modem.getGPSraw(); - modem.disableGPS(); - DBG("GPS raw data:", gps_raw); +#if TINY_GSM_TEST_TCP && defined TINY_GSM_MODEM_HAS_TCP + TinyGsmClient client(modem, 0); + const int port = 80; + DBG("Connecting to ", server); + if (!client.connect(server, port)) { + DBG("... failed"); + } else { + // Make a HTTP GET request: + client.print(String("GET ") + resource + " HTTP/1.0\r\n"); + client.print(String("Host: ") + server + "\r\n"); + client.print("Connection: close\r\n\r\n"); + + // Wait for data to arrive + uint32_t start = millis(); + while (client.connected() && !client.available() && + millis() - start < 30000L) { + delay(100); + }; + + // Read data + start = millis(); + while (client.connected() && millis() - start < 5000L) { + while (client.available()) { + SerialMon.write(client.read()); + start = millis(); + } + } + client.stop(); + } #endif -#if TINY_GSM_TEST_SMS && defined(SMS_TARGET) - res = modem.sendSMS(SMS_TARGET, String("Hello from ") + imei); - DBG("SMS:", res ? "OK" : "fail"); - - // This is only supported on SIMxxx series - res = modem.sendSMS_UTF16(SMS_TARGET, u"Привіііт!", 9); - DBG("UTF16 SMS:", res ? "OK" : "fail"); +#if TINY_GSM_TEST_SSL && defined TINY_GSM_MODEM_HAS_SSL + TinyGsmClientSecure secureClient(modem, 1); + const int securePort = 443; + DBG("Connecting to ", server); + if (!secureClient.connect(server, securePort)) { + DBG("... failed"); + } else { + // Make a HTTP GET request: + secureClient.print(String("GET ") + resource + " HTTP/1.0\r\n"); + secureClient.print(String("Host: ") + server + "\r\n"); + secureClient.print("Connection: close\r\n\r\n"); + + // Wait for data to arrive + uint32_t startS = millis(); + while (secureClient.connected() && !secureClient.available() && + millis() - startS < 30000L) { + delay(100); + }; + + // Read data + startS = millis(); + while (secureClient.connected() && millis() - startS < 5000L) { + while (secureClient.available()) { + SerialMon.write(secureClient.read()); + startS = millis(); + } + } + secureClient.stop(); + } #endif -#if TINY_GSM_TEST_CALL && defined(CALL_TARGET) +#if TINY_GSM_TEST_CALL && defined TINY_GSM_MODEM_HAS_CALLING && \ + defined CALL_TARGET DBG("Calling:", CALL_TARGET); // This is NOT supported on M590 @@ -257,7 +296,7 @@ void loop() { modem.dtmfSend('A', 1000); // Play DTMF 0..4, default duration (100ms) - for (char tone='0'; tone<='4'; tone++) { + for (char tone = '0'; tone <= '4'; tone++) { modem.dtmfSend(tone); } @@ -268,21 +307,120 @@ void loop() { } #endif -#if TINY_GSM_TEST_BATTERY - uint8_t chargeState = -99; - int8_t percent = -99; - uint16_t milliVolts = -9999; - modem.getBattStats(chargeState, percent, milliVolts); - DBG("Battery charge state:", chargeState); - DBG("Battery charge 'percent':", percent); - DBG("Battery voltage:", milliVolts / 1000.0F); +#if TINY_GSM_TEST_SMS && defined TINY_GSM_MODEM_HAS_SMS && defined SMS_TARGET + res = modem.sendSMS(SMS_TARGET, String("Hello from ") + imei); + DBG("SMS:", res ? "OK" : "fail"); - float temp = modem.getTemperature(); - DBG("Chip temperature:", temp); + // This is only supported on SIMxxx series + res = modem.sendSMS_UTF8_begin(SMS_TARGET); + if (res) { + auto stream = modem.sendSMS_UTF8_stream(); + stream.print(F("Привіііт! Print number: ")); + stream.print(595); + res = modem.sendSMS_UTF8_end(); + } + DBG("UTF8 SMS:", res ? "OK" : "fail"); + +#endif + +#if TINY_GSM_TEST_GSM_LOCATION && defined TINY_GSM_MODEM_HAS_GSM_LOCATION + float lat = 0; + float lon = 0; + float accuracy = 0; + int year = 0; + int month = 0; + int day = 0; + int hour = 0; + int min = 0; + int sec = 0; + for (int8_t i = 15; i; i--) { + DBG("Requesting current GSM location"); + if (modem.getGsmLocation(&lat, &lon, &accuracy, &year, &month, &day, &hour, + &min, &sec)) { + DBG("Latitude:", String(lat, 8), "\tLongitude:", String(lon, 8)); + DBG("Accuracy:", accuracy); + DBG("Year:", year, "\tMonth:", month, "\tDay:", day); + DBG("Hour:", hour, "\tMinute:", min, "\tSecond:", sec); + break; + } else { + DBG("Couldn't get GSM location, retrying in 15s."); + delay(15000L); + } + } + DBG("Retrieving GSM location again as a string"); + String location = modem.getGsmLocation(); + DBG("GSM Based Location String:", location); +#endif + +#if TINY_GSM_TEST_GPS && defined TINY_GSM_MODEM_HAS_GPS + DBG("Enabling GPS/GNSS/GLONASS and waiting 15s for warm-up"); + modem.enableGPS(); + delay(15000L); + float lat2 = 0; + float lon2 = 0; + float speed2 = 0; + float alt2 = 0; + int vsat2 = 0; + int usat2 = 0; + float accuracy2 = 0; + int year2 = 0; + int month2 = 0; + int day2 = 0; + int hour2 = 0; + int min2 = 0; + int sec2 = 0; + for (int8_t i = 15; i; i--) { + DBG("Requesting current GPS/GNSS/GLONASS location"); + if (modem.getGPS(&lat2, &lon2, &speed2, &alt2, &vsat2, &usat2, &accuracy2, + &year2, &month2, &day2, &hour2, &min2, &sec2)) { + DBG("Latitude:", String(lat2, 8), "\tLongitude:", String(lon2, 8)); + DBG("Speed:", speed2, "\tAltitude:", alt2); + DBG("Visible Satellites:", vsat2, "\tUsed Satellites:", usat2); + DBG("Accuracy:", accuracy2); + DBG("Year:", year2, "\tMonth:", month2, "\tDay:", day2); + DBG("Hour:", hour2, "\tMinute:", min2, "\tSecond:", sec2); + break; + } else { + DBG("Couldn't get GPS/GNSS/GLONASS location, retrying in 15s."); + delay(15000L); + } + } + DBG("Retrieving GPS/GNSS/GLONASS location again as a string"); + String gps_raw = modem.getGPSraw(); + DBG("GPS/GNSS Based Location String:", gps_raw); + DBG("Disabling GPS"); + modem.disableGPS(); +#endif + +#if TINY_GSM_TEST_TIME && defined TINY_GSM_MODEM_HAS_TIME + int year3 = 0; + int month3 = 0; + int day3 = 0; + int hour3 = 0; + int min3 = 0; + int sec3 = 0; + float timezone = 0; + for (int8_t i = 5; i; i--) { + DBG("Requesting current network time"); + if (modem.getNetworkTime(&year3, &month3, &day3, &hour3, &min3, &sec3, + &timezone)) { + DBG("Year:", year3, "\tMonth:", month3, "\tDay:", day3); + DBG("Hour:", hour3, "\tMinute:", min3, "\tSecond:", sec3); + DBG("Timezone:", timezone); + break; + } else { + DBG("Couldn't get network time, retrying in 15s."); + delay(15000L); + } + } + DBG("Retrieving time again as a string"); + String time = modem.getGSMDateTime(DATE_FULL); + DBG("Current Network Time:", time); #endif #if TINY_GSM_TEST_GPRS modem.gprsDisconnect(); + delay(5000L); if (!modem.isGprsConnected()) { DBG("GPRS disconnected"); } else { @@ -295,6 +433,21 @@ void loop() { DBG("WiFi disconnected"); #endif +#if TINY_GSM_TEST_BATTERY && defined TINY_GSM_MODEM_HAS_BATTERY + uint8_t chargeState = -99; + int8_t percent = -99; + uint16_t milliVolts = -9999; + modem.getBattStats(chargeState, percent, milliVolts); + DBG("Battery charge state:", chargeState); + DBG("Battery charge 'percent':", percent); + DBG("Battery voltage:", milliVolts / 1000.0F); +#endif + +#if TINY_GSM_TEST_TEMPERATURE && defined TINY_GSM_MODEM_HAS_TEMPERATURE + float temp = modem.getTemperature(); + DBG("Chip temperature:", temp); +#endif + #if TINY_GSM_POWERDOWN // Try to power-off (modem may decide to restart automatically) // To turn off modem completely, please use Reset/Enable pins @@ -302,6 +455,8 @@ void loop() { DBG("Poweroff."); #endif + DBG("End of tests."); + // Do nothing forevermore while (true) { modem.maintain(); 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..ec1e5b5 100644 --- a/examples/FileDownload/FileDownload.ino +++ b/examples/FileDownload/FileDownload.ino @@ -49,17 +49,19 @@ // Chips without internal buffering (A6/A7, ESP8266, M590) // need enough space in the buffer for the entire response // else data will be lost (and the http library will fail). +#if !defined(TINY_GSM_RX_BUFFER) #define TINY_GSM_RX_BUFFER 1024 +#endif // 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 +219,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 +248,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 +303,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..98d222f 100644 --- a/examples/HttpClient/HttpClient.ino +++ b/examples/HttpClient/HttpClient.ino @@ -54,21 +54,23 @@ // Chips without internal buffering (A6/A7, ESP8266, M590) // need enough space in the buffer for the entire response // else data will be lost (and the http library will fail). +#if !defined(TINY_GSM_RX_BUFFER) #define TINY_GSM_RX_BUFFER 650 +#endif // 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..ace7da7 100644 --- a/examples/HttpsClient/HttpsClient.ino +++ b/examples/HttpsClient/HttpsClient.ino @@ -46,21 +46,23 @@ // Chips without internal buffering (A6/A7, ESP8266, M590) // need enough space in the buffer for the entire response // else data will be lost (and the http library will fail). +#if !defined(TINY_GSM_RX_BUFFER) #define TINY_GSM_RX_BUFFER 650 +#endif // 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 @@ -143,11 +145,6 @@ void setup() { modem.simUnlock(GSM_PIN); } #endif - - if (!modem.hasSSL()) { - SerialMon.println(F("SSL is not supported by this modem")); - while(true) { delay(1000); } - } } void loop() { 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..f686fa2 100644 --- a/examples/WebClient/WebClient.ino +++ b/examples/WebClient/WebClient.ino @@ -44,10 +44,12 @@ // Chips without internal buffering (A6/A7, ESP8266, M590) // need enough space in the buffer for the entire response // else data will be lost (and the http library will fail). +#if !defined(TINY_GSM_RX_BUFFER) #define TINY_GSM_RX_BUFFER 650 +#endif // 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 +59,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 +212,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..59d3dce 100644 --- a/examples/more/Hologram_Dash/Hologram_Dash.ino +++ b/examples/more/Hologram_Dash/Hologram_Dash.ino @@ -12,15 +12,17 @@ #define TINY_GSM_MODEM_UBLOX // Increase RX buffer if needed -//#define TINY_GSM_RX_BUFFER 512 +#if !defined(TINY_GSM_RX_BUFFER) +#define TINY_GSM_RX_BUFFER 512 +#endif #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 +110,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..e1522d0 100644 --- a/examples/more/Industruino/Industruino.ino +++ b/examples/more/Industruino/Industruino.ino @@ -18,16 +18,18 @@ #define TINY_GSM_MODEM_SIM800 // Increase RX buffer if needed -//#define TINY_GSM_RX_BUFFER 512 +#if !defined(TINY_GSM_RX_BUFFER) +#define TINY_GSM_RX_BUFFER 512 +#endif #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/library.json b/library.json index 4fec615..b529261 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TinyGSM", - "version": "0.9.17", + "version": "0.10.0", "description": "A small Arduino library for GPRS modules, that just works. Includes examples for Blynk, MQTT, File Download, and Web Client. Supports many GSM, LTE, and WiFi modules with AT command interfaces.", "keywords": "GSM, AT commands, AT, SIM800, SIM900, A6, A7, M590, ESP8266, SIM7000, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900A, SIM900D, SIM908, SIM968, M95, MC60, MC60E, BG96, ublox, Quectel, SIMCOM, AI Thinker, LTE, LTE-M", "authors": diff --git a/library.properties b/library.properties index 549761f..fcfd3fb 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TinyGSM -version=0.9.17 +version=0.10.0 author=Volodymyr Shymanskyy maintainer=Volodymyr Shymanskyy sentence=A small Arduino library for GPRS modules, that just works. diff --git a/src/TinyGsmBattery.tpp b/src/TinyGsmBattery.tpp new file mode 100644 index 0000000..36596e7 --- /dev/null +++ b/src/TinyGsmBattery.tpp @@ -0,0 +1,98 @@ +/** + * @file TinyGsmBattery.tpp + * @author Volodymyr Shymanskyy + * @license LGPL-3.0 + * @copyright Copyright (c) 2016 Volodymyr Shymanskyy + * @date Nov 2016 + */ + +#ifndef SRC_TINYGSMBATTERY_H_ +#define SRC_TINYGSMBATTERY_H_ + +#include "TinyGsmCommon.h" + +#define TINY_GSM_MODEM_HAS_BATTERY + +template +class TinyGsmBattery { + public: + /* + * Battery 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); + } + + /* + * CRTP Helper + */ + protected: + inline const modemType& thisModem() const { + return static_cast(*this); + } + inline modemType& thisModem() { + return static_cast(*this); + } + + /* + * Battery 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().streamGetIntBefore(','); + // 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 + int8_t res = thisModem().streamGetIntBefore(','); + // 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 + int8_t res = thisModem().streamGetIntBefore(','); + // 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().streamGetIntBefore(','); + percent = thisModem().streamGetIntBefore(','); + milliVolts = thisModem().streamGetIntBefore('\n'); + // Wait for final OK + thisModem().waitResponse(); + return true; + } +}; + +#endif // SRC_TINYGSMBATTERY_H_ diff --git a/src/TinyGsmCalling.tpp b/src/TinyGsmCalling.tpp new file mode 100644 index 0000000..413515a --- /dev/null +++ b/src/TinyGsmCalling.tpp @@ -0,0 +1,90 @@ +/** + * @file TinyGsmCalling.tpp + * @author Volodymyr Shymanskyy + * @license LGPL-3.0 + * @copyright Copyright (c) 2016 Volodymyr Shymanskyy + * @date Nov 2016 + */ + +#ifndef SRC_TINYGSMCALLING_H_ +#define SRC_TINYGSMCALLING_H_ + +#include "TinyGsmCommon.h" + +#define TINY_GSM_MODEM_HAS_CALLING + +template +class TinyGsmCalling { + public: + /* + * 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); + } + + /* + * CRTP Helper + */ + protected: + inline const modemType& thisModem() const { + return static_cast(*this); + } + inline modemType& thisModem() { + return static_cast(*this); + } + + /* + * 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 { + thisModem().sendAT(GF("D"), number, ";"); + } + int8_t 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; + } + } + + 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; + } +}; + +#endif // SRC_TINYGSMCALLING_H_ diff --git a/src/TinyGsmClient.h b/src/TinyGsmClient.h index 0b25d28..cec5d68 100644 --- a/src/TinyGsmClient.h +++ b/src/TinyGsmClient.h @@ -6,129 +6,104 @@ * @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; +#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; +#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; +#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! +#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) +#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; +#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; +#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; +#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; +#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; +#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; +#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; +#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" +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 +#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_WIFI +#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; - +#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 +#endif // SRC_TINYGSMCLIENT_H_ diff --git a/src/TinyGsmClientA6.h b/src/TinyGsmClientA6.h index 15a19f1..c1c6236 100644 --- a/src/TinyGsmClientA6.h +++ b/src/TinyGsmClientA6.h @@ -6,31 +6,32 @@ * @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 +#define TINY_GSM_NO_MODEM_BUFFER -#include +#include "TinyGsmBattery.tpp" +#include "TinyGsmCalling.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmModem.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmTime.tpp" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +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, -}; +#if defined TINY_GSM_DEBUG +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; +#endif enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,111 +40,104 @@ enum RegStatus { REG_UNKNOWN = 4, }; +class TinyGsmA6 : public TinyGsmModem, + public TinyGsmGPRS, + public TinyGsmTCP, + public TinyGsmCalling, + public TinyGsmSMS, + public TinyGsmTime, + public TinyGsmBattery { + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmCalling; + friend class TinyGsmSMS; + friend class TinyGsmTime; + friend class TinyGsmBattery; -class TinyGsmA6 -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmA6; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} - - GsmClient(TinyGsmA6& modem) { - init(&modem); - } - - 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, uint8_t = 0) { + 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 = 0) { + 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; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES -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; - -private: - TinyGsmA6* at; - uint8_t mux; - bool sock_connected; - RxFifo rx; -}; - + // Doesn't support SSL -public: - - TinyGsmA6(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmA6(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } - virtual ~TinyGsmA6() {} - /* * 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("&FZE0")); // Factory + Reset + Echo Off - if (waitResponse() != 1) { - return false; - } + // sendAT(GF("&FZ")); // Factory + Reset + // waitResponse(); + + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } #ifdef TINY_GSM_DEBUG sendAT(GF("+CMEE=2")); // turn on verbose error codes @@ -151,282 +145,179 @@ 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()); - int ret = getSimStatus(); + SimStatus ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); - } - // 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() { - return false; - } - - bool hasWifi() { - return false; - } - - bool hasGPRS() { - 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; - - /* - * SIM card functions - */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - - String getSimCCID() { - sendAT(GF("+CCID")); - 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; - } + bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE; /* * 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() + String getLocalIPImpl() { + sendAT(GF("+CIFSR")); + String res; + if (waitResponse(10000L, res) != 1) { return ""; } + res.replace(GSM_NL "OK" GSM_NL, ""); + res.replace(GSM_NL, ""); + res.trim(); + 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(); 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 + * SIM card functions */ - - String getLocalIP() { - sendAT(GF("+CIFSR")); - String res; - if (waitResponse(10000L, res) != 1) { - return ""; - } - res.replace(GSM_NL "OK" GSM_NL, ""); - res.replace(GSM_NL, ""); + protected: + String getSimCCIDImpl() { + sendAT(GF("+CCID")); + if (waitResponse(GF(GSM_NL "+SCID: SIM Card ID:")) != 1) { return ""; } + String res = stream.readStringUntil('\n'); + waitResponse(); 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")); + int8_t 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)); + int8_t 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, uint8_t 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) { @@ -439,7 +330,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() /* * Audio functions */ - + public: bool audioSetHeadphones() { sendAT(GF("+SNFS=0")); return waitResponse() == 1; @@ -458,23 +349,19 @@ 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 ""; - } - stream.readStringUntil('"'); + if (waitResponse(10000L) != 1) { return ""; } + if (waitResponse(GF(GSM_NL "+CUSD:")) != 1) { return ""; } + streamSkipUntil('"'); String hex = stream.readStringUntil('"'); - stream.readStringUntil(','); - int dcs = stream.readStringUntil('\n').toInt(); + streamSkipUntil(','); + int8_t dcs = streamGetIntBefore('\n'); if (dcs == 15) { return TinyGsmDecodeHex7bit(hex); @@ -485,96 +372,61 @@ 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 + * Time functions */ - - String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; + protected: + // Can follow the standard CCLK function in the template + // Note - the clock probably has to be set manaually first /* - * Battery & temperature functions + * Battery functions */ + protected: + uint16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE; - uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE; - - int8_t getBattPercent() { + // Needs a '?' after CBC, unlike most + 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 - 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(); + int8_t res = streamGetIntBefore('\n'); // Wait for final OK waitResponse(); return res; } - bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) { + // Needs a '?' after CBC, unlike most + bool getBattStatsImpl(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('\n').toInt(); - milliVolts = 0; + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return false; } + chargeState = streamGetIntBefore(','); + percent = streamGetIntBefore('\n'); + milliVolts = 0; // Wait for final OK waitResponse(); return true; } - - float getTemperature() 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; - } - int newMux = stream.readStringUntil('\n').toInt(); - - 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; - } + 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; } + int8_t newMux = streamGetIntBefore('\n'); + + int8_t 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; } *mux = newMux; return (1 == rsp); @@ -582,37 +434,35 @@ 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; } 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? + int8_t 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! + int8_t waitResponse(uint32_t timeout_ms, String& data, + GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -620,15 +470,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(); + uint8_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; + int8_t a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -636,6 +486,11 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 2; goto finish; } else if (r3 && data.endsWith(r3)) { +#if defined TINY_GSM_DEBUG + if (r3 == GFP(GSM_CME_ERROR)) { + streamSkipUntil('\n'); // Read out the error + } +#endif index = 3; goto finish; } else if (r4 && data.endsWith(r4)) { @@ -645,23 +500,23 @@ 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 len_orig = len; + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore(','); + int16_t 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:"))) { - int mux = stream.readStringUntil('\n').toInt(); + int8_t mux = streamGetIntBefore('\n'); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT) { sockets[mux]->sock_connected = false; } @@ -670,38 +525,44 @@ 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) - { + int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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) - { + int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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 c9f4510..6b02890 100644 --- a/src/TinyGsmClientBG96.h +++ b/src/TinyGsmClientBG96.h @@ -6,31 +6,34 @@ * @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 +#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE + +#include "TinyGsmBattery.tpp" +#include "TinyGsmCalling.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmGPS.tpp" +#include "TinyGsmModem.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmTemperature.tpp" +#include "TinyGsmTime.tpp" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +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, -}; +#if defined TINY_GSM_DEBUG +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; +#endif enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,528 +42,491 @@ enum RegStatus { REG_UNKNOWN = 4, }; +class TinyGsmBG96 : public TinyGsmModem, + public TinyGsmGPRS, + public TinyGsmTCP, + public TinyGsmCalling, + public TinyGsmSMS, + public TinyGsmTime, + public TinyGsmGPS, + public TinyGsmBattery, + public TinyGsmTemperature { + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmCalling; + friend class TinyGsmSMS; + friend class TinyGsmTime; + friend class TinyGsmGPS; + friend class TinyGsmBattery; + friend class TinyGsmTemperature; -class TinyGsmBG96 -{ - -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); - } - - virtual ~GsmClient(){} + explicit GsmClientBG96(TinyGsmBG96& modem, uint8_t mux = 0) { + init(&modem, mux); + } - 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 = 0) { + this->at = modem; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; + + if (mux < TINY_GSM_MUX_COUNT) { + this->mux = mux; + } else { + this->mux = (mux % TINY_GSM_MUX_COUNT); + } + at->sockets[this->mux] = this; - 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; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES -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 = 0) + : public GsmClient(modem, mux) + {} -TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + public: + int connect(const char* host, uint16_t port, int timeout_s) override { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES + }; + */ /* - * 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)); } - virtual ~TinyGsmBG96() {} /* * 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("&FZE0")); // Factory + Reset + 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()); - int ret = getSimStatus(); + // Disable time and time zone URC's + sendAT(GF("+CTZR=0")); + if (waitResponse(10000L) != 1) { return false; } + + // Enable automatic time zone update + sendAT(GF("+CTZU=1")); + if (waitResponse(10000L) != 1) { return false; } + + SimStatus ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); - } - // 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 hasWifi() { - return false; - } - - bool hasGPRS() { - 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; - } - delay(3000); + if (waitResponse(10000L, GF("OK")) != 1) { return false; } + waitResponse(10000L, GF("APP 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; - } - - /* - * SIM card functions - */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - - String getSimCCID() { - sendAT(GF("+QCCID")); - 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; + // 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; } -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 + * SIM card functions */ - - String getLocalIP() { - sendAT(GF("+QILOCIP")); - stream.readStringUntil('\n'); + protected: + String getSimCCIDImpl() { + sendAT(GF("+QCCID")); + if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); - if (waitResponse() != 1) { - return ""; - } + waitResponse(); + 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; - } - - // 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 */ + protected: + // Follows all messaging functions per template - String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED; + /* + * GSM Location functions + */ + protected: + // NOTE: As of application firmware version 01.016.01.016 triangulated + // locations can be obtained via the QuecLocator service and accompanying AT + // commands. As this is a separate paid service which I do not have access + // to, I am not implementing it here. - 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; + /* + * GPS/GNSS/GLONASS location functions + */ + protected: + // enable GPS + bool enableGPSImpl() { + sendAT(GF("+QGPS=1")); + if (waitResponse() != 1) { return false; } + return true; } - bool sendSMS_UTF16(const String& number, const void* text, size_t len) { - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("+CSMP=17,167,0,8")); + bool disableGPSImpl() { + sendAT(GF("+QGPSEND")); + if (waitResponse() != 1) { return false; } + return true; + } + + // get the RAW GPS output + String getGPSrawImpl() { + sendAT(GF("+QGPSLOC=2")); + if (waitResponse(10000L, GF(GSM_NL "+QGPSLOC:")) != 1) { return ""; } + String res = stream.readStringUntil('\n'); waitResponse(); + res.trim(); + return res; + } - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { + // get GPS informations + bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0, + int* vsat = 0, int* usat = 0, float* accuracy = 0, + int* year = 0, int* month = 0, int* day = 0, int* hour = 0, + int* minute = 0, int* second = 0) { + sendAT(GF("+QGPSLOC=2")); + if (waitResponse(10000L, GF(GSM_NL "+QGPSLOC:")) != 1) { + // NOTE: Will return an error if the position isn't fixed return false; } - uint16_t* t = (uint16_t*)text; - for (size_t i=0; i> 8; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - c = t[i] & 0xFF; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - } - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; + // init variables + float ilat = 0; + float ilon = 0; + float ispeed = 0; + float ialt = 0; + int iusat = 0; + float iaccuracy = 0; + int iyear = 0; + int imonth = 0; + int iday = 0; + int ihour = 0; + int imin = 0; + float secondWithSS = 0; + + // UTC date & Time + ihour = streamGetIntLength(2); // Two digit hour + imin = streamGetIntLength(2); // Two digit minute + secondWithSS = streamGetFloatBefore(','); // 6 digit second with subseconds + + ilat = streamGetFloatBefore(','); // Latitude + ilon = streamGetFloatBefore(','); // Longitude + iaccuracy = streamGetFloatBefore(','); // Horizontal precision + ialt = streamGetFloatBefore(','); // Altitude from sea level + streamSkipUntil(','); // GNSS positioning mode + streamSkipUntil(','); // Course Over Ground based on true north + streamSkipUntil(','); // Speed Over Ground in Km/h + ispeed = streamGetFloatBefore(','); // Speed Over Ground in knots + + iday = streamGetIntLength(2); // Two digit day + imonth = streamGetIntLength(2); // Two digit month + iyear = streamGetIntBefore(','); // Two digit year + + iusat = streamGetIntBefore(','); // Number of satellites, + streamSkipUntil('\n'); // The error code of the operation. If it is not + // 0, it is the type of error. + + // Set pointers + if (lat != NULL) *lat = ilat; + if (lon != NULL) *lon = ilon; + if (speed != NULL) *speed = ispeed; + if (alt != NULL) *alt = ialt; + if (vsat != NULL) *vsat = 0; + if (usat != NULL) *usat = iusat; + if (accuracy != NULL) *accuracy = iaccuracy; + if (iyear < 2000) iyear += 2000; + if (year != NULL) *year = iyear; + if (month != NULL) *month = imonth; + if (day != NULL) *day = iday; + if (hour != NULL) *hour = ihour; + if (minute != NULL) *minute = imin; + if (second != NULL) *second = static_cast(secondWithSS); + + waitResponse(); // Final OK + return true; } - /* - * Location functions + * Time functions */ - - String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; - - /* - * 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; + protected: + String getGSMDateTimeImpl(TinyGSMDateTimeFormat format) { + sendAT(GF("+QLTS=2")); + if (waitResponse(2000L, GF("+QLTS: \"")) != 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; } - 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(); + waitResponse(); // Ends with OK 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; + // The BG96 returns UTC time instead of local time as other modules do in + // response to CCLK, so we're using QLTS where we can specifically request + // local time. + bool getNetworkTimeImpl(int* year, int* month, int* day, int* hour, + int* minute, int* second, float* timezone) { + sendAT(GF("+QLTS=2")); + if (waitResponse(2000L, GF("+QLTS: \"")) != 1) { return false; } + + int iyear = 0; + int imonth = 0; + int iday = 0; + int ihour = 0; + int imin = 0; + int isec = 0; + int itimezone = 0; + + // Date & Time + iyear = streamGetIntBefore('/'); + imonth = streamGetIntBefore('/'); + iday = streamGetIntBefore(','); + ihour = streamGetIntBefore(':'); + imin = streamGetIntBefore(':'); + isec = streamGetIntLength(2); + char tzSign = stream.read(); + itimezone = streamGetIntBefore(','); + if (tzSign == '-') { itimezone = itimezone * -1; } + streamSkipUntil('\n'); // DST flag + + // Set pointers + if (iyear < 2000) iyear += 2000; + if (year != NULL) *year = iyear; + if (month != NULL) *month = imonth; + if (day != NULL) *day = iday; + if (hour != NULL) *hour = ihour; + if (minute != NULL) *minute = imin; + if (second != NULL) *second = isec; + if (timezone != NULL) *timezone = static_cast(itimezone) / 4.0; + + // Final OK + waitResponse(); // Ends with OK + return true; } - 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; - } + /* + * Battery functions + */ + protected: + // Can follow CBC as in the template - 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(); + /* + * Temperature functions + */ + protected: + // get temperature in degree celsius + uint16_t getTemperatureImpl() { + sendAT(GF("+QTEMP")); + if (waitResponse(GF(GSM_NL "+QTEMP:")) != 1) { return 0; } + // return temperature in C + uint16_t res = + streamGetIntBefore(','); // read PMIC (primary ic) temperature + streamSkipUntil(','); // skip XO temperature ?? + streamSkipUntil('\n'); // skip PA temperature ?? // Wait for final OK waitResponse(); - return true; + return res; } - float getTemperature() 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 = 150) { + if (ssl) { DBG("SSL not yet supported on this module!"); } + + 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 (streamGetIntBefore(',') != mux) { return false; } // Read status - rsp = stream.readStringUntil('\n').toInt(); - - return (0 == rsp); + return (0 == streamGetIntBefore('\n')); } 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; - } - int len = stream.readStringUntil('\n').toInt(); + if (waitResponse(GF("+QIRD:")) != 1) { return 0; } + int16_t len = streamGetIntBefore('\n'); - for (int i=0; isock_available = modemGetAvailable(mux); return len; } @@ -569,33 +535,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 - result = stream.readStringUntil('\n').toInt(); - if (result) { - DBG("### DATA AVAILABLE:", result, "on", mux); - } + streamSkipUntil(','); // Skip total received + streamSkipUntil(','); // Skip have read + result = streamGetIntBefore('\n'); + 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:"))) - 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 + int8_t res = streamGetIntBefore(','); // socket state waitResponse(); @@ -603,19 +564,20 @@ 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! + int8_t waitResponse(uint32_t timeout_ms, String& data, + GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -623,15 +585,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(); + uint8_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; + int8_t a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -639,6 +601,11 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 2; goto finish; } else if (r3 && data.endsWith(r3)) { +#if defined TINY_GSM_DEBUG + if (r3 == GFP(GSM_CME_ERROR)) { + streamSkipUntil('\n'); // Read out the error + } +#endif index = 3; goto finish; } else if (r4 && data.endsWith(r4)) { @@ -648,60 +615,66 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 5; goto finish; } else if (data.endsWith(GF(GSM_NL "+QIURC:"))) { - stream.readStringUntil('\"'); + streamSkipUntil('\"'); String urc = stream.readStringUntil('\"'); - stream.readStringUntil(','); + streamSkipUntil(','); if (urc == "recv") { - int mux = stream.readStringUntil('\n').toInt(); + int8_t mux = streamGetIntBefore('\n'); DBG("### URC RECV:", mux); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->got_data = true; } } else if (urc == "closed") { - int mux = stream.readStringUntil('\n').toInt(); + int8_t mux = streamGetIntBefore('\n'); DBG("### URC CLOSE:", mux); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } } else { - stream.readStringUntil('\n'); + streamSkipUntil('\n'); } 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) - { + int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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) - { + int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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 b993805..fc57cf7 100644 --- a/src/TinyGsmClientESP8266.h +++ b/src/TinyGsmClientESP8266.h @@ -6,24 +6,24 @@ * @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 +#define TINY_GSM_NO_MODEM_BUFFER -#include +#include "TinyGsmModem.tpp" +#include "TinyGsmSSL.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmWifi.tpp" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" 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 unsigned TINY_GSM_TCP_KEEP_ALIVE = 120; +static uint8_t TINY_GSM_TCP_KEEP_ALIVE = 120; // status of ESP8266 station interface // 2 : ESP8266 station connected to an AP and has obtained IP @@ -38,271 +38,227 @@ enum RegStatus { REG_UNKNOWN = 6, }; +class TinyGsmESP8266 : public TinyGsmModem, + public TinyGsmWifi, + public TinyGsmTCP, + public TinyGsmSSL { + friend class TinyGsmModem; + friend class TinyGsmWifi; + friend class TinyGsmTCP; + friend class TinyGsmSSL; -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); - } - - 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 = 0) { + init(&modem, mux); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + bool init(TinyGsmESP8266* modem, uint8_t mux = 0) { + this->at = modem; + 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(); - } + if (mux < TINY_GSM_MUX_COUNT) { + this->mux = mux; + } else { + this->mux = (mux % TINY_GSM_MUX_COUNT); + } + at->sockets[this->mux] = this; - virtual void stop() { stop(5000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + virtual int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES -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; - -private: - TinyGsmESP8266* at; - uint8_t mux; - bool sock_connected; - RxFifo rx; -}; - - -class GsmClientSecure : public GsmClient -{ -public: - GsmClientSecure() {} + explicit GsmClientSecureESP8266(TinyGsmESP8266& modem, uint8_t mux = 0) + : GsmClientESP8266(modem, mux) {} - 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) override { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES + }; - TinyGsmESP8266(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmESP8266(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } - virtual ~TinyGsmESP8266() {} - /* * 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() { - return true; - } - - bool hasWifi() { - return true; - } - - bool hasGPRS() { - 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 + * Generic network functions */ - + public: RegStatus getRegistrationStatus() { sendAT(GF("+CIPSTATUS")); if (waitResponse(3000, GF("STATUS:")) != 1) return REG_UNKNOWN; - int status = - waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5")); + int8_t status = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), + GF("5")); waitResponse(); // Returns an OK after the status return (RegStatus)status; } - /* - * Generic network functions - */ - - int16_t getSignalQuality() { + protected: + int8_t getSignalQualityImpl() { sendAT(GF("+CWJAP_CUR?")); - int res1 = waitResponse(GF("No AP"), GF("+CWJAP_CUR:")); + int8_t 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 - int res2 = stream.parseInt(); // Read RSSI - waitResponse(); // Returns an OK after the value + streamSkipUntil(','); // Skip SSID + streamSkipUntil(','); // Skip BSSID/MAC address + streamSkipUntil(','); // Skip Chanel number + int8_t res2 = stream.parseInt(); // Read RSSI + 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() + String getLocalIPImpl() { + sendAT(GF("+CIPSTA_CUR?")); + int8_t res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:")); + if (res1 != 2) { return ""; } + String res2 = stream.readStringUntil('"'); + waitResponse(); + return res2; + } /* * 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; @@ -311,86 +267,49 @@ TINY_GSM_MODEM_MAINTAIN_LISTEN() return true; } - bool networkDisconnect() { + bool networkDisconnectImpl() { sendAT(GF("+CWQAP")); bool retVal = waitResponse(10000L) == 1; waitResponse(GF("WIFI DISCONNECT")); return retVal; } - /* - * IP Address functions - */ - - String getLocalIP() { - sendAT(GF("+CIPSTA_CUR??")); - int res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:")); - if (res1 != 2) { - return ""; - } - String res2 = stream.readStringUntil('"'); - waitResponse(); - return res2; - } - - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - - /* - * Battery & temperature functions - */ - - // 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; - /* * Client related functions */ - -protected: - + 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("ALREADY CONNECT")); - // if (rsp == 3) waitResponse(); // May return "ERROR" after the "ALREADY CONNECT" + GF("\",\""), host, GF("\","), port, GF(","), + TINY_GSM_TCP_KEEP_ALIVE); + // TODO(?): Check mux + int8_t rsp = waitResponse(timeout_ms, GFP(GSM_OK), GFP(GSM_ERROR), + GF("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; } bool modemGetConnected(uint8_t mux) { sendAT(GF("+CIPSTATUS")); - if (waitResponse(3000, GF("STATUS:")) != 1) return false; - int status = - waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5")); + if (waitResponse(3000, GF("STATUS:")) != 1) { return false; } + int8_t status = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), + GF("5")); if (status != 3) { // if the status is anything but 3, there are no connections open waitResponse(); // Returns an OK after the status @@ -401,14 +320,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 + int8_t returned_mux = streamGetIntBefore(','); + 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; } @@ -420,19 +340,15 @@ 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! + int8_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) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -440,15 +356,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(); + uint8_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; + int8_t a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -465,25 +381,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 len_orig = len; + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore(':'); + int16_t 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); + // 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 coma = data.indexOf(',', muxStart); - int mux = data.substring(muxStart, coma).toInt(); + int8_t muxStart = + TinyGsmMax(0, data.lastIndexOf(GSM_NL, data.length() - 8)); + int8_t coma = data.indexOf(',', muxStart); + int8_t mux = data.substring(muxStart, coma).toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } @@ -492,38 +410,34 @@ 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) - { + int8_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) { 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) - { + int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL, + 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 f312e59..7da804a 100644 --- a/src/TinyGsmClientM590.h +++ b/src/TinyGsmClientM590.h @@ -6,31 +6,30 @@ * @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 +#define TINY_GSM_NO_MODEM_BUFFER -#include +#include "TinyGsmGPRS.tpp" +#include "TinyGsmModem.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmTime.tpp" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +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, -}; +#if defined TINY_GSM_DEBUG +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; +#endif enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 3, REG_DENIED = 2, @@ -39,109 +38,92 @@ enum RegStatus { REG_UNKNOWN = 4, }; +class TinyGsmM590 : public TinyGsmModem, + public TinyGsmGPRS, + public TinyGsmTCP, + public TinyGsmSMS, + public TinyGsmTime { + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmSMS; + friend class TinyGsmTime; -class TinyGsmM590 -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmM590; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} - - 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 = 0) { + init(&modem, mux); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + bool init(TinyGsmM590* modem, uint8_t mux = 0) { + this->at = modem; + 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(); - } + if (mux < TINY_GSM_MUX_COUNT) { + this->mux = mux; + } else { + this->mux = (mux % TINY_GSM_MUX_COUNT); + } + at->sockets[this->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; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES -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)); } - virtual ~TinyGsmM590() {} - /* * 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("&FZE0")); // Factory + Reset + 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 @@ -152,136 +134,90 @@ public: DBG(GF("### Modem:"), getModemName()); - int ret = getSimStatus(); + SimStatus ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); - } - // 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() { - return false; - } - - bool hasWifi() { - return false; - } - - bool hasGPRS() { - 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; } - /* - * 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() - /* * 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() + String getLocalIPImpl() { + sendAT(GF("+XIIC?")); + if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { return ""; } + streamSkipUntil(','); + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + 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(); sendAT(GF("+XISP=0")); @@ -291,162 +227,80 @@ 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; - } - int res = stream.readStringUntil(',').toInt(); + if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { return false; } + int8_t res = streamGetIntBefore(','); waitResponse(); return res == 1; } /* - * IP Address functions - */ - - String getLocalIP() { - sendAT(GF("+XIIC?")); - if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { - return ""; - } - stream.readStringUntil(','); - String res = stream.readStringUntil('\n'); - waitResponse(); - res.trim(); - return res; - } - - IPAddress localIP() { - return TinyGsmIpFromString(getLocalIP()); - } - - /* - * Phone Call functions + * SIM card 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: + // Able to follow all SIM card functions as inherited from the template /* * 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 + * Time functions */ - - String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; - - /* - * 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: + // Can follow the standard CCLK function in the template /* * 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)); + int8_t 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) { @@ -460,50 +314,46 @@ 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; - } - stream.readStringUntil('\n'); + if (waitResponse(30000L, GF(GSM_NL "+TCPSEND:")) != 1) { return 0; } + streamSkipUntil('\n'); return len; } bool modemGetConnected(uint8_t mux) { sendAT(GF("+CIPSTATUS="), mux); - int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\"")); + int8_t 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! + int8_t waitResponse(uint32_t timeout_ms, String& data, + GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -511,15 +361,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(); + uint8_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; + int8_t a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -527,6 +377,11 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 2; goto finish; } else if (r3 && data.endsWith(r3)) { +#if defined TINY_GSM_DEBUG + if (r3 == GFP(GSM_CME_ERROR)) { + streamSkipUntil('\n'); // Read out the error + } +#endif index = 3; goto finish; } else if (r4 && data.endsWith(r4)) { @@ -536,24 +391,24 @@ 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 len_orig = len; + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore(','); + int16_t 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:"))) { - int mux = stream.readStringUntil(',').toInt(); - stream.readStringUntil('\n'); + int8_t mux = streamGetIntBefore(','); + streamSkipUntil('\n'); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT) { sockets[mux]->sock_connected = false; } @@ -562,38 +417,44 @@ 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) - { + int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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) - { + int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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 3e108d0..65b740b 100644 --- a/src/TinyGsmClientM95.h +++ b/src/TinyGsmClientM95.h @@ -2,35 +2,38 @@ * @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 +#define TINY_GSM_BUFFER_READ_NO_CHECK -#include +#include "TinyGsmBattery.tpp" +#include "TinyGsmCalling.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmModem.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmTemperature.tpp" +#include "TinyGsmTime.tpp" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +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, -}; +#if defined TINY_GSM_DEBUG +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; +#endif enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,131 +42,130 @@ enum RegStatus { REG_UNKNOWN = 4, }; +class TinyGsmM95 : public TinyGsmModem, + public TinyGsmGPRS, + public TinyGsmTCP, + public TinyGsmCalling, + public TinyGsmSMS, + public TinyGsmTime, + public TinyGsmBattery, + public TinyGsmTemperature { + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmCalling; + friend class TinyGsmSMS; + friend class TinyGsmTime; + friend class TinyGsmBattery; + friend class TinyGsmTemperature; -class TinyGsmM95 -{ - -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 = 0) { + init(&modem, mux); + } - virtual ~GsmClient(){} + bool init(TinyGsmM95* modem, uint8_t mux = 0) { + this->at = modem; + 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; + if (mux < TINY_GSM_MUX_COUNT) { + this->mux = mux; + } else { + this->mux = (mux % TINY_GSM_MUX_COUNT); + } + at->sockets[this->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; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES -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 = 0) + : GsmClient(modem, mux) + {} -TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + public: + int connect(const char* host, uint16_t port, int timeout_s) override { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES + }; + */ /* - * 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)); } - virtual ~TinyGsmM95() {} - /* * 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("&FZE0")); // Factory + Reset + Echo Off - if (waitResponse() != 1) { - return false; - } + // sendAT(GF("&FZ")); // Factory + Reset + // waitResponse(); + + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } #ifdef TINY_GSM_DEBUG sendAT(GF("+CMEE=2")); // turn on verbose error codes @@ -174,62 +176,31 @@ public: DBG(GF("### Modem:"), getModemName()); - int ret = getSimStatus(); + // Enable network time synchronization + sendAT(GF("+QNITZ=1")); + if (waitResponse(10000L) != 1) { return false; } + + SimStatus ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); - } - // 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"; - } - -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() { - return false; - } - - bool hasGPRS() { - 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")); @@ -239,77 +210,37 @@ 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; - } - - bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED; - - /* - * SIM card functions - */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - - String getSimCCID() { - sendAT(GF("+QCCID")); - 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; + // 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; } -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 ) { + void setHostFormat(bool useDottedQuad) { + if (useDottedQuad) { sendAT(GF("+QIDNSIP=0")); } else { sendAT(GF("+QIDNSIP=1")); @@ -317,63 +248,55 @@ TINY_GSM_MODEM_GET_CSQ() waitResponse(); } -TINY_GSM_MODEM_WAIT_FOR_NETWORK() + String getLocalIPImpl() { + sendAT(GF("+QILOCIP")); + streamSkipUntil('\n'); + String res = stream.readStringUntil('\n'); + res.trim(); + 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(); // 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: ,,” + // +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):" @@ -389,7 +312,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) { @@ -406,194 +330,71 @@ 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 + * SIM card functions */ - - String getLocalIP() { - sendAT(GF("+QILOCIP")); - stream.readStringUntil('\n'); + protected: + String getSimCCIDImpl() { + sendAT(GF("+QCCID")); + if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); + waitResponse(); res.trim(); 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; - } - - bool sendSMS_UTF16(const String& number, const void* text, size_t len) { - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("+CSMP=17,167,0,8")); - waitResponse(); - - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - - uint16_t* t = (uint16_t*)text; - for (size_t i=0; i> 8; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - c = t[i] & 0xFF; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - } - 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 + * Time 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: + // Can follow the standard CCLK function in the template /* - * Location functions + * Battery functions */ - - String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE; + // Can follow the battery functions in the template /* - * Battery & temperature functions + * 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 - float temp = stream.readStringUntil('\n').toFloat(); + // milliVolts = streamGetIntBefore(','); + streamSkipUntil(','); // Skip thermistor charge + float temp = streamGetFloatBefore('\n'); // Wait for final OK waitResponse(); return temp; @@ -602,33 +403,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); + int8_t 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; @@ -640,18 +433,18 @@ protected: // streamSkipUntil(','); // Skip total length sent on connection // streamSkipUntil(','); // Skip length already acknowledged by remote // // Make sure the total length un-acknowledged is 0 - // if ( stream.readStringUntil('\n').toInt() == 0 ) { + // if ( streamGetIntBefore('\n') == 0 ) { // allAcknowledged = true; // } // } // } // 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 @@ -660,47 +453,50 @@ protected: sendAT(GF("+QIRD=0,1,"), mux, ',', (uint16_t)size); // If it replies only OK for the write command, it means there is no // received data in the buffer of the connection. - int res = waitResponse(GF("+QIRD:"), GFP(GSM_OK), GFP(GSM_ERROR)); + int8_t res = waitResponse(GF("+QIRD:"), GFP(GSM_OK), GFP(GSM_ERROR)); if (res == 1) { streamSkipUntil(':'); // skip IP address streamSkipUntil(','); // skip port streamSkipUntil(','); // skip connection type (TCP/UDP) // read the real length of the retrieved data - uint16_t len = stream.readStringUntil('\n').toInt(); - // We have no way of knowing in advance how much data will be in the buffer - // so when data is received we always assume the buffer is completely full. - // Chances are, this is not true and there's really not that much there. - // In that case, make sure we make sure we re-set the amount of data available. - if (len < size) { - sockets[mux]->sock_available = len; - } - for (uint16_t i=0; isock_available = 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); + // 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 + int8_t res = streamGetIntBefore(','); // socket state waitResponse(); @@ -708,19 +504,20 @@ 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! + int8_t waitResponse(uint32_t timeout_ms, String& data, + GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -728,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(); + uint8_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; + int8_t a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -744,6 +541,11 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 2; goto finish; } else if (r3 && data.endsWith(r3)) { +#if defined TINY_GSM_DEBUG + if (r3 == GFP(GSM_CME_ERROR)) { + streamSkipUntil('\n'); // Read out the error + } +#endif index = 3; goto finish; } else if (r4 && data.endsWith(r4)) { @@ -755,8 +557,8 @@ TINY_GSM_MODEM_STREAM_UTILITIES() } else if (data.endsWith(GF(GSM_NL "+QIRDI:"))) { streamSkipUntil(','); // Skip the context streamSkipUntil(','); // Skip the role - int mux = stream.readStringUntil('\n').toInt(); - DBG("### Got Data:", mux); + int8_t mux = streamGetIntBefore('\n'); + // DBG("### Got Data:", mux); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { // We have no way of knowing how much data actually came in, so // we set the value to 1500, the maximum possible size. @@ -764,49 +566,59 @@ 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(); + int8_t nl = data.lastIndexOf(GSM_NL, data.length() - 8); + int8_t coma = data.indexOf(',', nl + 2); + int8_t mux = data.substring(nl + 2, coma).toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } data = ""; DBG("### Closed: ", mux); + } else if (data.endsWith(GF("+QNITZ:"))) { + streamSkipUntil('\n'); // URC for time sync + data = ""; + DBG("### Network time updated."); } } } 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) - { + int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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) - { + int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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 6036e9c..45bd290 100644 --- a/src/TinyGsmClientMC60.h +++ b/src/TinyGsmClientMC60.h @@ -9,32 +9,32 @@ * */ -#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 +#define TINY_GSM_BUFFER_READ_NO_CHECK -#include +#include "TinyGsmBattery.tpp" +#include "TinyGsmCalling.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmModem.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmTime.tpp" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +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, -}; +#if defined TINY_GSM_DEBUG +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; +#endif enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -43,306 +43,223 @@ enum RegStatus { REG_UNKNOWN = 4, }; +class TinyGsmMC60 : public TinyGsmModem, + public TinyGsmGPRS, + public TinyGsmTCP, + public TinyGsmCalling, + public TinyGsmSMS, + public TinyGsmTime, + public TinyGsmBattery { + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmCalling; + friend class TinyGsmSMS; + friend class TinyGsmTime; + friend class TinyGsmBattery; -class TinyGsmMC60 -{ - -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 = 0) { + init(&modem, mux); + } - virtual ~GsmClient(){} + bool init(TinyGsmMC60* modem, uint8_t mux = 0) { + this->at = modem; + 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; + if (mux < TINY_GSM_MUX_COUNT) { + this->mux = mux; + } else { + this->mux = (mux % TINY_GSM_MUX_COUNT); + } + at->sockets[this->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; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES -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 = 0) + : GsmClient(modem, mux) + {} -TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() -TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() + public: + int connect(const char* host, uint16_t port, int timeout_s) override { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES + }; + */ /* - * 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)); } - virtual ~TinyGsmMC60() {} - /* * Basic functions */ + protected: + bool initImpl(const char* pin = NULL) { + DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - bool begin(const char* pin = NULL) { - return init(pin); - } + if (!testAT()) { return false; } - bool init(const char* pin = NULL) { - DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + // sendAT(GF("&FZ")); // Factory + Reset + // waitResponse(); - if (!testAT()) { - return false; - } + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } - sendAT(GF("&FZ")); // Factory + Reset +#ifdef TINY_GSM_DEBUG + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes +#endif waitResponse(); - sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } - DBG(GF("### Modem:"), getModemName()); - int ret = getSimStatus(); + // Enable network time synchronization + sendAT(GF("+QNITZ=1")); + if (waitResponse(10000L) != 1) { return false; } + + SimStatus ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); - } - // 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() { - // sendAT(GF("+QIPSSL=?")); - // if (waitResponse(GF(GSM_NL "+CIPSSL:")) != 1) { - // return false; - // } - // return waitResponse() == 1; - // } - - bool hasSSL() { - return false; // TODO: For now - } - - bool hasWifi() { - return false; - } - - bool hasGPRS() { - 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; - } - - 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; ) { - 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")); - waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 5: - case 6: return SIM_ANTITHEFT_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } - } - return SIM_ERROR; + // 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; } -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() + String getLocalIPImpl() { + sendAT(GF("+QILOCIP")); + streamSkipUntil('\n'); + String res = stream.readStringUntil('\n'); + res.trim(); + 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(); // 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(); @@ -352,328 +269,193 @@ 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>” + // +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 + * SIM card functions */ - - String getLocalIP() { - sendAT(GF("+QILOCIP")); - stream.readStringUntil('\n'); - String res = stream.readStringUntil('\n'); - res.trim(); - return res; + 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; + } + int8_t 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 5: + case 6: return SIM_ANTITHEFT_LOCKED; + case 1: return SIM_READY; + default: return SIM_ERROR; + } + } + return SIM_ERROR; } - 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; - } - - bool sendSMS_UTF16(const String& number, const void* text, size_t len) { - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("+CSMP=17,167,0,8")); - waitResponse(); - - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - - uint16_t* t = (uint16_t*)text; - for (size_t i=0; i> 8; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - c = t[i] & 0xFF; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - } - 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; } /* - * Location functions + * Time 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 follow the standard CCLK function in the template /* - * Battery & temperature functions + * Battery 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; + // Can follow battery functions as in the template /* * 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); + int8_t 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 ) { - allAcknowledged = true; - } + if (streamGetIntBefore('\n') == 0) { allAcknowledged = true; } } } waitResponse(5000L); // streamSkipUntil(','); // Skip mux - // return stream.readStringUntil('\n').toInt(); + // return streamGetIntBefore('\n'); - 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. - int res = waitResponse(GF("+QIRD:"), GFP(GSM_OK), GFP(GSM_ERROR)); + int8_t res = waitResponse(GF("+QIRD:"), GFP(GSM_OK), GFP(GSM_ERROR)); if (res == 1) { streamSkipUntil(':'); // skip IP address streamSkipUntil(','); // skip port streamSkipUntil(','); // skip connection type (TCP/UDP) // read the real length of the retrieved data - uint16_t len = stream.readStringUntil('\n').toInt(); + uint16_t len = streamGetIntBefore('\n'); // It's possible that the real length available is less than expected // This is quite likely if the buffer is broken into packets - which may // be different sizes. // If so, make sure we make sure we re-set the amount of data available. - if (len < size) { - sockets[mux]->sock_available = len; - } - for (uint16_t i=0; isock_available = 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(); - DBG("### READ:", len, "from", mux); + 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 + int8_t res = streamGetIntBefore(','); // socket state waitResponse(); @@ -681,19 +463,21 @@ 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! + int8_t waitResponse(uint32_t timeout_ms, String& data, + GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + GsmConstStr r4 = NULL, GsmConstStr r5 = NULL, + GsmConstStr r6 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -702,15 +486,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(); + uint8_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; + int8_t a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -718,6 +502,11 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 2; goto finish; } else if (r3 && data.endsWith(r3)) { +#if defined TINY_GSM_DEBUG + if (r3 == GFP(GSM_CME_ERROR)) { + streamSkipUntil('\n'); // Read out the error + } +#endif index = 3; goto finish; } else if (r4 && data.endsWith(r4)) { @@ -729,65 +518,79 @@ 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 "+QIRDI:"))) { // TODO(?): QIRD? or QIRDI? // +QIRDI: ,,,,,< tlen> streamSkipUntil(','); // Skip the context streamSkipUntil(','); // Skip the role // read the connection id - int mux = stream.readStringUntil(',').toInt(); + int8_t mux = streamGetIntBefore(','); // read the number of packets in the buffer - int num_packets = stream.readStringUntil(',').toInt(); + int8_t num_packets = streamGetIntBefore(','); // 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; + int16_t len_packet = streamGetIntBefore('\n'); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux] && + num_packets >= 0 && len_packet >= 0) { + 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(); + int8_t nl = data.lastIndexOf(GSM_NL, data.length() - 8); + int8_t coma = data.indexOf(',', nl + 2); + int8_t mux = data.substring(nl + 2, coma).toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } data = ""; DBG("### Closed: ", mux); + } else if (data.endsWith(GF("+QNITZ:"))) { + streamSkipUntil('\n'); // URC for time sync + DBG("### Network time updated."); + 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, GsmConstStr r6=NULL) - { + int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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) - { + int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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 46b5eaf..c844afb 100644 --- a/src/TinyGsmClientSIM5360.h +++ b/src/TinyGsmClientSIM5360.h @@ -6,31 +6,33 @@ * @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 +#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE -#include +#include "TinyGsmBattery.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmGSMLocation.tpp" +#include "TinyGsmModem.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmTemperature.tpp" +#include "TinyGsmTime.tpp" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +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, -}; +#if defined TINY_GSM_DEBUG +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; +#endif enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,142 +41,161 @@ enum RegStatus { REG_UNKNOWN = 4, }; -enum TinyGSMDateTimeFormat { - DATE_FULL = 0, - DATE_TIME = 1, - DATE_DATE = 2 -}; - -class TinyGsmSim5360 -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmSim5360; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} - - GsmClient(TinyGsmSim5360& modem, uint8_t mux = 0) { - init(&modem, mux); - } - - virtual ~GsmClient(){} +class TinyGsmSim5360 : public TinyGsmModem, + public TinyGsmGPRS, + public TinyGsmTCP, + public TinyGsmSMS, + public TinyGsmTime, + public TinyGsmGSMLocation, + public TinyGsmBattery, + public TinyGsmTemperature { + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmSMS; + friend class TinyGsmTime; + friend class TinyGsmGSMLocation; + friend class TinyGsmBattery; + friend class TinyGsmTemperature; - 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; - } - -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 GsmClientSim5360(TinyGsmSim5360& modem, uint8_t mux = 0) { + init(&modem, mux); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + bool init(TinyGsmSim5360* modem, uint8_t mux = 0) { + this->at = modem; + 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(); - } + if (mux < TINY_GSM_MUX_COUNT) { + this->mux = mux; + } else { + this->mux = (mux % TINY_GSM_MUX_COUNT); + } + at->sockets[this->mux] = this; - virtual void stop() { stop(15000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + virtual int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES -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 = 0) + : GsmClientSim5360(modem, mux) {} -public: + public: + int connect(const char* host, uint16_t port, int timeout_s) override { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES + }; + */ - TinyGsmSim5360(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmSim5360(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } - virtual ~TinyGsmSim5360(){} - /* * 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()); - int ret = getSimStatus(); + // Disable time and time zone URC's + sendAT(GF("+CTZR=0")); + if (waitResponse(10000L) != 1) { return false; } + + // Enable automatic time zome update + sendAT(GF("+CTZU=1")); + if (waitResponse(10000L) != 1) { return false; } + + SimStatus ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); - } - // 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(); @@ -184,147 +205,87 @@ 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 - return false; - } - -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { - return false; // TODO: Module supports SSL, but not yet implemented - } - - bool hasWifi() { + bool factoryDefaultImpl() { // these commands aren't supported return false; } - bool hasGPRS() { - 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; } - /* - * SIM card functions - */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - -// Gets the CCID of a sim card via AT+CCID - String getSimCCID() { - sendAT(GF("+CICCID")); - 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; } + String getLocalIPImpl() { + sendAT(GF("+IPADDR")); // Inquire Socket PDP address + // sendAT(GF("+CGPADDR=1")); // Show PDP address + String res; + if (waitResponse(10000L, res) != 1) { return ""; } + res.replace(GSM_NL "OK" GSM_NL, ""); + res.replace(GSM_NL, ""); + res.trim(); + 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 @@ -338,7 +299,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 @@ -353,7 +314,7 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() waitResponse(); } - // Set active PDP context’s profile number + // Set active PDP context's profile number // This ties the embedded TCP/IP application to the external PDP context sendAT(GF("+CSOCKSETPN=1")); waitResponse(); @@ -369,21 +330,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) - // Ack = sets whether reporting a string “Send ok” = 0 (don't report) + // 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(); @@ -395,252 +359,120 @@ 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; } /* - * IP Address functions + * SIM card functions */ - - String getLocalIP() { - sendAT(GF("+IPADDR")); // Inquire Socket PDP address - // sendAT(GF("+CGPADDR=1")); // Show PDP address - String res; - if (waitResponse(10000L, res) != 1) { - return ""; - } - res.replace(GSM_NL "OK" GSM_NL, ""); - res.replace(GSM_NL, ""); + protected: + // Gets the CCID of a sim card via AT+CCID + String getSimCCIDImpl() { + sendAT(GF("+CICCID")); + if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { return ""; } + String res = stream.readStringUntil('\n'); + waitResponse(); 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; - /* * 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; - } - - bool sendSMS_UTF16(const String& number, const void* text, size_t len) { - // Select message format (1=text) - sendAT(GF("+CMGF=1")); - waitResponse(); - // Select TE character set - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - // Set text mode parameters - sendAT(GF("+CSMP=17,167,0,8")); - waitResponse(); - // Send the message - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - - uint16_t* t = (uint16_t*)text; - for (size_t i=0; i> 8; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - c = t[i] & 0xFF; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - } - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - - - /* - * Location functions - */ - - String getGsmLocation() TINY_GSM_ATTR_NOT_IMPLEMENTED; + protected: + // Follows all messaging functions per template /* - * GPS location functions + * GSM Location functions */ + protected: + // SIM5360 and SIM7100 can return a GSM-based location from CLBS as per the + // template; SIM5320 doesn't not appear to be able to /* * Time functions */ + protected: + // Can follow the standard CCLK function in the template /* - * Battery & temperature functions + * Battery functions */ - - // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() { + protected: + // SRGD Note: Returns voltage in VOLTS instead of millivolts + 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(); + float voltage = streamGetFloatBefore('\n'); // Wait for final OK waitResponse(); // Return millivolts - uint16_t res = voltage*1000; + uint16_t res = voltage * 1000; return res; } - int8_t getBattPercent() { + // SRGD Note: Returns voltage in VOLTS instead of millivolts + bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, + uint16_t& milliVolts) { 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(); + if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return false; } + chargeState = streamGetIntBefore(','); + percent = streamGetIntBefore(','); // get voltage in VOLTS - float voltage = stream.readStringUntil('\n').toFloat(); - milliVolts = voltage*1000; + float voltage = streamGetFloatBefore('\n'); + milliVolts = voltage * 1000; // Wait for final OK waitResponse(); return true; } + /* + * Temperature functions + */ + protected: // 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; - } - float res = stream.readStringUntil('\n').toFloat(); + if (waitResponse(GF(GSM_NL "+CMTE:")) != 1) { return false; } + float res = streamGetFloatBefore('\n'); // Wait for final OK waitResponse(); return res; @@ -649,80 +481,72 @@ 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 - return stream.readStringUntil('\n').toInt(); + 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 streamGetIntBefore('\n'); } 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) - int len_requested = stream.readStringUntil(',').toInt(); + streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX + streamSkipUntil(','); // Skip mux/cid (connecion id) + int16_t len_requested = streamGetIntBefore(','); // ^^ Requested number of data bytes (1-1460 bytes)to be read - int len_confirmed = stream.readStringUntil('\n').toInt(); + int16_t len_confirmed = streamGetIntBefore('\n'); // ^^ 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); } - DBG("### READ:", len_requested, "from", mux); + // DBG("### READ:", len_requested, "from", mux); // sockets[mux]->sock_available = modemGetAvailable(mux); sockets[mux]->sock_available = len_confirmed; waitResponse(); @@ -733,25 +557,21 @@ protected: sendAT(GF("+CIPRXGET=4,"), mux); size_t result = 0; if (waitResponse(GF("+CIPRXGET:")) == 1) { - streamSkipUntil(','); // Skip mode 4 - streamSkipUntil(','); // Skip mux - result = stream.readStringUntil('\n').toInt(); + streamSkipUntil(','); // Skip mode 4 + streamSkipUntil(','); // Skip mux + result = streamGetIntBefore('\n'); waitResponse(); } - DBG("### Available:", result, "on", mux); - if (!result) { - sockets[mux]->sock_connected = modemGetConnected(mux); - } + // DBG("### Available:", result, "on", 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(); } @@ -759,19 +579,20 @@ 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! + int8_t waitResponse(uint32_t timeout_ms, String& data, + GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -779,15 +600,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(); + uint8_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; + int8_t a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -795,6 +616,11 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 2; goto finish; } else if (r3 && data.endsWith(r3)) { +#if defined TINY_GSM_DEBUG + if (r3 == GFP(GSM_CME_ERROR)) { + streamSkipUntil('\n'); // Read out the error + } +#endif index = 3; goto finish; } else if (r4 && data.endsWith(r4)) { @@ -804,28 +630,28 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 5; goto finish; } else if (data.endsWith(GF(GSM_NL "+CIPRXGET:"))) { - String mode = stream.readStringUntil(','); - if (mode.toInt() == 1) { - int mux = stream.readStringUntil('\n').toInt(); + int8_t mode = streamGetIntBefore(','); + if (mode == 1) { + int8_t mux = streamGetIntBefore('\n'); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->got_data = true; } data = ""; - DBG("### Got Data:", mux); + // DBG("### Got Data:", mux); } else { data += mode; } } else if (data.endsWith(GF(GSM_NL "+RECEIVE:"))) { - int mux = stream.readStringUntil(',').toInt(); - int len = stream.readStringUntil('\n').toInt(); + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore('\n'); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->got_data = true; - sockets[mux]->sock_available = len; + if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } } data = ""; - DBG("### Got Data:", len, "on", mux); + // DBG("### Got Data:", len, "on", mux); } else if (data.endsWith(GF("+IPCLOSE:"))) { - int mux = stream.readStringUntil(',').toInt(); + int8_t mux = streamGetIntBefore(','); streamSkipUntil('\n'); // Skip the reason code if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; @@ -836,45 +662,49 @@ 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) - { + int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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) - { + int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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 ad13af1..2c7262c 100644 --- a/src/TinyGsmClientSIM7000.h +++ b/src/TinyGsmClientSIM7000.h @@ -6,31 +6,32 @@ * @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 +#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE -#include +#include "TinyGsmBattery.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmGPS.tpp" +#include "TinyGsmModem.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmTime.tpp" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +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, -}; +#if defined TINY_GSM_DEBUG +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; +#endif enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,311 +40,225 @@ enum RegStatus { REG_UNKNOWN = 4, }; -enum TinyGSMDateTimeFormat { - DATE_FULL = 0, - DATE_TIME = 1, - DATE_DATE = 2 -}; - -class TinyGsmSim7000 -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmSim7000; - typedef TinyGsmFifo RxFifo; +class TinyGsmSim7000 : public TinyGsmModem, + public TinyGsmGPRS, + public TinyGsmTCP, + public TinyGsmSMS, + public TinyGsmGPS, + public TinyGsmTime, + public TinyGsmBattery { + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmSMS; + friend class TinyGsmGPS; + friend class TinyGsmTime; + friend class TinyGsmBattery; -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; - - at->sockets[mux] = this; + /* + * Inner Client + */ + public: + class GsmClientSim7000 : public GsmClient { + friend class TinyGsmSim7000; - return true; - } + public: + GsmClientSim7000() {} -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 GsmClientSim7000(TinyGsmSim7000& modem, uint8_t mux = 0) { + init(&modem, mux); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + bool init(TinyGsmSim7000* modem, uint8_t mux = 0) { + this->at = modem; + 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(); - } + if (mux < TINY_GSM_MUX_COUNT) { + this->mux = mux; + } else { + this->mux = (mux % TINY_GSM_MUX_COUNT); + } + at->sockets[this->mux] = this; - virtual void stop() { stop(15000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + virtual int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES -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 = 0) + : public GsmClient(modem, mux) + {} -public: + public: + int connect(const char* host, uint16_t port, int timeout_s) override { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES + }; + */ - TinyGsmSim7000(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmSim7000(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } - virtual ~TinyGsmSim7000() {} - /* * 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()); - int ret = getSimStatus(); + // Enable Local Time Stamp for getting network time + sendAT(GF("+CLTS=1")); + if (waitResponse(10000L) != 1) { return false; } + + // Enable battery checks + sendAT(GF("+CBATCHK=1")); + if (waitResponse() != 1) { return false; } + + SimStatus ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); - } - // 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(); name = res2; - DBG("### Modem:", name); 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 hasWifi() { - return false; - } - - bool hasGPRS() { - 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; } + waitResponse(60000L, GF("SMS Ready")); 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; } - /* - * 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() - /* * Generic network functions */ + public: + RegStatus getRegistrationStatus() { + return (RegStatus)getRegistrationStatusXREG("CEREG"); + } -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; @@ -351,9 +266,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; @@ -361,24 +274,33 @@ 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; } + String getLocalIPImpl() { + sendAT(GF("+CIFSR;E0")); + String res; + if (waitResponse(10000L, res) != 1) { return ""; } + res.replace(GSM_NL "OK" GSM_NL, ""); + res.replace(GSM_NL, ""); + res.trim(); + 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 @@ -406,235 +328,94 @@ 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; } /* - * IP Address functions + * SIM card functions */ - - String getLocalIP() { - sendAT(GF("+CIFSR;E0")); - String res; - if (waitResponse(10000L, res) != 1) { - return ""; - } - res.replace(GSM_NL "OK" GSM_NL, ""); - res.replace(GSM_NL, ""); + protected: + // Doesn't return the "+CCID" before the number + String getSimCCIDImpl() { + sendAT(GF("+CCID")); + if (waitResponse(GF(GSM_NL)) != 1) { return ""; } + String res = stream.readStringUntil('\n'); + waitResponse(); 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; - /* * 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; - } - - bool sendSMS_UTF16(const String& number, const void* text, size_t len) { - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("+CSMP=17,167,0,8")); - waitResponse(); - - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - - uint16_t* t = (uint16_t*)text; - for (size_t i=0; i> 8; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - c = t[i] & 0xFF; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - } - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - - - /* - * 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: + // Follows all messaging functions per template /* - * GPS location functions + * GPS/GNSS/GLONASS location functions */ - + protected: // enable GPS - bool enableGPS() { + bool enableGPSImpl() { sendAT(GF("+CGNSPWR=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } - bool disableGPS() { + bool disableGPSImpl() { sendAT(GF("+CGNSPWR=0")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } // get the RAW GPS output - String getGPSraw() { + String getGPSrawImpl() { sendAT(GF("+CGNSINF")); - if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { - return ""; - } + if (waitResponse(10000L, GF(GSM_NL "+CGNSINF:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -642,244 +423,164 @@ 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 fix = false; - + bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0, + int* vsat = 0, int* usat = 0, float* accuracy = 0, + int* year = 0, int* month = 0, int* day = 0, int* hour = 0, + int* minute = 0, int* second = 0) { sendAT(GF("+CGNSINF")); - if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { - return false; - } + if (waitResponse(10000L, GF(GSM_NL "+CGNSINF:")) != 1) { return false; } + + streamSkipUntil(','); // GNSS run status + if (streamGetIntBefore(',') == 1) { // fix status + // init variables + float ilat = 0; + float ilon = 0; + float ispeed = 0; + float ialt = 0; + int ivsat = 0; + int iusat = 0; + float iaccuracy = 0; + int iyear = 0; + int imonth = 0; + int iday = 0; + int ihour = 0; + int imin = 0; + float secondWithSS = 0; + + // UTC date & Time + iyear = streamGetIntLength(4); // Four digit year + imonth = streamGetIntLength(2); // Two digit month + iday = streamGetIntLength(2); // Two digit day + ihour = streamGetIntLength(2); // Two digit hour + imin = streamGetIntLength(2); // Two digit minute + secondWithSS = + streamGetFloatBefore(','); // 6 digit second with subseconds + + ilat = streamGetFloatBefore(','); // Latitude + ilon = streamGetFloatBefore(','); // Longitude + ialt = streamGetFloatBefore(','); // MSL Altitude. Unit is meters + ispeed = streamGetFloatBefore(','); // Speed Over Ground. Unit is knots. + streamSkipUntil(','); // Course Over Ground. Degrees. + streamSkipUntil(','); // Fix Mode + streamSkipUntil(','); // Reserved1 + iaccuracy = + streamGetFloatBefore(','); // Horizontal Dilution Of Precision + streamSkipUntil(','); // Position Dilution Of Precision + streamSkipUntil(','); // Vertical Dilution Of Precision + streamSkipUntil(','); // Reserved2 + ivsat = streamGetIntBefore(','); // GNSS Satellites in View + iusat = streamGetIntBefore(','); // GNSS Satellites Used + streamSkipUntil(','); // GLONASS Satellites Used + streamSkipUntil(','); // Reserved3 + streamSkipUntil(','); // C/N0 max + streamSkipUntil(','); // HPA + streamSkipUntil('\n'); // VPA + + // Set pointers + if (lat != NULL) *lat = ilat; + if (lon != NULL) *lon = ilon; + if (speed != NULL) *speed = ispeed; + if (alt != NULL) *alt = ialt; + if (vsat != NULL) *vsat = ivsat; + if (usat != NULL) *usat = iusat; + if (accuracy != NULL) *accuracy = iaccuracy; + if (iyear < 2000) iyear += 2000; + if (year != NULL) *year = iyear; + if (month != NULL) *month = imonth; + if (day != NULL) *day = iday; + if (hour != NULL) *hour = ihour; + if (minute != NULL) *minute = imin; + if (second != NULL) *second = static_cast(secondWithSS); - 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(','); - stream.readStringUntil(','); - stream.readStringUntil(','); - stream.readStringUntil(','); - stream.readStringUntil(','); - if (vsat != NULL) *vsat = stream.readStringUntil(',').toInt(); //viewed satelites - if (usat != NULL) *usat = stream.readStringUntil(',').toInt(); //used satelites - stream.readStringUntil('\n'); + waitResponse(); + return true; + } + streamSkipUntil('\n'); // toss the row of commas waitResponse(); - - return fix; + return false; } /* * 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; - } - - // get GPS time - 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; - } - - for (int i = 0; i < 3; i++) { - String buffer = stream.readStringUntil(','); - buffer.toCharArray(chr_buffer, sizeof(chr_buffer)); - switch (i) { - case 0: - //mode - break; - case 1: - //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(); - break; - - default: - // if nothing else matches, do the default - // default is optional - break; - } - } - String res = stream.readStringUntil('\n'); - waitResponse(); - - if (fix) { - return true; - } else { - return false; - } - } + // Can follow CCLK as per template /* - * Battery & temperature functions + * Battery 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: + // Follows all battery functions per template /* * Client related functions */ + protected: + bool modemConnect(const char* host, uint16_t port, uint8_t mux, + bool ssl = false, int timeout_s = 75) { + if (ssl) { DBG("SSL not yet supported on this module!"); } -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); + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, + GF("\","), port); + return (1 == + 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))); } 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 - return stream.readStringUntil('\n').toInt(); + if (waitResponse(GF(GSM_NL "DATA ACCEPT:")) != 1) { return 0; } + streamSkipUntil(','); // Skip mux + return streamGetIntBefore('\n'); } 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 - int len_requested = stream.readStringUntil(',').toInt(); + streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX + streamSkipUntil(','); // Skip mux + int16_t len_requested = streamGetIntBefore(','); // ^^ 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); } - DBG("### READ:", len_requested, "from", mux); + // DBG("### READ:", len_requested, "from", mux); // sockets[mux]->sock_available = modemGetAvailable(mux); sockets[mux]->sock_available = len_confirmed; waitResponse(); @@ -890,38 +591,40 @@ protected: sendAT(GF("+CIPRXGET=4,"), mux); size_t result = 0; if (waitResponse(GF("+CIPRXGET:")) == 1) { - streamSkipUntil(','); // Skip mode 4 - streamSkipUntil(','); // Skip mux - result = stream.readStringUntil('\n').toInt(); + streamSkipUntil(','); // Skip mode 4 + streamSkipUntil(','); // Skip mux + result = streamGetIntBefore('\n'); waitResponse(); } - DBG("### Available:", result, "on", mux); - if (!result) { - sockets[mux]->sock_connected = modemGetConnected(mux); - } + // DBG("### Available:", result, "on", 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\"")); + waitResponse(GF("+CIPSTATUS")); + int8_t 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! + int8_t waitResponse(uint32_t timeout_ms, String& data, + GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -929,15 +632,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(); + uint8_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; + int8_t a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -945,6 +648,11 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 2; goto finish; } else if (r3 && data.endsWith(r3)) { +#if defined TINY_GSM_DEBUG + if (r3 == GFP(GSM_CME_ERROR)) { + streamSkipUntil('\n'); // Read out the error + } +#endif index = 3; goto finish; } else if (r4 && data.endsWith(r4)) { @@ -954,70 +662,93 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 5; goto finish; } else if (data.endsWith(GF(GSM_NL "+CIPRXGET:"))) { - String mode = stream.readStringUntil(','); - if (mode.toInt() == 1) { - int mux = stream.readStringUntil('\n').toInt(); + int8_t mode = streamGetIntBefore(','); + if (mode == 1) { + int8_t mux = streamGetIntBefore('\n'); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->got_data = true; } data = ""; - DBG("### Got Data:", mux); + // DBG("### Got Data:", mux); } else { data += mode; } } else if (data.endsWith(GF(GSM_NL "+RECEIVE:"))) { - int mux = stream.readStringUntil(',').toInt(); - int len = stream.readStringUntil('\n').toInt(); + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore('\n'); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->got_data = true; - sockets[mux]->sock_available = len; + if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } } data = ""; - DBG("### Got Data:", len, "on", mux); + // 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(); + int8_t nl = data.lastIndexOf(GSM_NL, data.length() - 8); + int8_t coma = data.indexOf(',', nl + 2); + int8_t mux = data.substring(nl + 2, coma).toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } data = ""; DBG("### Closed: ", mux); + } else if (data.endsWith(GF("*PSNWID:"))) { + streamSkipUntil('\n'); // Refresh network name by network + data = ""; + DBG("### Network name updated."); + } else if (data.endsWith(GF("*PSUTTZ:"))) { + streamSkipUntil('\n'); // Refresh time and time zone by network + data = ""; + DBG("### Network time and time zone updated."); + } else if (data.endsWith(GF("+CTZV:"))) { + streamSkipUntil('\n'); // Refresh network time zone by network + data = ""; + DBG("### Network time zone updated."); + } else if (data.endsWith(GF("DST: "))) { + streamSkipUntil( + '\n'); // Refresh Network Daylight Saving Time by network + data = ""; + DBG("### Daylight savings time state updated."); } } } 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) - { + int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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) - { + int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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 b9d767a..3d510be 100644 --- a/src/TinyGsmClientSIM7600.h +++ b/src/TinyGsmClientSIM7600.h @@ -6,31 +6,34 @@ * @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 +#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE + +#include "TinyGsmBattery.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmGPS.tpp" +#include "TinyGsmGSMLocation.tpp" +#include "TinyGsmModem.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmTemperature.tpp" +#include "TinyGsmTime.tpp" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +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, -}; +#if defined TINY_GSM_DEBUG +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; +#endif enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,142 +42,164 @@ enum RegStatus { REG_UNKNOWN = 4, }; -enum TinyGSMDateTimeFormat { - DATE_FULL = 0, - DATE_TIME = 1, - DATE_DATE = 2 -}; - -class TinyGsmSim7600 -{ - -public: - -class GsmClient : public Client -{ - friend class TinyGsmSim7600; - typedef TinyGsmFifo RxFifo; - -public: - GsmClient() {} - - GsmClient(TinyGsmSim7600& modem, uint8_t mux = 0) { - init(&modem, mux); - } +class TinyGsmSim7600 : public TinyGsmModem, + public TinyGsmGPRS, + public TinyGsmTCP, + public TinyGsmSMS, + public TinyGsmGSMLocation, + public TinyGsmGPS, + public TinyGsmTime, + public TinyGsmBattery, + public TinyGsmTemperature { + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmSMS; + friend class TinyGsmGPS; + friend class TinyGsmGSMLocation; + friend class TinyGsmTime; + friend class TinyGsmBattery; + friend class TinyGsmTemperature; - 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; + 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(); - } + if (mux < TINY_GSM_MUX_COUNT) { + this->mux = mux; + } else { + this->mux = (mux % TINY_GSM_MUX_COUNT); + } + at->sockets[this->mux] = this; - virtual void stop() { stop(15000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + virtual int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES -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: - TinyGsmSim7600* at; - uint8_t mux; - uint16_t sock_available; - uint32_t prev_check; - bool sock_connected; - bool got_data; - RxFifo rx; -}; + /*TODO(?)) + class GsmClientSecureSIM7600 : public GsmClientSim7600 + { + public: + GsmClientSecure() {} + GsmClientSecure(TinyGsmSim7600& modem, uint8_t mux = 0) + : public GsmClient(modem, mux) + {} -public: + public: + int connect(const char* host, uint16_t port, int timeout_s) override { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES + }; + */ - TinyGsmSim7600(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmSim7600(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } - virtual ~TinyGsmSim7600(){} - /* * 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()); - int ret = getSimStatus(); + // Disable time and time zone URC's + sendAT(GF("+CTZR=0")); + if (waitResponse(10000L) != 1) { return false; } + + // Enable automatic time zome update + sendAT(GF("+CTZU=1")); + if (waitResponse(10000L) != 1) { return false; } + + SimStatus ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); - } - // 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(); @@ -184,147 +209,87 @@ 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 - return false; - } - -TINY_GSM_MODEM_GET_INFO_ATI() - - bool hasSSL() { - return false; // TODO: Module supports SSL, but not yet implemented - } - - bool hasWifi() { + bool factoryDefaultImpl() { // these commands aren't supported return false; } - bool hasGPRS() { - 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; } - /* - * SIM card functions - */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - -// Gets the CCID of a sim card via AT+CCID - String getSimCCID() { - sendAT(GF("+CICCID")); - 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; } + String getLocalIPImpl() { + sendAT(GF("+IPADDR")); // Inquire Socket PDP address + // sendAT(GF("+CGPADDR=1")); // Show PDP address + String res; + if (waitResponse(10000L, res) != 1) { return ""; } + res.replace(GSM_NL "OK" GSM_NL, ""); + res.replace(GSM_NL, ""); + res.trim(); + 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 @@ -338,7 +303,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 @@ -352,21 +317,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) - // Ack = sets whether reporting a string “Send ok” = 0 (don't report) + // 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(); @@ -378,189 +346,91 @@ 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; } /* - * IP Address functions + * SIM card functions */ - - String getLocalIP() { - sendAT(GF("+IPADDR")); // Inquire Socket PDP address - // sendAT(GF("+CGPADDR=1")); // Show PDP address - String res; - if (waitResponse(10000L, res) != 1) { - return ""; - } - res.replace(GSM_NL "OK" GSM_NL, ""); - res.replace(GSM_NL, ""); + protected: + // Gets the CCID of a sim card via AT+CCID + String getSimCCIDImpl() { + sendAT(GF("+CICCID")); + if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { return ""; } + String res = stream.readStringUntil('\n'); + waitResponse(); 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; - } - - bool sendSMS_UTF16(const String& number, const void* text, size_t len) { - // Select message format (1=text) - sendAT(GF("+CMGF=1")); - waitResponse(); - // Select TE character set - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - // Set text mode parameters - sendAT(GF("+CSMP=17,167,0,8")); - waitResponse(); - // Send the message - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - - uint16_t* t = (uint16_t*)text; - for (size_t i=0; i> 8; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - c = t[i] & 0xFF; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - } - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - + protected: + // Follows all messaging functions per template /* - * Location functions + * GSM Location functions */ - - String getGsmLocation() TINY_GSM_ATTR_NOT_IMPLEMENTED; + protected: + // Can return a GSM-based location from CLBS as per the template /* - * GPS location functions + * GPS/GNSS/GLONASS location functions */ + protected: // enable GPS - bool enableGPS() { + bool enableGPSImpl() { sendAT(GF("+CGPS=1")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } - bool disableGPS() { + bool disableGPSImpl() { sendAT(GF("+CGPS=0")); - if (waitResponse() != 1) { - return false; - } + if (waitResponse() != 1) { return false; } return true; } // get the RAW GPS output - String getGPSraw() { + String getGPSrawImpl() { 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(); @@ -568,83 +438,137 @@ TINY_GSM_MODEM_WAIT_FOR_NETWORK() } // get GPS informations - bool getGPS(float *lat, float *lon, float *speed=0, int *alt=0) { - //String buffer = ""; - bool fix = false; - + bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0, + int* vsat = 0, int* usat = 0, float* accuracy = 0, + int* year = 0, int* month = 0, int* day = 0, int* hour = 0, + int* minute = 0, int* second = 0) { sendAT(GF("+CGNSSINFO")); - if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { - return false; - } + if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { return false; } + + uint8_t fixMode = streamGetIntBefore(','); // mode 2=2D Fix or 3=3DFix + // TODO(?) Can 1 be returned + if (fixMode == 1 || fixMode == 2 || fixMode == 3) { + // init variables + float ilat = 0; + char north; + float ilon = 0; + char east; + float ispeed = 0; + float ialt = 0; + int ivsat = 0; + int iusat = 0; + float iaccuracy = 0; + int iyear = 0; + int imonth = 0; + int iday = 0; + int ihour = 0; + int imin = 0; + float secondWithSS = 0; + + streamSkipUntil(','); // GPS satellite valid numbers + streamSkipUntil(','); // GLONASS satellite valid numbers + streamSkipUntil(','); // BEIDOU satellite valid numbers + ilat = streamGetFloatBefore(','); // Latitude in ddmm.mmmmmm + north = stream.read(); // N/S Indicator, N=north or S=south + streamSkipUntil(','); + ilon = streamGetFloatBefore(','); // Longitude in ddmm.mmmmmm + east = stream.read(); // E/W Indicator, E=east or W=west + streamSkipUntil(','); + + // Date. Output format is ddmmyy + iday = streamGetIntLength(2); // Two digit day + imonth = streamGetIntLength(2); // Two digit month + iyear = streamGetIntBefore(','); // Two digit year + + // UTC Time. Output format is hhmmss.s + ihour = streamGetIntLength(2); // Two digit hour + imin = streamGetIntLength(2); // Two digit minute + secondWithSS = + streamGetFloatBefore(','); // 4 digit second with subseconds + + ialt = streamGetFloatBefore(','); // MSL Altitude. Unit is meters + ispeed = streamGetFloatBefore(','); // Speed Over Ground. Unit is knots. + streamSkipUntil(','); // Course Over Ground. Degrees. + streamSkipUntil(','); // After set, will report GPS every x seconds + iaccuracy = streamGetFloatBefore(','); // Position Dilution Of Precision + streamSkipUntil(','); // Horizontal Dilution Of Precision + streamSkipUntil(','); // Vertical Dilution Of Precision + streamSkipUntil('\n'); // TODO(?) is one more field reported?? + + // Set pointers + if (lat != NULL) + *lat = (floor(ilat / 100) + fmod(ilat, 100.) / 60) * + (north == 'N' ? 1 : -1); + if (lon != NULL) + *lon = (floor(ilon / 100) + fmod(ilon, 100.) / 60) * + (east == 'E' ? 1 : -1); + if (speed != NULL) *speed = ispeed; + if (alt != NULL) *alt = ialt; + if (vsat != NULL) *vsat = ivsat; + if (usat != NULL) *usat = iusat; + if (accuracy != NULL) *accuracy = iaccuracy; + if (iyear < 2000) iyear += 2000; + if (year != NULL) *year = iyear; + if (month != NULL) *month = imonth; + if (day != NULL) *day = iday; + if (hour != NULL) *hour = ihour; + if (minute != NULL) *minute = imin; + if (second != NULL) *second = static_cast(secondWithSS); - //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 true; + } waitResponse(); - - return fix; + return false; } + /* * Time functions */ + protected: + // Can follow the standard CCLK function in the template /* - * Battery & temperature functions + * Battery functions */ - - // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltage() { + protected: + // returns volts, multiply by 1000 to get mV + 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(); + float voltage = streamGetFloatBefore('\n'); // 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; } + /* + * Temperature functions + */ + protected: // 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(); + uint16_t res = streamGetIntBefore('\n'); // Wait for final OK waitResponse(); return res; @@ -653,80 +577,72 @@ 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 - return stream.readStringUntil('\n').toInt(); + 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 streamGetIntBefore('\n'); } 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) - int len_requested = stream.readStringUntil(',').toInt(); + streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX + streamSkipUntil(','); // Skip mux/cid (connecion id) + int16_t len_requested = streamGetIntBefore(','); // ^^ Requested number of data bytes (1-1460 bytes)to be read - int len_confirmed = stream.readStringUntil('\n').toInt(); + int16_t len_confirmed = streamGetIntBefore('\n'); // ^^ 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); } - DBG("### READ:", len_requested, "from", mux); + // DBG("### READ:", len_requested, "from", mux); // sockets[mux]->sock_available = modemGetAvailable(mux); sockets[mux]->sock_available = len_confirmed; waitResponse(); @@ -737,15 +653,13 @@ protected: sendAT(GF("+CIPRXGET=4,"), mux); size_t result = 0; if (waitResponse(GF("+CIPRXGET:")) == 1) { - streamSkipUntil(','); // Skip mode 4 - streamSkipUntil(','); // Skip mux - result = stream.readStringUntil('\n').toInt(); + streamSkipUntil(','); // Skip mode 4 + streamSkipUntil(','); // Skip mux + result = streamGetIntBefore('\n'); waitResponse(); } - DBG("### Available:", result, "on", mux); - if (!result) { - sockets[mux]->sock_connected = modemGetConnected(mux); - } + // DBG("### Available:", result, "on", mux); + if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); } return result; } @@ -755,7 +669,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(); } @@ -763,19 +677,20 @@ 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! + int8_t waitResponse(uint32_t timeout_ms, String& data, + GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -783,15 +698,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(); + uint8_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; + int8_t a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -799,6 +714,11 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 2; goto finish; } else if (r3 && data.endsWith(r3)) { +#if defined TINY_GSM_DEBUG + if (r3 == GFP(GSM_CME_ERROR)) { + streamSkipUntil('\n'); // Read out the error + } +#endif index = 3; goto finish; } else if (r4 && data.endsWith(r4)) { @@ -808,28 +728,28 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 5; goto finish; } else if (data.endsWith(GF(GSM_NL "+CIPRXGET:"))) { - String mode = stream.readStringUntil(','); - if (mode.toInt() == 1) { - int mux = stream.readStringUntil('\n').toInt(); + int8_t mode = streamGetIntBefore(','); + if (mode == 1) { + int8_t mux = streamGetIntBefore('\n'); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->got_data = true; } data = ""; - DBG("### Got Data:", mux); + // DBG("### Got Data:", mux); } else { data += mode; } } else if (data.endsWith(GF(GSM_NL "+RECEIVE:"))) { - int mux = stream.readStringUntil(',').toInt(); - int len = stream.readStringUntil('\n').toInt(); + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore('\n'); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->got_data = true; - sockets[mux]->sock_available = len; + if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } } data = ""; - DBG("### Got Data:", len, "on", mux); + // DBG("### Got Data:", len, "on", mux); } else if (data.endsWith(GF("+IPCLOSE:"))) { - int mux = stream.readStringUntil(',').toInt(); + int8_t mux = streamGetIntBefore(','); streamSkipUntil('\n'); // Skip the reason code if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; @@ -840,45 +760,49 @@ 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) - { + int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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) - { + int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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 be225d5..0b20ccd 100644 --- a/src/TinyGsmClientSIM800.h +++ b/src/TinyGsmClientSIM800.h @@ -6,32 +6,35 @@ * @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 +#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE + +#include "TinyGsmBattery.tpp" +#include "TinyGsmCalling.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmGSMLocation.tpp" +#include "TinyGsmModem.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmSSL.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmTime.tpp" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +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, -}; +#if defined TINY_GSM_DEBUG +static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; +#endif enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -39,174 +42,172 @@ enum RegStatus { REG_OK_ROAMING = 5, REG_UNKNOWN = 4, }; +class TinyGsmSim800 : public TinyGsmModem, + public TinyGsmGPRS, + public TinyGsmTCP, + public TinyGsmSSL, + public TinyGsmCalling, + public TinyGsmSMS, + public TinyGsmGSMLocation, + public TinyGsmTime, + public TinyGsmBattery { + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmSSL; + friend class TinyGsmCalling; + friend class TinyGsmSMS; + friend class TinyGsmGSMLocation; + friend class TinyGsmTime; + friend class TinyGsmBattery; -enum TinyGSMDateTimeFormat { - DATE_FULL = 0, - DATE_TIME = 1, - DATE_DATE = 2 -}; - -class TinyGsmSim800 -{ - -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; + /* + * Inner Client + */ + public: + class GsmClientSim800 : public GsmClient { + friend class TinyGsmSim800; - at->sockets[mux] = this; + public: + GsmClientSim800() {} - 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 GsmClientSim800(TinyGsmSim800& modem, uint8_t mux = 0) { + init(&modem, mux); + } -TINY_GSM_CLIENT_CONNECT_OVERLOADS() + bool init(TinyGsmSim800* modem, uint8_t mux = 0) { + this->at = modem; + 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(); - } + if (mux < TINY_GSM_MUX_COUNT) { + this->mux = mux; + } else { + this->mux = (mux % TINY_GSM_MUX_COUNT); + } + at->sockets[this->mux] = this; - virtual void stop() { stop(15000L); } + return true; + } -TINY_GSM_CLIENT_WRITE() + public: + virtual int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES -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 = 0) + : 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) override { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES + }; - TinyGsmSim800(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmSim800(Stream& stream) : stream(stream) { memset(sockets, 0, sizeof(sockets)); } - virtual ~TinyGsmSim800() {} - /* * Basic functions */ + protected: + bool initImpl(const char* pin = NULL) { + DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); - bool begin(const char* pin = NULL) { - return init(pin); - } + if (!testAT()) { return false; } - bool init(const char* pin = NULL) { - DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + // sendAT(GF("&FZ")); // Factory + Reset + // waitResponse(); - if (!testAT()) { - return false; - } - sendAT(GF("&FZ")); // Factory + Reset + 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(); - sendAT(GF("E0")); // Echo Off - if (waitResponse() != 1) { - return false; - } DBG(GF("### Modem:"), getModemName()); - int ret = getSimStatus(); + // Enable Local Time Stamp for getting network time + sendAT(GF("+CLTS=1")); + if (waitResponse(10000L) != 1) { return false; } + + // Enable battery checks + sendAT(GF("+CBATCHK=1")); + waitResponse(); + + SimStatus ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); - } - // 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,156 +217,98 @@ 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() { -#if defined(TINY_GSM_MODEM_SIM900) - return false; -#else - sendAT(GF("+CIPSSL=?")); - if (waitResponse(GF(GSM_NL "+CIPSSL:")) != 1) { + /* + bool thisHasSSL() { + #if defined(TINY_GSM_MODEM_SIM900) return false; + #else + sendAT(GF("+CIPSSL=?")); + if (waitResponse(GF(GSM_NL "+CIPSSL:")) != 1) { return false; } + return waitResponse() == 1; + #endif } - return waitResponse() == 1; -#endif - } - - bool hasWifi() { - return false; - } - - bool hasGPRS() { - 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; } - /* - * 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() - /* * 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() + String getLocalIPImpl() { + sendAT(GF("+CIFSR;E0")); + String res; + if (waitResponse(10000L, res) != 1) { return ""; } + res.replace(GSM_NL "OK" GSM_NL, ""); + res.replace(GSM_NL, ""); + res.trim(); + 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 @@ -393,390 +336,147 @@ 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; } /* - * IP Address functions + * SIM card functions */ - - String getLocalIP() { - sendAT(GF("+CIFSR;E0")); - String res; - 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()); - } + protected: + // Able to follow all SIM card functions as inherited from the template /* * 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; - } - - bool sendSMS_UTF16(const String& number, const void* text, size_t len) { - sendAT(GF("+CMGF=1")); - waitResponse(); - sendAT(GF("+CSCS=\"HEX\"")); - waitResponse(); - sendAT(GF("+CSMP=17,167,0,8")); - waitResponse(); - - sendAT(GF("+CMGS=\""), number, GF("\"")); - if (waitResponse(GF(">")) != 1) { - return false; - } - - uint16_t* t = (uint16_t*)text; - for (size_t i=0; i> 8; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - c = t[i] & 0xFF; - if (c < 0x10) { stream.print('0'); } - stream.print(c, HEX); - } - stream.write((char)0x1A); - stream.flush(); - return waitResponse(60000L) == 1; - } - + protected: + // Follows all messaging functions per template /* - * Location functions + * GSM Location functions */ + protected: + // Depending on the exacty model and firmware revision, should return a + // GSM-based location from CLBS as per the template + // TODO(?): Check number of digits in year (2 or 4) - 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/GNSS/GLONASS location functions + */ + protected: + // 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 + * Battery 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: + // Follows all battery functions per template /* * 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) { + // Set GPRS bearer profile to associate with NTP sync sendAT(GF("+CNTPCID=1")); - if (waitResponse(10000L) != 1) { - return -1; - } + if (waitResponse(10000L) != 1) { return -1; } + // Set NTP server and timezone sendAT(GF("+CNTP="), server, ',', String(TimeZone)); - if (waitResponse(10000L) != 1) { - return -1; - } + if (waitResponse(10000L) != 1) { return -1; } + // Request network synchronization 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; @@ -785,81 +485,76 @@ 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) { + int8_t 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 - return stream.readStringUntil('\n').toInt(); + if (waitResponse(GF(GSM_NL "DATA ACCEPT:")) != 1) { return 0; } + streamSkipUntil(','); // Skip mux + return streamGetIntBefore('\n'); } 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 - int len_requested = stream.readStringUntil(',').toInt(); + streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX + streamSkipUntil(','); // Skip mux + int16_t len_requested = streamGetIntBefore(','); // ^^ 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); } - DBG("### READ:", len_requested, "from", mux); + // DBG("### READ:", len_requested, "from", mux); // sockets[mux]->sock_available = modemGetAvailable(mux); sockets[mux]->sock_available = len_confirmed; waitResponse(); @@ -870,40 +565,40 @@ protected: sendAT(GF("+CIPRXGET=4,"), mux); size_t result = 0; if (waitResponse(GF("+CIPRXGET:")) == 1) { - streamSkipUntil(','); // Skip mode 4 - streamSkipUntil(','); // Skip mux - result = stream.readStringUntil('\n').toInt(); + streamSkipUntil(','); // Skip mode 4 + streamSkipUntil(','); // Skip mux + result = streamGetIntBefore('\n'); waitResponse(); } - DBG("### Available:", result, "on", mux); - if (!result) { - sockets[mux]->sock_connected = modemGetConnected(mux); - } + // DBG("### Available:", result, "on", 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\"")); + int8_t 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! + int8_t waitResponse(uint32_t timeout_ms, String& data, + GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { /*String r1s(r1); r1s.trim(); String r2s(r2); r2s.trim(); String r3s(r3); r3s.trim(); @@ -911,14 +606,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(); + uint8_t index = 0; + uint32_t startMillis = millis(); do { TINY_GSM_YIELD(); while (stream.available() > 0) { - int a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += (char)a; + TINY_GSM_YIELD(); + int8_t a = stream.read(); + if (a <= 0) continue; // Skip 0x00 bytes, just in case + data += static_cast(a); if (r1 && data.endsWith(r1)) { index = 1; goto finish; @@ -926,6 +622,11 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 2; goto finish; } else if (r3 && data.endsWith(r3)) { +#if defined TINY_GSM_DEBUG + if (r3 == GFP(GSM_CME_ERROR)) { + streamSkipUntil('\n'); // Read out the error + } +#endif index = 3; goto finish; } else if (r4 && data.endsWith(r4)) { @@ -935,70 +636,93 @@ TINY_GSM_MODEM_STREAM_UTILITIES() index = 5; goto finish; } else if (data.endsWith(GF(GSM_NL "+CIPRXGET:"))) { - String mode = stream.readStringUntil(','); - if (mode.toInt() == 1) { - int mux = stream.readStringUntil('\n').toInt(); + int8_t mode = streamGetIntBefore(','); + if (mode == 1) { + int8_t mux = streamGetIntBefore('\n'); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->got_data = true; } data = ""; - DBG("### Got Data:", mux); + // DBG("### Got Data:", mux); } else { data += mode; } } else if (data.endsWith(GF(GSM_NL "+RECEIVE:"))) { - int mux = stream.readStringUntil(',').toInt(); - int len = stream.readStringUntil('\n').toInt(); + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore('\n'); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->got_data = true; - sockets[mux]->sock_available = len; + if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } } data = ""; - DBG("### Got Data:", len, "on", mux); + // 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(); + int8_t nl = data.lastIndexOf(GSM_NL, data.length() - 8); + int8_t coma = data.indexOf(',', nl + 2); + int8_t mux = data.substring(nl + 2, coma).toInt(); if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { sockets[mux]->sock_connected = false; } data = ""; DBG("### Closed: ", mux); + } else if (data.endsWith(GF("*PSNWID:"))) { + streamSkipUntil('\n'); // Refresh network name by network + data = ""; + DBG("### Network name updated."); + } else if (data.endsWith(GF("*PSUTTZ:"))) { + streamSkipUntil('\n'); // Refresh time and time zone by network + data = ""; + DBG("### Network time and time zone updated."); + } else if (data.endsWith(GF("+CTZV:"))) { + streamSkipUntil('\n'); // Refresh network time zone by network + data = ""; + DBG("### Network time zone updated."); + } else if (data.endsWith(GF("DST:"))) { + streamSkipUntil( + '\n'); // Refresh Network Daylight Saving Time by network + data = ""; + DBG("### Daylight savings time state updated."); } } } 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) - { + int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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) - { + int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), + GsmConstStr r2 = GFP(GSM_ERROR), +#if defined TINY_GSM_DEBUG + GsmConstStr r3 = GFP(GSM_CME_ERROR), +#else + GsmConstStr r3 = NULL, +#endif + 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 ecd07a1..ab699d1 100644 --- a/src/TinyGsmClientSIM808.h +++ b/src/TinyGsmClientSIM808.h @@ -6,58 +6,42 @@ * @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 +#include "TinyGsmClientSIM800.h" +#include "TinyGsmGPS.tpp" +class TinyGsmSim808 : public TinyGsmSim800, public TinyGsmGPS { + friend class TinyGsmGPS; -class TinyGsmSim808: public TinyGsmSim800 -{ + public: + explicit TinyGsmSim808(Stream& stream) : TinyGsmSim800(stream) {} -public: - - TinyGsmSim808(Stream& stream) - : TinyGsmSim800(stream) - {} - - virtual ~TinyGsmSim808(){} /* - * GPS location functions + * GPS/GNSS/GLONASS location functions */ - + protected: // enable GPS - bool enableGPS() { - // uint16_t state; - + bool enableGPSImpl() { sendAT(GF("+CGNSPWR=1")); - if (waitResponse() != 1) { - return false; - } - + if (waitResponse() != 1) { return false; } return true; } - bool disableGPS() { - // uint16_t state; - + bool disableGPSImpl() { sendAT(GF("+CGNSPWR=0")); - if (waitResponse() != 1) { - return false; - } - + if (waitResponse() != 1) { return false; } return true; } // get the RAW GPS output // works only with ans SIM808 V2 - String getGPSraw() { + String getGPSrawImpl() { sendAT(GF("+CGNSINF")); - if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { - return ""; - } + if (waitResponse(10000L, GF(GSM_NL "+CGNSINF:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -66,87 +50,83 @@ 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 = ""; - // 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 - stream.readStringUntil(','); - stream.readStringUntil(','); - stream.readStringUntil(','); - stream.readStringUntil(','); - stream.readStringUntil(','); - stream.readStringUntil(','); - stream.readStringUntil(','); - if (vsat != NULL) *vsat = stream.readStringUntil(',').toInt(); //viewed satelites - if (usat != NULL) *usat = stream.readStringUntil(',').toInt(); //used satelites - stream.readStringUntil('\n'); - - waitResponse(); - - return fix; - } - - // get GPS time - // works only with SIM808 V2 - bool getGPSTime(int *year, int *month, int *day, int *hour, int *minute, int *second) { - bool fix = false; - char chr_buffer[12]; + bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0, + int* vsat = 0, int* usat = 0, float* accuracy = 0, + int* year = 0, int* month = 0, int* day = 0, int* hour = 0, + int* minute = 0, int* second = 0) { sendAT(GF("+CGNSINF")); - if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) { - return false; + if (waitResponse(10000L, GF(GSM_NL "+CGNSINF:")) != 1) { return false; } + + streamSkipUntil(','); // GNSS run status + if (streamGetIntBefore(',') == 1) { // fix status + // init variables + float ilat = 0; + float ilon = 0; + float ispeed = 0; + float ialt = 0; + int ivsat = 0; + int iusat = 0; + float iaccuracy = 0; + int iyear = 0; + int imonth = 0; + int iday = 0; + int ihour = 0; + int imin = 0; + float secondWithSS = 0; + + // UTC date & Time + iyear = streamGetIntLength(4); // Four digit year + imonth = streamGetIntLength(2); // Two digit month + iday = streamGetIntLength(2); // Two digit day + ihour = streamGetIntLength(2); // Two digit hour + imin = streamGetIntLength(2); // Two digit minute + secondWithSS = + streamGetFloatBefore(','); // 6 digit second with subseconds + + ilat = streamGetFloatBefore(','); // Latitude + ilon = streamGetFloatBefore(','); // Longitude + ialt = streamGetFloatBefore(','); // MSL Altitude. Unit is meters + ispeed = streamGetFloatBefore(','); // Speed Over Ground. Unit is knots. + streamSkipUntil(','); // Course Over Ground. Degrees. + streamSkipUntil(','); // Fix Mode + streamSkipUntil(','); // Reserved1 + iaccuracy = + streamGetFloatBefore(','); // Horizontal Dilution Of Precision + streamSkipUntil(','); // Position Dilution Of Precision + streamSkipUntil(','); // Vertical Dilution Of Precision + streamSkipUntil(','); // Reserved2 + ivsat = streamGetIntBefore(','); // GNSS Satellites in View + iusat = streamGetIntBefore(','); // GNSS Satellites Used + streamSkipUntil(','); // GLONASS Satellites Used + streamSkipUntil(','); // Reserved3 + streamSkipUntil(','); // C/N0 max + streamSkipUntil(','); // HPA + streamSkipUntil('\n'); // VPA + + // Set pointers + if (lat != NULL) *lat = ilat; + if (lon != NULL) *lon = ilon; + if (speed != NULL) *speed = ispeed; + if (alt != NULL) *alt = ialt; + if (vsat != NULL) *vsat = ivsat; + if (usat != NULL) *usat = iusat; + if (accuracy != NULL) *accuracy = iaccuracy; + if (iyear < 2000) iyear += 2000; + if (year != NULL) *year = iyear; + if (month != NULL) *month = imonth; + if (day != NULL) *day = iday; + if (hour != NULL) *hour = ihour; + if (minute != NULL) *minute = imin; + if (second != NULL) *second = static_cast(secondWithSS); + + waitResponse(); + return true; } - for (int i = 0; i < 3; i++) { - String buffer = stream.readStringUntil(','); - buffer.toCharArray(chr_buffer, sizeof(chr_buffer)); - switch (i) { - case 0: - //mode - break; - case 1: - //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(); - break; - - default: - // if nothing else matches, do the default - // default is optional - break; - } - } - String res = stream.readStringUntil('\n'); + streamSkipUntil('\n'); // toss the row of commas waitResponse(); - - if (fix) { - return true; - } else { - return false; - } + return false; } - }; -#endif +#endif // SRC_TINYGSMCLIENTSIM808_H_ diff --git a/src/TinyGsmClientSaraR4.h b/src/TinyGsmClientSaraR4.h index b37e75f..dddcbc0 100644 --- a/src/TinyGsmClientSaraR4.h +++ b/src/TinyGsmClientSaraR4.h @@ -6,32 +6,35 @@ * @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 +#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE + +#include "TinyGsmBattery.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmGPS.tpp" +#include "TinyGsmGSMLocation.tpp" +#include "TinyGsmModem.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmSSL.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmTemperature.tpp" +#include "TinyGsmTime.tpp" #define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; +static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; +#if defined TINY_GSM_DEBUG static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; - -enum SimStatus { - SIM_ERROR = 0, - SIM_READY = 1, - SIM_LOCKED = 2, -}; +#endif enum RegStatus { + REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, REG_DENIED = 3, @@ -40,157 +43,186 @@ enum RegStatus { REG_UNKNOWN = 4, }; +class TinyGsmSaraR4 : public TinyGsmModem, + public TinyGsmGPRS, + public TinyGsmTCP, + public TinyGsmSSL, + public TinyGsmBattery, + public TinyGsmGSMLocation, + public TinyGsmGPS, + public TinyGsmSMS, + public TinyGsmTemperature, + public TinyGsmTime { + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmSSL; + friend class TinyGsmBattery; + friend class TinyGsmGSMLocation; + friend class TinyGsmGPS; + friend class TinyGsmSMS; + friend class TinyGsmTemperature; + friend class TinyGsmTime; -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; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; - at->sockets[mux] = this; + if (mux < TINY_GSM_MUX_COUNT) { + this->mux = mux; + } else { + this->mux = (mux % TINY_GSM_MUX_COUNT); + } + at->sockets[this->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: + virtual int connect(const char* host, uint16_t port, int timeout_s) { + // stop(); // DON'T 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; + } + virtual 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; + } + } + void stop() override { + stop(135000L); } - 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() - - // // synchronous close - // at->sendAT(GF("+USOCL="), mux); - // // NOTE: can take up to 120s to get a response - // at->waitResponse((maxWaitMs - (millis() - startMillis))); - // sock_connected = false; - - // faster asynchronous close - // NOT supported on SARA-R404M / SARA-R410M-01B - at->sendAT(GF("+USOCL="), mux, GF(",1")); - // NOTE: can take up to 120s to get a response - at->waitResponse((maxWaitMs - (millis() - startMillis))); - sock_connected = false; - } - - virtual void stop() { stop(135000L); } - -TINY_GSM_CLIENT_WRITE() - -TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() -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 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; -}; - + explicit GsmClientSecureR4(TinyGsmSaraR4& modem, uint8_t mux = 0) + : GsmClientSaraR4(modem, mux) {} -class GsmClientSecure : public GsmClient -{ -public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmSaraR4& 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(); - 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) override { + // stop(); // DON'T 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: + int connect(IPAddress ip, uint16_t port, int timeout_s) override { + 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); + } + }; - TinyGsmSaraR4(Stream& stream) - : stream(stream) - { + /* + * Constructor + */ + public: + explicit TinyGsmSaraR4(Stream& stream) + : stream(stream), + has2GFallback(false), + supportsAsyncSockets(false) { memset(sockets, 0, sizeof(sockets)); } - virtual ~TinyGsmSaraR4(){} - /* * 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 @@ -199,35 +231,47 @@ public: #endif waitResponse(); - DBG(GF("### Modem:"), getModemName()); + String modemName = getModemName(); + DBG(GF("### Modem:"), modemName); + if (modemName.startsWith("u-blox SARA-R412")) { + has2GFallback = true; + } else { + has2GFallback = false; + } + if (modemName.startsWith("u-blox SARA-R404M") || + modemName.startsWith("u-blox SARA-R410M-01B")) { + supportsAsyncSockets = false; + } else { + supportsAsyncSockets = true; + } + + // Enable automatic time zome update + sendAT(GF("+CTZU=1")); + if (waitResponse(10000L) != 1) { return false; } - int ret = getSimStatus(); + SimStatus ret = getSimStatus(); // if the sim isn't ready and a pin has been provided, try to unlock the sim if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); - } - // 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(); @@ -241,152 +285,87 @@ 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() { - return true; - } - - bool hasWifi() { - return false; - } - - bool hasGPRS() { - 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 + * Generic network functions */ - -TINY_GSM_MODEM_SIM_UNLOCK_CPIN() - -TINY_GSM_MODEM_GET_SIMCCID_CCID() - - String getIMEI() { - sendAT(GF("+CGSN")); - if (waitResponse(GF(GSM_NL)) != 1) { - return ""; - } - String res = stream.readStringUntil('\n'); + public: + RegStatus getRegistrationStatus() { + // Check first for EPS registration + sendAT(GF("+CEREG?")); + if (waitResponse(GF(GSM_NL "+CEREG:")) != 1) { return REG_UNKNOWN; } + streamSkipUntil(','); /* Skip format (0) */ + int status = streamGetIntBefore('\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")); + // If we're connected on EPS, great! + if ((RegStatus)status == REG_OK_HOME || + (RegStatus)status == REG_OK_ROAMING) { + return (RegStatus)status; + } else { + // Otherwise, check generic network status + sendAT(GF("+CREG?")); + if (waitResponse(GF(GSM_NL "+CREG:")) != 1) { return REG_UNKNOWN; } + streamSkipUntil(','); /* Skip format (0) */ + status = streamGetIntBefore('\n'); waitResponse(); - switch (status) { - case 2: - case 3: return SIM_LOCKED; - case 1: return SIM_READY; - default: return SIM_ERROR; - } + return (RegStatus)status; } - return SIM_ERROR; } -TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG) - -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 @@ -403,14 +382,19 @@ 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++) { + GsmClientSaraR4* sock = sockets[mux]; + if (sock && sock->sock_connected) { sock->sock_connected = false; } + } + // sendAT(GF("+CGACT=0,1")); // Deactivate PDP context 1 sendAT(GF("+CGACT=0")); // Deactivate all contexts if (waitResponse(40000L) != 1) { @@ -418,149 +402,255 @@ 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 + * SIM card 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 ""; - } + protected: + // This uses "CGSN" instead of "GSN" + String getIMEIImpl() { + sendAT(GF("+CGSN")); + if (waitResponse(GF(GSM_NL)) != 1) { return ""; } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); 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; - /* * 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 + * GSM/GPS/GNSS/GLONASS Location functions + * NOTE: u-blox modules use the same function to get location data from both + * GSM tower triangulation and from dedicated GPS/GNSS/GLONASS receivers. The + * only difference in which sensor the data is requested from. If a GNSS + * location is requested from a modem without a GNSS receiver installed on the + * I2C port, the GSM-based "Cell Locate" location will be returned instead. */ - - String getGsmLocation() { - sendAT(GF("+ULOC=2,3,0,120,1")); - if (waitResponse(30000L, GF(GSM_NL "+UULOC:")) != 1) { - return ""; - } + protected: + bool enableGPSImpl() { + // AT+UGPS=[,[,]] + // - 0: GNSS receiver powered off, 1: on + // - 0: no aiding (default) + // - 3: GPS + SBAS (default) + sendAT(GF("+UGPS=1,0,3")); + if (waitResponse(10000L, GF(GSM_NL "+UGPS:")) != 1) { return false; } + return waitResponse(10000L) == 1; + } + bool disableGPSImpl() { + sendAT(GF("+UGPS=0")); + if (waitResponse(10000L, GF(GSM_NL "+UGPS:")) != 1) { return false; } + return waitResponse(10000L) == 1; + } + String inline getUbloxLocationRaw(int8_t sensor) { + // AT+ULOC=,,,, + // - 2: single shot position + // - 0: use the last fix in the internal database and stop the GNSS + // receiver + // - 1: use the GNSS receiver for localization + // - 2: use cellular CellLocate location information + // - 3: ?? use the combined GNSS receiver and CellLocate service + // information ?? - Docs show using sensor 3 and it's + // documented for the +UTIME command but not for +ULOC + // - 0: standard (single-hypothesis) response + // - Timeout period in seconds + // - Target accuracy in meters (1 - 999999) + sendAT(GF("+ULOC=2,"), sensor, GF(",0,120,1")); + // wait for first "OK" + if (waitResponse(10000L) != 1) { return ""; } + // wait for the final result - wait full timeout time + if (waitResponse(120000L, GF(GSM_NL "+UULOC:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); return res; } + String getGsmLocationRawImpl() { + return getUbloxLocationRaw(2); + } + String getGPSrawImpl() { + return getUbloxLocationRaw(1); + } + + inline bool getUbloxLocation(int8_t sensor, float* lat, float* lon, + float* speed = 0, float* alt = 0, int* vsat = 0, + int* usat = 0, float* accuracy = 0, + int* year = 0, int* month = 0, int* day = 0, + int* hour = 0, int* minute = 0, + int* second = 0) { + // AT+ULOC=,,,, + // - 2: single shot position + // - 2: use cellular CellLocate location information + // - 0: use the last fix in the internal database and stop the GNSS + // receiver + // - 1: use the GNSS receiver for localization + // - 3: ?? use the combined GNSS receiver and CellLocate service + // information ?? - Docs show using sensor 3 and it's documented + // for the +UTIME command but not for +ULOC + // - 0: standard (single-hypothesis) response + // - Timeout period in seconds + // - Target accuracy in meters (1 - 999999) + sendAT(GF("+ULOC=2,"), sensor, GF(",0,120,1")); + // wait for first "OK" + if (waitResponse(10000L) != 1) { return false; } + // wait for the final result - wait full timeout time + if (waitResponse(120000L, GF(GSM_NL "+UULOC: ")) != 1) { return false; } + + // +UULOC: ,