diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index a03a7001..a011a11c 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -25,7 +25,7 @@ with your board before submitting any issues. Modem: Main processor board: -TinyGSM version: +TinyGSM version: Code: ### Scenario, steps to reproduce diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..0f88bec1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'daily' + labels: + - 'CI/CD' + commit-message: + prefix: ci diff --git a/.github/workflows/build_examples_platformio.yaml b/.github/workflows/build_examples_platformio.yaml index 88d464dd..1ebee7da 100644 --- a/.github/workflows/build_examples_platformio.yaml +++ b/.github/workflows/build_examples_platformio.yaml @@ -3,10 +3,67 @@ name: Build Examples with PlatformIO # Triggers the workflow on push or pull request events on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: - build: + determine_library_source: + name: determine_library_source runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, 'ci skip')" + outputs: + library_install_zip: ${{ steps.store_vars.outputs.library_install_zip }} + library_install_git: ${{ steps.store_vars.outputs.library_install_git }} + + steps: + - uses: actions/checkout@v4 + + - name: Set environment variables for pushes to any branch + if: github.event_name == 'push' + run: | + echo "Push to commit ${GITHUB_SHA}" + echo "LIBRARY_INSTALL_ZIP=https://github.com/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.zip" >> $GITHUB_ENV + echo "LIBRARY_INSTALL_GIT=https://github.com/${GITHUB_REPOSITORY}.git#${GITHUB_SHA}" >> $GITHUB_ENV + + - name: Set environment variable for PR's from any branch + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.name == github.repository + run: | + echo "Pull Request from the ${GITHUB_HEAD_REF} branch" + echo "LIBRARY_INSTALL_ZIP=https://github.com/${GITHUB_REPOSITORY}/archive/${GITHUB_HEAD_REF}.zip" >> $GITHUB_ENV + echo "LIBRARY_INSTALL_GIT=https://github.com/${GITHUB_REPOSITORY}.git#${GITHUB_HEAD_REF}" >> $GITHUB_ENV + + - name: Set environment variable for PR's from any fork + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.name != github.repository + run: | + echo "Pull Request from the fork ${{ github.event.pull_request.head.repo.full_name }} at ${{ github.event.pull_request.head.ref }}" + echo "LIBRARY_INSTALL_ZIP=https://github.com/${{ github.event.pull_request.head.repo.full_name }}/archive/${{ github.event.pull_request.head.ref }}.zip" >> $GITHUB_ENV + echo "LIBRARY_INSTALL_GIT=https://github.com/${{ github.event.pull_request.head.repo.full_name }}.git#${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV + + - name: store enviroment variables as output + id: store_vars + run: | + echo "library_install_zip=${{ env.LIBRARY_INSTALL_ZIP }}" >> $GITHUB_OUTPUT + echo "library_install_git=${{ env.LIBRARY_INSTALL_GIT }}" >> $GITHUB_OUTPUT + + print_library_source: + name: print_library_source + runs-on: ubuntu-latest + needs: determine_library_source + steps: + - name: Check the library installation source + run: | + echo "Link to zip for Arduino CLI testing install:" + echo "${{ needs.determine_library_source.outputs.library_install_zip }}" + echo + echo "Git reference for PlatformIO testing install" + echo "${{ needs.determine_library_source.outputs.library_install_git }}" + + build_pio: + runs-on: ubuntu-latest + needs: [determine_library_source] + env: + LIBRARY_INSTALL_GIT: ${{ needs.determine_library_source.outputs.library_install_git }} strategy: matrix: @@ -23,6 +80,7 @@ jobs: [ TINY_GSM_MODEM_A6, TINY_GSM_MODEM_BG96, + TINY_GSM_MODEM_A7672X, TINY_GSM_MODEM_ESP8266, TINY_GSM_MODEM_M95, TINY_GSM_MODEM_M590, @@ -36,12 +94,13 @@ jobs: TINY_GSM_MODEM_SIM7070, TINY_GSM_MODEM_UBLOX, TINY_GSM_MODEM_SARAR4, + TINY_GSM_MODEM_SARAR5, TINY_GSM_MODEM_XBEE, TINY_GSM_MODEM_SEQUANS_MONARCH, ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set variables run: | @@ -54,7 +113,9 @@ jobs: fi - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 + with: + python-version: '3.x' # This should be pulled from cache, if there's not a new version - name: Install PlatformIO @@ -62,8 +123,8 @@ jobs: python -m pip install --upgrade pip pip install --upgrade platformio - - name: Restore or Cache Platforms and Libraries - uses: actions/cache@v2.1.5 + - name: Restore or Cache PlatformIO Platforms and Libraries + uses: actions/cache@v4 id: cache_pio with: path: ~/.platformio @@ -73,7 +134,7 @@ jobs: key: ${{ hashFiles('./examples/example_dependencies.json') }} # Install cores and library dependencies for the Arduino CLI, iff no cache - - name: Install the Arduino libraries + - name: Install the Arduino libraries for PlatformIO if: steps.cache_pio.outputs.cache-hit != 'true' run: pio lib --global install 89 415 1202 1286 @@ -82,8 +143,8 @@ jobs: PLATFORMIO_CI_SRC: ${{ matrix.example }} TINYGSM_MODEM_TO_USE: ${{ matrix.modem }} run: | - echo "${{ env.LIBRARY_INSTALL_SOURCE }}" - pio lib --global install ${{ env.LIBRARY_INSTALL_SOURCE }} + echo "${{ env.LIBRARY_INSTALL_GIT }}" + pio pkg install -g --library ${{ env.LIBRARY_INSTALL_GIT }} sed -i 's/\/\/ #define TINY_GSM_MODEM_SIM800/#define TINY_GSM_MODEM_SIM800/g' ${{ matrix.example }}/* platformio ci --project-option='build_flags=-D ${{ env.TINYGSM_MODEM_TO_USE }}' --project-option='framework=arduino' --board=uno --board=leonardo --board=yun --board=megaatmega2560 --board=genuino101 --board=mkr1000USB --board=zero --board=teensy31 --board=bluepill_f103c8 --board=uno_pic32 --board=esp01 --board=nodemcuv2 --board=esp32dev pio lib --global uninstall TinyGSM diff --git a/.gitignore b/.gitignore index 2b40655e..c6417bab 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,6 @@ extras/At Command Manuals - Unsupported/* filters/* platformio_extra_envs.ini *.tar.gz +pioScripts/generate_compile_commands.py +compile_commands.json +src/TinyGsmATDefines.h diff --git a/ChangeLog.md b/ChangeLog.md new file mode 100644 index 00000000..474ca1c2 --- /dev/null +++ b/ChangeLog.md @@ -0,0 +1,60 @@ +# ChangeLog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and its stricter, better defined, brother [Common Changelog](https://common-changelog.org/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +*** + + +## [Unreleased] +**** +### Changed + +### Added + +### Removed + +### Fixed + +*** + + +## [v0.12.0] + +### Changed +- Moved bulk of waitResponse function to modem template and gave modems handleURCs fxn +- Moved option in waitResponse for verbose outputs. +- setBaud now returns a bool +- Replace new line consts with defines and renamed to AT_NL +- Renamed all RegStatus enums to be unique +- Replaced `NULL` with `nullptr` and replaced c-style casts. +- Moved setCertificates function and the certificate name matrix to the SSL template. +- Changed inputs for (unimplemented) SSL certificate functions. +- All modems will now return the pre-defined manufacturer and model in the name if the function to get the internal name fails. +- Cleaned up code for getting modem names. +- Made battery return types signed. + +### Added +- Added support for SSL for the Quentcel BG95 and BG96 from [Aurelien BOUIN](https://github.com/aurelihein) and [George O'Connor](https://github.com/georgeman93) +- Added support for UBLOX SARA-R5 from [Sebastian Bergner](https://github.com/sebastianbergner) +- Added support for SIMCOM A7672X from [Giovanni de Rosso Unruh](https://github.com/giovannirosso) +- Added SIM5320 GPS location from [Bengarman](https://github.com/Bengarman) +- Added functions `getModemSerialNumber`, `getModemModel`, and `getModemRevision`. +- Added deep debugging option +- Added documentation to the FIFO class + +### Removed +- Removed non-functional factory reset from SIM70xx series + +### Fixed +- Removed extra wait on SIM7000 from [Mikael Fredriksson](https://github.com/Gelemikke) +- Fix status returns on ESP8266/ESP32 AT commands +- Fix length of HEX for Sequans Monarch +- Fix SIM7600 password then user when cid is set from [github0013](https://github.com/github0013) +- Fix cardinal points in location by gps for SIM7600 from [Juxn3](https://github.com/Juxn3) +- Fix NTP server sync for SIM70xx models from [Gonzalo Brusco](https://github.com/gonzabrusco) +- Fixed SIM70xx inheritance + +*** diff --git a/README.md b/README.md index c71cc1ca..4b45b8e8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct.svg)](https://vshymanskyy.github.io/StandWithUkraine) + ![TinyGSM logo](https://cdn.rawgit.com/vshymanskyy/TinyGSM/d18e93dc51fe988a0b175aac647185457ef640b5/extras/logo.svg) A small Arduino library for GSM modules, that just works. @@ -15,7 +17,7 @@ If you like **TinyGSM** - give it a star, or fork it and contribute! [![GitHub forks](https://img.shields.io/github/forks/vshymanskyy/TinyGSM.svg?style=social&label=Fork)](https://github.com/vshymanskyy/TinyGSM/network) You can also join our chat: -[![Gitter](https://img.shields.io/gitter/room/vshymanskyy/TinyGSM.svg)](https://gitter.im/tinygsm) +[![Gitter](https://img.shields.io/gitter/room/vshymanskyy/TinyGSM.svg)](https://app.gitter.im/#/room/#tinygsm_Lobby:gitter.im) - [Supported modems](#supported-modems) - [Supported boards/modules](#supported-boardsmodules) @@ -27,7 +29,7 @@ You can also join our chat: - [How does it work?](#how-does-it-work) - [API Reference](#api-reference) - [Troubleshooting](#troubleshooting) - - [Ensure stable data & power connection](#ensure-stable-data--power-connection) + - [Ensure stable data \& power connection](#ensure-stable-data--power-connection) - [Baud rates](#baud-rates) - [Broken initial configuration](#broken-initial-configuration) - [Failed connection or no data received](#failed-connection-or-no-data-received) @@ -68,34 +70,32 @@ TinyGSM also pulls data gently from the modem (whenever possible), so it can ope - SIMCom LTE Modules (SIM7100E, SIM7500E, SIM7500A, SIM7600C, SIM7600E) - SIMCom SIM7000E/A/G CAT-M1/NB-IoT Module - SIMCom SIM7070/SIM7080/SIM7090 CAT-M1/NB-IoT Module +- SIMCom A7672X CAT-M1 Module - AI-Thinker A6, A6C, A7, A20 - ESP8266/ESP32 (AT commands interface, similar to GSM modems) - Digi XBee WiFi and Cellular (using XBee command mode) - 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/NB-IoT Modems (SARA-R4xx, SARA-N4xx, _but NOT SARA-N2xx_) +- u-blox LTE-M/NB-IoT Modems (SARA-R4xx, SARA-N4xx, SARA-R5xx, _but NOT SARA-N2xx_) - Sequans Monarch LTE Cat M1/NB1 (VZM20Q) - Quectel BG96 +- Quectel BG95 - Quectel M95 - Quectel MC60 ***(alpha)*** ### Supported boards/modules +- EnviroDIY LTE Bee, WiFi Bee - Arduino MKR GSM 1400 -- GPRSbee +- Sodaq GPRSbee, uBee - Microduino GSM -- Adafruit FONA (Mini Cellular GSM Breakout) -- Adafruit FONA 800/808 Shield +- Adafruit FONA Mini Cellular GSM Breakout, 800/808 Shield, FONA 3G - Industruino GSM +- Dragino NB-IoT Bee +- Digi XBee S6B, XBee LTE Cat 1, XBee3 LTE Cat 1, XBee3 CatM +- Nimbelink Skywire/Airgain NL-SW-LTE-QBG96, NL-SW-LTE-QBG95 - RAK WisLTE ***(alpha)*** - ... other modules, based on supported modems. Some boards require [**special configuration**](https://github.com/vshymanskyy/TinyGSM/wiki/Board-configuration). -More modems may be supported later: -- [ ] Quectel M10, UG95 -- [ ] SIMCom SIM7020 -- [ ] Telit GL865 -- [ ] ZTE MG2639 -- [ ] Hi-Link HLK-RM04 - Watch this repo for new updates! And of course, contributions are welcome ;) ## Features @@ -108,6 +108,7 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - ESP8266 - 5 - Neoway M590 - 2 - Quectel BG96 - 12 + - Quectel BG95 - 12 - Quectel M95 - 6 - Quectel MC60/MC60E - 6 - Sequans Monarch - 6 @@ -116,6 +117,7 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - SIM7000 - 8 possible without SSL, only 2 with - SIM 7070/7080/7090 - 12 - SIM 7500/7600/7800 - 10 + - SIM A7672X - 10 - u-blox 2G/3G - 7 - u-blox SARA R4/N4 - 7 - Digi XBee - _only 1 connection supported!_ @@ -123,10 +125,10 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - Not yet supported on any module, though it may be some day - SSL/TLS (HTTPS) - Supported on: - - SIM800, SIM7000, u-Blox, XBee _cellular_, ESP8266, and Sequans Monarch + - SIM800, SIM7000, A7672X, u-Blox, XBee _cellular_, ESP8266, Sequans Monarch and Quectel BG95 and BG96 - Note: **only some device models or firmware revisions have this feature** (SIM8xx R14.18, A7, etc.) - Not yet supported on: - - Quectel modems, SIM 5360/5320/7100, SIM 7500/7600/7800 + - SIM 5360/5320/7100, SIM 7500/7600/7800 - Not possible on: - SIM900, A6/A7, Neoway M590, XBee _WiFi_ - Like TCP, most modules support simultaneous connections @@ -149,14 +151,14 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - Not yet supported on: - SIM7000, SIM5360/5320/7100, SIM7500/7800, VZM20Q (Monarch) - Not possible on: - - XBee (any type), u-blox SARA R4/N4, Neoway M590, ESP8266 (obviously) + - XBee (any type), u-blox SARA R4/R5/N4, Neoway M590, ESP8266 (obviously) - Functions: - Dial, hangup - DTMF sending **Location** - GPS/GNSS - - SIM808, SIM7000, SIM7500/7600/7800, BG96, u-blox + - SIM808, SIM7000, SIM7500/7600/7800, BG96, BG95, u-blox - NOTE: u-blox chips do _NOT_ have embedded GPS - this functionality only works if a secondary GPS is connected to primary cellular chip over I2C - GSM location service - SIM800, SIM7000, Quectel, u-blox @@ -174,6 +176,10 @@ Watch this repo for new updates! And of course, contributions are welcome ;) - [V1pr](https://github.com/V1pr) - Quectel M95 - [replicadeltd](https://github.com/replicadeltd) +- UBLOX SARA-R5 + - [Sebastian Bergner](https://github.com/sebastianbergner) +- SIMCOM A7672X + - [Giovanni de Rosso Unruh](https://github.com/giovannirosso) - Other Contributors: - https://github.com/vshymanskyy/TinyGSM/graphs/contributors diff --git a/examples/AllFunctions/AllFunctions.ino b/examples/AllFunctions/AllFunctions.ino index b3e6ff6b..df183fc2 100644 --- a/examples/AllFunctions/AllFunctions.ino +++ b/examples/AllFunctions/AllFunctions.ino @@ -19,9 +19,12 @@ // #define TINY_GSM_MODEM_SIM7080 // #define TINY_GSM_MODEM_SIM5360 // #define TINY_GSM_MODEM_SIM7600 +// #define TINY_GSM_MODEM_A7672X // #define TINY_GSM_MODEM_UBLOX // #define TINY_GSM_MODEM_SARAR4 +// #define TINY_GSM_MODEM_SARAR5 // #define TINY_GSM_MODEM_M95 +// #define TINY_GSM_MODEM_BG95 // #define TINY_GSM_MODEM_BG96 // #define TINY_GSM_MODEM_A6 // #define TINY_GSM_MODEM_A7 @@ -29,6 +32,7 @@ // #define TINY_GSM_MODEM_MC60 // #define TINY_GSM_MODEM_MC60E // #define TINY_GSM_MODEM_ESP8266 +// #define TINY_GSM_MODEM_ESP32 // #define TINY_GSM_MODEM_XBEE // #define TINY_GSM_MODEM_SEQUANS_MONARCH @@ -69,17 +73,17 @@ SoftwareSerial SerialAT(2, 3); // RX, TX #define TINY_GSM_TEST_WIFI false #define TINY_GSM_TEST_TCP true #define TINY_GSM_TEST_SSL true -#define TINY_GSM_TEST_CALL false -#define TINY_GSM_TEST_SMS false -#define TINY_GSM_TEST_USSD false +#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_TEMPERATURE true -#define TINY_GSM_TEST_GSM_LOCATION false -#define TINY_GSM_TEST_NTP false -#define TINY_GSM_TEST_TIME false -#define TINY_GSM_TEST_GPS false +#define TINY_GSM_TEST_GSM_LOCATION true +#define TINY_GSM_TEST_GPS true +#define TINY_GSM_TEST_NTP true +#define TINY_GSM_TEST_TIME true // disconnect and power down modem after tests -#define TINY_GSM_POWERDOWN false +#define TINY_GSM_POWERDOWN true // set GSM PIN, if any #define GSM_PIN "" @@ -135,7 +139,7 @@ void setup() { // !!!!!!!!!!! DBG("Wait..."); - delay(6000); + delay(6000L); // Set GSM module baud rate TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX); @@ -154,18 +158,32 @@ void loop() { return; } + String modemInfo = modem.getModemInfo(); + DBG("Modem Info:", modemInfo); + String name = modem.getModemName(); DBG("Modem Name:", name); - String modemInfo = modem.getModemInfo(); - DBG("Modem Info:", modemInfo); + String manufacturer = modem.getModemManufacturer(); + DBG("Modem Manufacturer:", manufacturer); + + String hw_ver = modem.getModemModel(); + DBG("Modem Hardware Version:", hw_ver); + + String fv_ver = modem.getModemRevision(); + DBG("Modem Firware Version:", fv_ver); + +#if not defined(TINY_GSM_MODEM_ESP8266) && not defined(TINY_GSM_MODEM_ESP32) + String mod_sn = modem.getModemSerialNumber(); + DBG("Modem Serial Number (may be SIM CCID):", mod_sn); +#endif #if TINY_GSM_TEST_GPRS // Unlock your SIM card with a PIN if needed if (GSM_PIN && modem.getSimStatus() != 3) { modem.simUnlock(GSM_PIN); } #endif -#if TINY_GSM_TEST_WIFI +#if TINY_GSM_TEST_WIFI && defined(TINY_GSM_MODEM_HAS_WIFI) DBG("Setting SSID/password..."); if (!modem.networkConnect(wifiSSID, wifiPass)) { DBG(" fail"); @@ -175,7 +193,7 @@ void loop() { SerialMon.println(" success"); #endif -#if TINY_GSM_TEST_GPRS && defined TINY_GSM_MODEM_XBEE +#if TINY_GSM_TEST_GPRS && defined(TINY_GSM_MODEM_XBEE) // The XBee must run the gprsConnect function BEFORE waiting for network! modem.gprsConnect(apn, gprsUser, gprsPass); #endif @@ -210,6 +228,9 @@ void loop() { String cop = modem.getOperator(); DBG("Operator:", cop); + // String prov = modem.getProvider(); + // DBG("Provider:", prov); + IPAddress local = modem.localIP(); DBG("Local IP:", local); @@ -265,6 +286,7 @@ void loop() { #endif #if TINY_GSM_TEST_SSL && defined TINY_GSM_MODEM_HAS_SSL + // TODO: Add test of adding certificcate TinyGsmClientSecure secureClient(modem, 1); const int securePort = 443; DBG("Connecting securely to", server); @@ -303,8 +325,8 @@ void loop() { } #endif -#if TINY_GSM_TEST_CALL && defined TINY_GSM_MODEM_HAS_CALLING && \ - 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 @@ -327,6 +349,7 @@ void loop() { } #endif +// Test the SMS functions #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"); @@ -343,24 +366,27 @@ void loop() { #endif +// Test the GSM location functions #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; + float gsm_latitude = 0; + float gsm_longitude = 0; + float gsm_accuracy = 0; + int gsm_year = 0; + int gsm_month = 0; + int gsm_day = 0; + int gsm_hour = 0; + int gsm_minute = 0; + int gsm_second = 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); + if (modem.getGsmLocation(&gsm_latitude, &gsm_longitude, &gsm_accuracy, + &gsm_year, &gsm_month, &gsm_day, &gsm_hour, + &gsm_minute, &gsm_second)) { + DBG("Latitude:", String(gsm_latitude, 8), + "\tLongitude:", String(gsm_longitude, 8)); + DBG("Accuracy:", gsm_accuracy); + DBG("Year:", gsm_year, "\tMonth:", gsm_month, "\tDay:", gsm_day); + DBG("Hour:", gsm_hour, "\tMinute:", gsm_minute, "\tSecond:", gsm_second); break; } else { DBG("Couldn't get GSM location, retrying in 15s."); @@ -372,33 +398,38 @@ void loop() { DBG("GSM Based Location String:", location); #endif +// Test the GPS functions #if TINY_GSM_TEST_GPS && defined TINY_GSM_MODEM_HAS_GPS DBG("Enabling GPS/GNSS/GLONASS and waiting 15s for warm-up"); +#if !defined(TINY_GSM_MODEM_SARAR5) // not needed for this module modem.enableGPS(); +#endif 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; + float gps_latitude = 0; + float gps_longitude = 0; + float gps_speed = 0; + float gps_altitude = 0; + int gps_vsat = 0; + int gps_usat = 0; + float gps_accuracy = 0; + int gps_year = 0; + int gps_month = 0; + int gps_day = 0; + int gps_hour = 0; + int gps_minute = 0; + int gps_second = 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); + if (modem.getGPS(&gps_latitude, &gps_longitude, &gps_speed, &gps_altitude, + &gps_vsat, &gps_usat, &gps_accuracy, &gps_year, &gps_month, + &gps_day, &gps_hour, &gps_minute, &gps_second)) { + DBG("Latitude:", String(gps_latitude, 8), + "\tLongitude:", String(gps_longitude, 8)); + DBG("Speed:", gps_speed, "\tAltitude:", gps_altitude); + DBG("Visible Satellites:", gps_vsat, "\tUsed Satellites:", gps_usat); + DBG("Accuracy:", gps_accuracy); + DBG("Year:", gps_year, "\tMonth:", gps_month, "\tDay:", gps_day); + DBG("Hour:", gps_hour, "\tMinute:", gps_minute, "\tSecond:", gps_second); break; } else { DBG("Couldn't get GPS/GNSS/GLONASS location, retrying in 15s."); @@ -407,31 +438,34 @@ void loop() { } DBG("Retrieving GPS/GNSS/GLONASS location again as a string"); String gps_raw = modem.getGPSraw(); +#if !defined(TINY_GSM_MODEM_SARAR5) // not available for this module DBG("GPS/GNSS Based Location String:", gps_raw); DBG("Disabling GPS"); modem.disableGPS(); #endif +#endif +// Test the Network time functions #if TINY_GSM_TEST_NTP && defined TINY_GSM_MODEM_HAS_NTP DBG("Asking modem to sync with NTP"); - modem.NTPServerSync("132.163.96.5", 20); + modem.NTPServerSync("pool.ntp.org", 20); #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; + int ntp_year = 0; + int ntp_month = 0; + int ntp_day = 0; + int ntp_hour = 0; + int ntp_min = 0; + int ntp_sec = 0; + float ntp_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); + if (modem.getNetworkTime(&ntp_year, &ntp_month, &ntp_day, &ntp_hour, + &ntp_min, &ntp_sec, &ntp_timezone)) { + DBG("Year:", ntp_year, "\tMonth:", ntp_month, "\tDay:", ntp_day); + DBG("Hour:", ntp_hour, "\tMinute:", ntp_min, "\tSecond:", ntp_sec); + DBG("Timezone:", ntp_timezone); break; } else { DBG("Couldn't get network time, retrying in 15s."); @@ -443,16 +477,18 @@ void loop() { DBG("Current Network Time:", time); #endif +// Test Battery functions #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); + int8_t chargeState = -99; + int8_t chargePercent = -99; + int16_t milliVolts = -9999; + modem.getBattStats(chargeState, chargePercent, milliVolts); DBG("Battery charge state:", chargeState); - DBG("Battery charge 'percent':", percent); + DBG("Battery charge 'percent':", chargePercent); DBG("Battery voltage:", milliVolts / 1000.0F); #endif +// Test temperature functions #if TINY_GSM_TEST_TEMPERATURE && defined TINY_GSM_MODEM_HAS_TEMPERATURE float temp = modem.getTemperature(); DBG("Chip temperature:", temp); diff --git a/examples/BlynkClient/BlynkClient.ino b/examples/BlynkClient/BlynkClient.ino index bd4885d8..6be1eb3f 100644 --- a/examples/BlynkClient/BlynkClient.ino +++ b/examples/BlynkClient/BlynkClient.ino @@ -22,7 +22,11 @@ * Change GPRS apm, user, pass, and Blynk auth token to run :) **************************************************************/ -#define BLYNK_PRINT Serial // Comment this out to disable prints and save space +#define BLYNK_TEMPLATE_ID "TMPxxxxxx" +#define BLYNK_TEMPLATE_NAME "Device" +#define BLYNK_AUTH_TOKEN "YourAuthToken" + +#define BLYNK_PRINT Serial // Comment this out to disable prints and save space // Default heartbeat interval for GSM is 60 // If you want override this value, uncomment and set this option: @@ -38,9 +42,12 @@ // #define TINY_GSM_MODEM_SIM7080 // #define TINY_GSM_MODEM_SIM5360 // #define TINY_GSM_MODEM_SIM7600 +// #define TINY_GSM_MODEM_A7672X // #define TINY_GSM_MODEM_UBLOX // #define TINY_GSM_MODEM_SARAR4 +// #define TINY_GSM_MODEM_SARAR5 // #define TINY_GSM_MODEM_M95 +// #define TINY_GSM_MODEM_BG95 // #define TINY_GSM_MODEM_BG96 // #define TINY_GSM_MODEM_A6 // #define TINY_GSM_MODEM_A7 @@ -48,6 +55,7 @@ // #define TINY_GSM_MODEM_MC60 // #define TINY_GSM_MODEM_MC60E // #define TINY_GSM_MODEM_ESP8266 +// #define TINY_GSM_MODEM_ESP32 // #define TINY_GSM_MODEM_XBEE // #define TINY_GSM_MODEM_SEQUANS_MONARCH @@ -79,8 +87,7 @@ const char auth[] = "YourAuthToken"; TinyGsm modem(SerialAT); -void setup() -{ +void setup() { // Set console baud rate SerialMon.begin(115200); delay(10); @@ -99,12 +106,11 @@ void setup() SerialMon.println(modemInfo); // Unlock your SIM card with a PIN - //modem.simUnlock("1234"); + // modem.simUnlock("1234"); Blynk.begin(auth, modem, apn, user, pass); } -void loop() -{ +void loop() { Blynk.run(); } diff --git a/examples/FileDownload/FileDownload.ino b/examples/FileDownload/FileDownload.ino index d66e4b7d..8d102e5c 100644 --- a/examples/FileDownload/FileDownload.ino +++ b/examples/FileDownload/FileDownload.ino @@ -23,9 +23,12 @@ // #define TINY_GSM_MODEM_SIM7080 // #define TINY_GSM_MODEM_SIM5360 // #define TINY_GSM_MODEM_SIM7600 +// #define TINY_GSM_MODEM_A7672X // #define TINY_GSM_MODEM_UBLOX // #define TINY_GSM_MODEM_SARAR4 +// #define TINY_GSM_MODEM_SARAR5 // #define TINY_GSM_MODEM_M95 +// #define TINY_GSM_MODEM_BG95 // #define TINY_GSM_MODEM_BG96 // #define TINY_GSM_MODEM_A6 // #define TINY_GSM_MODEM_A7 @@ -33,6 +36,7 @@ // #define TINY_GSM_MODEM_MC60 // #define TINY_GSM_MODEM_MC60E // #define TINY_GSM_MODEM_ESP8266 +// #define TINY_GSM_MODEM_ESP32 // #define TINY_GSM_MODEM_XBEE // #define TINY_GSM_MODEM_SEQUANS_MONARCH diff --git a/examples/HttpClient/HttpClient.ino b/examples/HttpClient/HttpClient.ino index 3e4659bf..f3d2e01c 100644 --- a/examples/HttpClient/HttpClient.ino +++ b/examples/HttpClient/HttpClient.ino @@ -28,9 +28,12 @@ // #define TINY_GSM_MODEM_SIM7080 // #define TINY_GSM_MODEM_SIM5360 // #define TINY_GSM_MODEM_SIM7600 +// #define TINY_GSM_MODEM_A7672X // #define TINY_GSM_MODEM_UBLOX // #define TINY_GSM_MODEM_SARAR4 +// #define TINY_GSM_MODEM_SARAR5 // #define TINY_GSM_MODEM_M95 +// #define TINY_GSM_MODEM_BG95 // #define TINY_GSM_MODEM_BG96 // #define TINY_GSM_MODEM_A6 // #define TINY_GSM_MODEM_A7 @@ -38,6 +41,7 @@ // #define TINY_GSM_MODEM_MC60 // #define TINY_GSM_MODEM_MC60E // #define TINY_GSM_MODEM_ESP8266 +// #define TINY_GSM_MODEM_ESP32 // #define TINY_GSM_MODEM_XBEE // #define TINY_GSM_MODEM_SEQUANS_MONARCH diff --git a/examples/HttpsClient/HttpsClient.ino b/examples/HttpsClient/HttpsClient.ino index 33092f20..95a107b2 100644 --- a/examples/HttpsClient/HttpsClient.ino +++ b/examples/HttpsClient/HttpsClient.ino @@ -30,6 +30,7 @@ // #define TINY_GSM_MODEM_UBLOX // #define TINY_GSM_MODEM_SARAR4 // #define TINY_GSM_MODEM_ESP8266 +// #define TINY_GSM_MODEM_ESP32 // #define TINY_GSM_MODEM_XBEE // #define TINY_GSM_MODEM_SEQUANS_MONARCH diff --git a/examples/MqttClient/MqttClient.ino b/examples/MqttClient/MqttClient.ino index 77a85008..797a52c3 100644 --- a/examples/MqttClient/MqttClient.ino +++ b/examples/MqttClient/MqttClient.ino @@ -32,9 +32,12 @@ // #define TINY_GSM_MODEM_SIM7080 // #define TINY_GSM_MODEM_SIM5360 // #define TINY_GSM_MODEM_SIM7600 +// #define TINY_GSM_MODEM_A7672X // #define TINY_GSM_MODEM_UBLOX // #define TINY_GSM_MODEM_SARAR4 +// #define TINY_GSM_MODEM_SARAR5 // #define TINY_GSM_MODEM_M95 +// #define TINY_GSM_MODEM_BG95 // #define TINY_GSM_MODEM_BG96 // #define TINY_GSM_MODEM_A6 // #define TINY_GSM_MODEM_A7 @@ -42,6 +45,7 @@ // #define TINY_GSM_MODEM_MC60 // #define TINY_GSM_MODEM_MC60E // #define TINY_GSM_MODEM_ESP8266 +// #define TINY_GSM_MODEM_ESP32 // #define TINY_GSM_MODEM_XBEE // #define TINY_GSM_MODEM_SEQUANS_MONARCH diff --git a/examples/WebClient/WebClient.ino b/examples/WebClient/WebClient.ino index 74ffa0a8..3cb27032 100644 --- a/examples/WebClient/WebClient.ino +++ b/examples/WebClient/WebClient.ino @@ -18,9 +18,12 @@ // #define TINY_GSM_MODEM_SIM7080 // #define TINY_GSM_MODEM_SIM5360 // #define TINY_GSM_MODEM_SIM7600 +// #define TINY_GSM_MODEM_A7672X // #define TINY_GSM_MODEM_UBLOX // #define TINY_GSM_MODEM_SARAR4 +// #define TINY_GSM_MODEM_SARAR5 // #define TINY_GSM_MODEM_M95 +// #define TINY_GSM_MODEM_BG95 // #define TINY_GSM_MODEM_BG96 // #define TINY_GSM_MODEM_A6 // #define TINY_GSM_MODEM_A7 @@ -28,6 +31,7 @@ // #define TINY_GSM_MODEM_MC60 // #define TINY_GSM_MODEM_MC60E // #define TINY_GSM_MODEM_ESP8266 +// #define TINY_GSM_MODEM_ESP32 // #define TINY_GSM_MODEM_XBEE // #define TINY_GSM_MODEM_SEQUANS_MONARCH @@ -117,6 +121,10 @@ TinyGsm modem(debugger); TinyGsm modem(SerialAT); #endif +#if defined(TINY_GSM_MODEM_HAS_SSL) +#define USE_SSL +#endif + #ifdef USE_SSL TinyGsmClientSecure client(modem); const int port = 443; diff --git a/examples/more/SIM800_SslSetCert/SIM800_SslSetCert.ino b/examples/more/SIM800_SslSetCert/SIM800_SslSetCert.ino index eef42076..cefb37cc 100644 --- a/examples/more/SIM800_SslSetCert/SIM800_SslSetCert.ino +++ b/examples/more/SIM800_SslSetCert/SIM800_SslSetCert.ino @@ -32,11 +32,11 @@ #ifdef DUMP_AT_COMMANDS - #include - StreamDebugger debugger(SerialAT, SerialMon); - TinyGsm modem(debugger); +#include +StreamDebugger debugger(SerialAT, SerialMon); +TinyGsm modem(debugger); #else - TinyGsm modem(SerialAT); +TinyGsm modem(SerialAT); #endif void setup() { @@ -57,23 +57,21 @@ void setup() { const int cert_size = sizeof(cert); modem.sendAT(GF("+FSWRITE=" CERT_FILE ",0,"), cert_size, GF(",10")); - if (modem.waitResponse(GF(">")) != 1) { - return; - } + if (modem.waitResponse(GF(">")) != 1) { return; } for (int i = 0; i < cert_size; i++) { char c = pgm_read_byte(&cert[i]); modem.stream.write(c); } - modem.stream.write(GSM_NL); + modem.stream.write(AT_NL); modem.stream.flush(); if (modem.waitResponse(2000) != 1) return; modem.sendAT(GF("+SSLSETCERT=\"" CERT_FILE "\"")); if (modem.waitResponse() != 1) return; - if (modem.waitResponse(5000L, GF(GSM_NL "+SSLSETCERT:")) != 1) return; + if (modem.waitResponse(5000L, GF(AT_NL "+SSLSETCERT:")) != 1) return; const int retCode = modem.stream.readStringUntil('\n').toInt(); @@ -86,11 +84,7 @@ void setup() { } void loop() { - if (SerialAT.available()) { - SerialMon.write(SerialAT.read()); - } - if (SerialMon.available()) { - SerialAT.write(SerialMon.read()); - } + if (SerialAT.available()) { SerialMon.write(SerialAT.read()); } + if (SerialMon.available()) { SerialAT.write(SerialMon.read()); } delay(0); } diff --git a/extras/logo.svg b/extras/logo.svg index 988fcedd..90a6d35b 100644 --- a/extras/logo.svg +++ b/extras/logo.svg @@ -5,8 +5,8 @@ width="500" height="80" > - _____ _____ _____ _____ - | | |\ | \_/ | ___ |_____ | | | - | | | \| | |_____| _____|| | | + _____ _____ _____ _____ + | | |\ | \_/ | ___ |_____ | | | + | | | \| | |_____| _____|| | | diff --git a/library.json b/library.json index 06f41728..031a15fd 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name": "TinyGSM", - "version": "0.11.5", + "version": "0.12.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", + "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, BG95, ublox, Quectel, SIMCOM, AI Thinker, LTE, LTE-M", "authors": [ { "name": "Volodymyr Shymanskyy", diff --git a/library.properties b/library.properties index 1792c675..ef295746 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TinyGSM -version=0.11.5 +version=0.12.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 index a6626829..767ae789 100644 --- a/src/TinyGsmBattery.tpp +++ b/src/TinyGsmBattery.tpp @@ -15,21 +15,62 @@ template class TinyGsmBattery { + /* =========================================== */ + /* =========================================== */ + /* + * Define the interface + */ public: /* * Battery functions */ - uint16_t getBattVoltage() { + + /** + * @brief Get the current battery voltage. + * + * @note Unless you have a battery directly connected to your modem module, + * this will be the input voltage going to the module from your main processor + * board, not the battery voltage of your main processor. + * + * @return *int16_t* The battery voltage measured by the modem module. + */ + int16_t getBattVoltage() { return thisModem().getBattVoltageImpl(); } + + /** + * @brief Get the current battery percent. + * + * @note Unless you have a battery directly connected to your modem module, + * this will be the percent from the input voltage going to the module from + * your main processor board, not the battery percent of your main processor. + * + * @return *int8_t* The current battery percent. + */ int8_t getBattPercent() { return thisModem().getBattPercentImpl(); } - uint8_t getBattChargeState() { + + /** + * @brief Get the battery charging state. + * + * @return *int8_t* The battery charge state. + */ + int8_t getBattChargeState() { return thisModem().getBattChargeStateImpl(); } - bool getBattStats(uint8_t& chargeState, int8_t& percent, - uint16_t& milliVolts) { + + /** + * @brief Get the all battery state + * + * @param chargeState A reference to an int to set to the battery charge state + * @param percent A reference to an int to set to the battery percent + * @param milliVolts A reference to an int to set to the battery voltage + * @return *true* The battery stats were updated by the module. + * @return *false* There was a failure in updating the battery stats from the + * module. + */ + bool getBattStats(int8_t& chargeState, int8_t& percent, int16_t& milliVolts) { return thisModem().getBattStatsImpl(chargeState, percent, milliVolts); } @@ -43,13 +84,20 @@ class TinyGsmBattery { inline modemType& thisModem() { return static_cast(*this); } + ~TinyGsmBattery() {} + + /* =========================================== */ + /* =========================================== */ + /* + * Define the default function implementations + */ /* * Battery functions */ protected: // Use: float vBatt = modem.getBattVoltage() / 1000.0; - uint16_t getBattVoltageImpl() { + int16_t getBattVoltageImpl() { thisModem().sendAT(GF("+CBC")); if (thisModem().waitResponse(GF("+CBC:")) != 1) { return 0; } thisModem().streamSkipUntil(','); // Skip battery charge status @@ -72,7 +120,7 @@ class TinyGsmBattery { return res; } - uint8_t getBattChargeStateImpl() { + int8_t getBattChargeStateImpl() { thisModem().sendAT(GF("+CBC")); if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; } // Read battery charge status @@ -82,8 +130,8 @@ class TinyGsmBattery { return res; } - bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, - uint16_t& milliVolts) { + bool getBattStatsImpl(int8_t& chargeState, int8_t& percent, + int16_t& milliVolts) { thisModem().sendAT(GF("+CBC")); if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; } chargeState = thisModem().streamGetIntBefore(','); diff --git a/src/TinyGsmBluetooth.tpp b/src/TinyGsmBluetooth.tpp index ca1cb4ee..b3913d93 100644 --- a/src/TinyGsmBluetooth.tpp +++ b/src/TinyGsmBluetooth.tpp @@ -15,23 +15,59 @@ template class TinyGsmBluetooth { + /* =========================================== */ + /* =========================================== */ + /* + * Define the interface + */ public: /* * Bluetooth functions */ + + /** + * @brief Enable module Bluetooth + * + * @return *true* Bluetooth was succcessfully enabled + * @return *false* Bluetooth failed to enable + */ bool enableBluetooth() { return thisModem().enableBluetoothImpl(); } + + /** + * @brief Disable module Bluetooth + * + * @return *true* Bluetooth was succcessfully disabled + * @return *false* Bluetooth failed to disable + */ bool disableBluetooth() { return thisModem().disableBluetoothImpl(); } + + /** + * @brief Set the Bluetooth visibility + * + * @param visible True to make the modem visible over Bluetooth, false to make + * it invisible. + * @return *true* Bluetooth visibility was successfully changed. + * @return *false* Bluetooth visibility failed to change + */ bool setBluetoothVisibility(bool visible) { return thisModem().setBluetoothVisibilityImpl(visible); } + + /** + * @brief Set the Bluetooth host name + * + * @param name The name visible to other Bluetooth objects + * @return *true* Bluetooth host name was successfully changed. + * @return *false* Bluetooth host name failed to change + */ bool setBluetoothHostName(const char* name) { - return thisModem().setBluetoothHostNameImpl(name); + return thisModem().setBluetoothHostNameImpl(name); } - + /* * CRTP Helper */ @@ -42,15 +78,22 @@ class TinyGsmBluetooth { inline modemType& thisModem() { return static_cast(*this); } + ~TinyGsmBluetooth() {} + + /* =========================================== */ + /* =========================================== */ + /* + * Define the default function implementations + */ /* * Bluetooth functions */ - bool enableBluetoothImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool disableBluetoothImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool setBluetoothVisibilityImpl(bool visible) TINY_GSM_ATTR_NOT_IMPLEMENTED; - bool setBluetoothHostNameImpl(const char* name) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool enableBluetoothImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool disableBluetoothImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool setBluetoothVisibilityImpl(bool visible) TINY_GSM_ATTR_NOT_IMPLEMENTED; + bool setBluetoothHostNameImpl(const char* name) TINY_GSM_ATTR_NOT_IMPLEMENTED; }; diff --git a/src/TinyGsmCalling.tpp b/src/TinyGsmCalling.tpp index 413515a5..59525fd6 100644 --- a/src/TinyGsmCalling.tpp +++ b/src/TinyGsmCalling.tpp @@ -15,19 +15,55 @@ template class TinyGsmCalling { + /* =========================================== */ + /* =========================================== */ + /* + * Define the interface + */ public: /* * Phone Call functions */ + + /** + * @brief Answer an incoming phone call + * + * @return *true* The call was answered. + * @return *false* The call was not answered. + */ bool callAnswer() { return thisModem().callAnswerImpl(); } + + /** + * @brief Make a phone call + * + * @param number The number to call + * @return *true* The call was successfully made + * @return *false* The call wasn't made + */ bool callNumber(const String& number) { return thisModem().callNumberImpl(number); } + + /** + * @brief Hang up an in-progress phone call + * + * @return *true* The call was hung up. + * @return *false* The call was not hung up. + */ bool callHangup() { return thisModem().callHangupImpl(); } + + /** + * @brief Sent a DTMF (dual tone multi frequency) message over a phone call. + * + * @param cmd The command to send + * @param duration_ms The tone duration + * @return *true* The message was sent + * @return *false* The message failed to send + */ bool dtmfSend(char cmd, int duration_ms = 100) { return thisModem().dtmfSendImpl(cmd, duration_ms); } @@ -42,6 +78,13 @@ class TinyGsmCalling { inline modemType& thisModem() { return static_cast(*this); } + ~TinyGsmCalling() {} + + /* =========================================== */ + /* =========================================== */ + /* + * Define the default function implementations + */ /* * Phone Call functions diff --git a/src/TinyGsmClient.h b/src/TinyGsmClient.h index a8ae763e..cd2a98f8 100644 --- a/src/TinyGsmClient.h +++ b/src/TinyGsmClient.h @@ -68,15 +68,23 @@ typedef TinyGsmSaraR4 TinyGsm; typedef TinyGsmSaraR4::GsmClientSaraR4 TinyGsmClient; typedef TinyGsmSaraR4::GsmClientSecureR4 TinyGsmClientSecure; +#elif defined(TINY_GSM_MODEM_SARAR5) +#include "TinyGsmClientSaraR5.h" +typedef TinyGsmSaraR5 TinyGsm; +typedef TinyGsmSaraR5::GsmClientSaraR5 TinyGsmClient; +typedef TinyGsmSaraR5::GsmClientSecureR5 TinyGsmClientSecure; + #elif defined(TINY_GSM_MODEM_M95) #include "TinyGsmClientM95.h" typedef TinyGsmM95 TinyGsm; typedef TinyGsmM95::GsmClientM95 TinyGsmClient; -#elif defined(TINY_GSM_MODEM_BG96) +#elif defined(TINY_GSM_MODEM_BG96) || defined(TINY_GSM_MODEM_BG95) || \ + defined(TINY_GSM_MODEM_BG95SSL) #include "TinyGsmClientBG96.h" -typedef TinyGsmBG96 TinyGsm; -typedef TinyGsmBG96::GsmClientBG96 TinyGsmClient; +typedef TinyGsmBG96 TinyGsm; +typedef TinyGsmBG96::GsmClientBG96 TinyGsmClient; +typedef TinyGsmBG96::GsmClientSecureBG96 TinyGsmClientSecure; #elif defined(TINY_GSM_MODEM_A6) || defined(TINY_GSM_MODEM_A7) #include "TinyGsmClientA6.h" @@ -93,7 +101,7 @@ typedef TinyGsmM590::GsmClientM590 TinyGsmClient; typedef TinyGsmMC60 TinyGsm; typedef TinyGsmMC60::GsmClientMC60 TinyGsmClient; -#elif defined(TINY_GSM_MODEM_ESP8266) +#elif defined(TINY_GSM_MODEM_ESP8266) || defined(TINY_GSM_MODEM_ESP32) #define TINY_GSM_MODEM_HAS_WIFI #include "TinyGsmClientESP8266.h" typedef TinyGsmESP8266 TinyGsm; @@ -114,6 +122,12 @@ typedef TinyGsmSequansMonarch::GsmClientSequansMonarch TinyGsmClient; typedef TinyGsmSequansMonarch::GsmClientSecureSequansMonarch TinyGsmClientSecure; +#elif defined(TINY_GSM_MODEM_A7672X) +#include "TinyGsmClientA7672x.h" +typedef TinyGsmA7672X TinyGsm; +typedef TinyGsmA7672X::GsmClientA7672X TinyGsmClient; +typedef TinyGsmA7672X::GsmClientSecureA7672X TinyGsmClientSecure; + #else #error "Please define GSM modem model" #endif diff --git a/src/TinyGsmClientA6.h b/src/TinyGsmClientA6.h index 9226960e..f486b465 100644 --- a/src/TinyGsmClientA6.h +++ b/src/TinyGsmClientA6.h @@ -14,24 +14,34 @@ #define TINY_GSM_MUX_COUNT 8 #define TINY_GSM_NO_MODEM_BUFFER +#ifdef AT_NL +#undef AT_NL +#endif +#define AT_NL "\r\n" + +#ifdef MODEM_MANUFACTURER +#undef MODEM_MANUFACTURER +#endif +#define MODEM_MANUFACTURER "Ai-Thinker" + +#ifdef MODEM_MODEL +#undef MODEM_MODEL +#endif +#if defined(TINY_GSM_MODEM_A7) +#define MODEM_MODEL "A7" +#else +#define MODEM_MODEL "A6" +#endif -#include "TinyGsmBattery.tpp" -#include "TinyGsmCalling.tpp" -#include "TinyGsmGPRS.tpp" #include "TinyGsmModem.tpp" -#include "TinyGsmSMS.tpp" #include "TinyGsmTCP.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmCalling.tpp" +#include "TinyGsmSMS.tpp" #include "TinyGsmTime.tpp" +#include "TinyGsmBattery.tpp" -#define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; -#if defined TINY_GSM_DEBUG -static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; -static const char GSM_CMS_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CMS ERROR:"; -#endif - -enum RegStatus { +enum A6RegStatus { REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, @@ -129,7 +139,7 @@ class TinyGsmA6 : public TinyGsmModem, * Basic functions */ protected: - bool initImpl(const char* pin = NULL) { + bool initImpl(const char* pin = nullptr) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); DBG(GF("### TinyGSM Compiled Module: TinyGsmClientA6")); @@ -155,7 +165,7 @@ class TinyGsmA6 : public TinyGsmModem, 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) { + if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); } else { @@ -165,6 +175,15 @@ class TinyGsmA6 : public TinyGsmModem, } } + // Gets the modem serial number + String getModemSerialNumberImpl() { + sendAT(GF("GSN")); // Not CGSN + String res; + if (waitResponse(1000L, res) != 1) { return ""; } + cleanResponseString(res); + return res; + } + bool factoryDefaultImpl() { sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write waitResponse(); @@ -176,7 +195,7 @@ class TinyGsmA6 : public TinyGsmModem, * Power functions */ protected: - bool restartImpl(const char* pin = NULL) { + bool restartImpl(const char* pin = nullptr) { if (!testAT()) { return false; } sendAT(GF("+RST=1")); delay(3000); @@ -198,13 +217,13 @@ class TinyGsmA6 : public TinyGsmModem, * Generic network functions */ public: - RegStatus getRegistrationStatus() { - return (RegStatus)getRegistrationStatusXREG("CREG"); + A6RegStatus getRegistrationStatus() { + return (A6RegStatus)getRegistrationStatusXREG("CREG"); } protected: bool isNetworkConnectedImpl() { - RegStatus s = getRegistrationStatus(); + A6RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } @@ -212,18 +231,27 @@ class TinyGsmA6 : public TinyGsmModem, sendAT(GF("+CIFSR")); String res; if (waitResponse(10000L, res) != 1) { return ""; } - res.replace(GSM_NL "OK" GSM_NL, ""); - res.replace(GSM_NL, ""); - res.trim(); + cleanResponseString(res); return res; } + /* + * Secure socket layer (SSL) functions + */ + protected: + // No functions of this type supported + + /* + * WiFi functions + */ + // No functions of this type supported + /* * GPRS functions */ protected: - bool gprsConnectImpl(const char* apn, const char* user = NULL, - const char* pwd = NULL) { + bool gprsConnectImpl(const char* apn, const char* user = nullptr, + const char* pwd = nullptr) { gprsDisconnect(); sendAT(GF("+CGATT=1")); @@ -266,7 +294,7 @@ class TinyGsmA6 : public TinyGsmModem, waitResponse(); sendAT(GF("+COPS?")); - if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { return ""; } + if (waitResponse(GF(AT_NL "+COPS:")) != 1) { return ""; } streamSkipUntil('"'); // Skip mode and format String res = stream.readStringUntil('"'); waitResponse(); @@ -279,7 +307,7 @@ class TinyGsmA6 : public TinyGsmModem, protected: String getSimCCIDImpl() { sendAT(GF("+CCID")); - if (waitResponse(GF(GSM_NL "+SCID: SIM Card ID:")) != 1) { return ""; } + if (waitResponse(GF(AT_NL "+SCID: SIM Card ID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -300,16 +328,16 @@ class TinyGsmA6 : public TinyGsmModem, 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(AT_NL "+CIEV: \"CALL\",1"), + GF(AT_NL "+CIEV: \"CALL\",0"), GFP(GSM_ERROR)) != 1) { return false; } - int8_t rsp = waitResponse(60000L, GF(GSM_NL "+CIEV: \"SOUNDER\",0"), - GF(GSM_NL "+CIEV: \"CALL\",0")); + int8_t rsp = waitResponse(60000L, GF(AT_NL "+CIEV: \"SOUNDER\",0"), + GF(AT_NL "+CIEV: \"CALL\",0")); - int8_t rsp2 = waitResponse(300L, GF(GSM_NL "BUSY" GSM_NL), - GF(GSM_NL "NO ANSWER" GSM_NL)); + int8_t rsp2 = waitResponse(300L, GF(AT_NL "BUSY" AT_NL), + GF(AT_NL "NO ANSWER" AT_NL)); return rsp == 1 && rsp2 == 0; } @@ -352,7 +380,7 @@ class TinyGsmA6 : public TinyGsmModem, } /* - * Messaging functions + * Text messaging (SMS) functions */ protected: String sendUSSDImpl(const String& code) { @@ -362,7 +390,7 @@ class TinyGsmA6 : public TinyGsmModem, waitResponse(); sendAT(GF("+CUSD=1,\""), code, GF("\",15")); if (waitResponse(10000L) != 1) { return ""; } - if (waitResponse(GF(GSM_NL "+CUSD:")) != 1) { return ""; } + if (waitResponse(GF(AT_NL "+CUSD:")) != 1) { return ""; } streamSkipUntil('"'); String hex = stream.readStringUntil('"'); streamSkipUntil(','); @@ -377,23 +405,42 @@ class TinyGsmA6 : public TinyGsmModem, } } + /* + * GSM Location functions + */ + // No functions of this type supported + + /* + * GPS/GNSS/GLONASS location functions + */ + // No functions of this type supported + /* * Time functions */ - protected: - // Can follow the standard CCLK function in the template + // Follows all clock functions as inherited from TinyGsmTime.tpp // Note - the clock probably has to be set manaually first + /* + * NTP server functions + */ + // No functions of this type supported + + /* + * BLE functions + */ + // No functions of this type supported + /* * Battery functions */ protected: - uint16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + int16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE; // Needs a '?' after CBC, unlike most int8_t getBattPercentImpl() { sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return false; } + if (waitResponse(GF(AT_NL "+CBC:")) != 1) { return false; } streamSkipUntil(','); // Skip battery charge status // Read battery charge level int8_t res = streamGetIntBefore('\n'); @@ -403,10 +450,10 @@ class TinyGsmA6 : public TinyGsmModem, } // Needs a '?' after CBC, unlike most - bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, - uint16_t& milliVolts) { + bool getBattStatsImpl(int8_t& chargeState, int8_t& percent, + int16_t& milliVolts) { sendAT(GF("+CBC?")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return false; } + if (waitResponse(GF(AT_NL "+CBC:")) != 1) { return false; } chargeState = streamGetIntBefore(','); percent = streamGetIntBefore('\n'); milliVolts = 0; @@ -415,6 +462,11 @@ class TinyGsmA6 : public TinyGsmModem, return true; } + /* + * Temperature functions + */ + // No functions of this type supported + /* * Client related functions */ @@ -425,12 +477,12 @@ class TinyGsmA6 : public TinyGsmModem, 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; } + if (waitResponse(timeout_ms, GF(AT_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)); + int8_t rsp = waitResponse((timeout_ms - (millis() - startMillis)), + GF("CONNECT OK" AT_NL), GF("CONNECT FAIL" AT_NL), + GF("ALREADY CONNECT" AT_NL)); if (waitResponse() != 1) { return false; } *mux = newMux; @@ -439,10 +491,10 @@ class TinyGsmA6 : public TinyGsmModem, 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; } + if (waitResponse(2000L, GF(AT_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(AT_NL "FAIL")) != 1) { return 0; } return len; } @@ -458,115 +510,37 @@ class TinyGsmA6 : public TinyGsmModem, * Utilities */ public: - // TODO(vshymanskyy): Optimize this! - int8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - /*String r1s(r1); r1s.trim(); - String r2s(r2); r2s.trim(); - String r3s(r3); r3s.trim(); - String r4s(r4); r4s.trim(); - String r5s(r5); r5s.trim(); - DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ - data.reserve(64); - uint8_t index = 0; - uint32_t startMillis = millis(); - do { - TINY_GSM_YIELD(); - while (stream.available() > 0) { - TINY_GSM_YIELD(); - int8_t a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += static_cast(a); - if (r1 && data.endsWith(r1)) { - index = 1; - goto finish; - } else if (r2 && data.endsWith(r2)) { - index = 2; - goto finish; - } else if (r3 && data.endsWith(r3)) { -#if defined TINY_GSM_DEBUG - if (r3 == GFP(GSM_CME_ERROR)) { - streamSkipUntil('\n'); // Read out the error - } -#endif - index = 3; - goto finish; - } else if (r4 && data.endsWith(r4)) { - index = 4; - goto finish; - } else if (r5 && data.endsWith(r5)) { - index = 5; - goto finish; - } else if (data.endsWith(GF("+CIPRCV:"))) { - int8_t mux = streamGetIntBefore(','); - int16_t len = streamGetIntBefore(','); - int16_t len_orig = len; - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - if (len > sockets[mux]->rx.free()) { - DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free()); - } else { - DBG("### Got: ", len, "->", sockets[mux]->rx.free()); - } - 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:"))) { - int8_t mux = streamGetIntBefore('\n'); - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->sock_connected = false; - } - data = ""; - DBG("### Closed: ", mux); + bool handleURCs(String& data) { + if (data.endsWith(GF("+CIPRCV:"))) { + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore(','); + int16_t len_orig = len; + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + if (len > sockets[mux]->rx.free()) { + DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free()); + // reset the len to read to the amount free + len = sockets[mux]->rx.free(); } + while (len--) { moveCharFromStreamToFifo(mux); } + // TODO(?) Deal with missing characters + if (len_orig != sockets[mux]->available()) { + DBG("### Different number of characters received than expected: ", + sockets[mux]->available(), " vs ", len_orig); + } + } + data = ""; + DBG("### Got Data: ", len_orig, "on", mux); + return true; + } else if (data.endsWith(GF("+TCPCLOSED:"))) { + int8_t mux = streamGetIntBefore('\n'); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->sock_connected = false; } - } while (millis() - startMillis < timeout_ms); - finish: - if (!index) { - data.trim(); - if (data.length()) { DBG("### Unhandled:", data); } data = ""; + DBG("### Closed: ", mux); + return true; } - // data.replace(GSM_NL, "/"); - // DBG('<', index, '>', data); - return index; - } - - int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - String data; - return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); - } - - int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - return waitResponse(1000, r1, r2, r3, r4, r5); + return false; } public: @@ -574,7 +548,6 @@ class TinyGsmA6 : public TinyGsmModem, protected: GsmClientA6* sockets[TINY_GSM_MUX_COUNT]; - const char* gsmNL = GSM_NL; }; #endif // SRC_TINYGSMCLIENTA6_H_ diff --git a/src/TinyGsmClientA7672x.h b/src/TinyGsmClientA7672x.h new file mode 100644 index 00000000..b470ac45 --- /dev/null +++ b/src/TinyGsmClientA7672x.h @@ -0,0 +1,775 @@ +/** + * @file TinyGsmClientA7672x.h + * @author Giovanni de Rosso Unruh + * @license LGPL-3.0 + * @copyright Copyright (c) 2022 Giovanni de Rosso Unruh + * @date Oct 2022 + */ + +#ifndef SRC_TINYGSMCLIENTA7672X_H_ +#define SRC_TINYGSMCLIENTA7672X_H_ + +// #define TINY_GSM_DEBUG Serial + +#define TINY_GSM_MUX_COUNT 10 +#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE +#ifdef AT_NL +#undef AT_NL +#endif +#define AT_NL "\r\n" + +#ifdef MODEM_MANUFACTURER +#undef MODEM_MANUFACTURER +#endif +#define MODEM_MANUFACTURER "SIMCom" + +#ifdef MODEM_MODEL +#undef MODEM_MODEL +#endif +#define MODEM_MODEL "A7672x" + +#include "TinyGsmModem.tpp" +#include "TinyGsmTCP.tpp" +#include "TinyGsmSSL.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmCalling.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmGSMLocation.tpp" +#include "TinyGsmTime.tpp" +#include "TinyGsmNTP.tpp" +#include "TinyGsmBattery.tpp" +#include "TinyGsmTemperature.tpp" + +enum A7672xRegStatus { + REG_NO_RESULT = -1, + REG_UNREGISTERED = 0, + REG_SEARCHING = 2, + REG_DENIED = 3, + REG_OK_HOME = 1, + REG_OK_ROAMING = 5, + REG_UNKNOWN = 4, +}; +class TinyGsmA7672X : public TinyGsmModem, + public TinyGsmGPRS, + public TinyGsmTCP, + public TinyGsmSSL, + public TinyGsmCalling, + public TinyGsmSMS, + public TinyGsmGSMLocation, + public TinyGsmTime, + public TinyGsmNTP, + public TinyGsmBattery, + public TinyGsmTemperature { + 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 TinyGsmNTP; + friend class TinyGsmBattery; + friend class TinyGsmTemperature; + + /* + * Inner Client + */ + public: + class GsmClientA7672X : public GsmClient { + friend class TinyGsmA7672X; + + public: + GsmClientA7672X() {} + + explicit GsmClientA7672X(TinyGsmA7672X& modem, uint8_t mux = 0) { + init(&modem, mux); + } + + bool init(TinyGsmA7672X* modem, uint8_t mux = 0) { + this->at = modem; + sock_available = 0; + prev_check = 0; + sock_connected = false; + got_data = false; + + if (mux < TINY_GSM_MUX_COUNT) { + this->mux = mux; + } else { + this->mux = (mux % TINY_GSM_MUX_COUNT); + } + at->sockets[this->mux] = this; + + return true; + } + + public: + virtual int connect(const char* host, uint16_t port, int timeout_s) { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, false, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES + + void stop(uint32_t maxWaitMs) { + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+CIPCLOSE="), mux); + sock_connected = false; + at->waitResponse(); + } + void stop() override { + stop(15000L); + } + + /* + * Extended API + */ + + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; + + /* + * Inner Secure Client + */ + public: + class GsmClientSecureA7672X : public GsmClientA7672X { + public: + GsmClientSecureA7672X() {} + + explicit GsmClientSecureA7672X(TinyGsmA7672X& modem, uint8_t mux = 0) + : GsmClientA7672X(modem, mux) {} + + public: + bool addCertificate(const char* certificateName, const char* cert, + const uint16_t len) { + return at->addCertificate(certificateName, cert, len); + } + + bool setCertificate(const char* certificateName) { + return at->setCertificate(certificateName, mux); + } + + bool deleteCertificate(const char* certificateName) { + return at->deleteCertificate(certificateName); + } + + 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 + + void stop(uint32_t maxWaitMs) { + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+CCHCLOSE="), mux); //, GF(",1")); // Quick close + sock_connected = false; + at->waitResponse(); + } + void stop() override { + stop(15000L); + } + }; + + /* + * Constructor + */ + public: + explicit TinyGsmA7672X(Stream& stream) : stream(stream) { + memset(sockets, 0, sizeof(sockets)); + } + + /* + * Basic functions + */ + protected: + bool initImpl(const char* pin = nullptr) { + DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + DBG(GF("### TinyGSM Compiled Module: A7672X")); + + if (!testAT(2000)) { return false; } + + // sendAT(GF("&FZ")); // Factory + Reset + // waitResponse(); + + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } + +#ifdef TINY_GSM_DEBUG + sendAT(GF("V1")); // turn on verbose error codes +#else + sendAT(GF("V0")); // turn off error codes +#endif + waitResponse(); + + DBG(GF("### Modem:"), getModemName()); + + 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 != nullptr && strlen(pin) > 0) { + simUnlock(pin); + return (getSimStatus() == SIM_READY); + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true + return (ret == SIM_READY || ret == SIM_LOCKED); + } + } + + bool factoryDefaultImpl() { + sendAT(GF("&F")); // Factory + Reset + waitResponse(); + sendAT(GF("+IFC=0,0")); // No Flow Control + waitResponse(); + sendAT(GF("+ICF=2,2")); // 8 data 0 parity 1 stop + waitResponse(); + sendAT(GF("+CSCLK=0")); // Control UART Sleep always work + waitResponse(); + sendAT(GF("&W")); // Write configuration + return waitResponse() == 1; + } + + /* + * Power functions + */ + protected: + bool restartImpl(const char* pin = nullptr) { + if (!testAT()) { return false; } + sendAT(GF("+CRESET")); + waitResponse(); + if (!setPhoneFunctionality(0)) { return false; } + if (!setPhoneFunctionality(1, true)) { return false; } + delay(3000); + return init(pin); + } + + bool powerOffImpl() { + sendAT(GF("+CPOF")); + return waitResponse(10000L) == 1; + } + + // This command is used to enable UART Sleep or always work. If set to 0, + // UART always work. If set to 1, ensure that DTR is pulled high and the + // module can go to DTR sleep. If set to 2, the module will enter RXsleep. RX + // wakeup directly sends data through the serial port (for example: AT) to + // wake up + bool sleepEnableImpl(bool enable = true) { + sendAT(GF("+CSCLK="), + enable ? "2" : "1"); // 2: RXsleep (at wakeup) 1: DTR sleep + return waitResponse() == 1; + } + + // 0 minimum functionality + // 1 full functionality, online mode + // 4 disable phone both transmit and receive RF circuits + // 5 Factory Test Mode (The A7600's 5 and 1 have the same function) + // 6 Reset + // 7 Offline Mode + // 0 do not reset the ME before setting it to power level + // 1 reset the ME before setting it to power level. This + // valueonlytakes effect when equals 1 + bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) { + sendAT(GF("+CFUN="), fun, reset ? ",1" : ",0"); + return waitResponse(10000L) == 1; + } + + /* + * Generic network functions + */ + public: + A7672xRegStatus getRegistrationStatus() { + return (A7672xRegStatus)getRegistrationStatusXREG("CREG"); + } + + String getLocalIPImpl() { + if (hasSSL) { + sendAT(GF("+CCHADDR")); + if (waitResponse(GF("+CCHADDR:")) != 1) { return ""; } + } else { + sendAT(GF("+CGPADDR=1")); + if (waitResponse(GF("+CGPADDR:")) != 1) { return ""; } + } + streamSkipUntil(','); // Skip context id + String res = stream.readStringUntil('\r'); + if (waitResponse() != 1) { return ""; } + return res; + } + + protected: + bool isNetworkConnectedImpl() { + A7672xRegStatus s = getRegistrationStatus(); + return (s == REG_OK_HOME || s == REG_OK_ROAMING); + } + + /* + * Secure socket layer (SSL) functions + */ + public: + // The name of the certificate/key/password file. The file name must + // havetype like ".pem" or ".der". + // The certificate like - const char ca_cert[] PROGMEM = R"EOF(-----BEGIN... + // len of certificate like - sizeof(ca_cert) + bool addCertificate(const char* certificateName, const char* cert, + const uint16_t len) { + sendAT(GF("+CCERTDOWN="), certificateName, GF(","), len); + if (waitResponse(GF(">")) != 1) { return false; } + stream.write(cert, len); + stream.flush(); + return waitResponse() == 1; + } + + bool deleteCertificate(const char* certificateName) { // todo test + sendAT(GF("+CCERTDELE="), certificateName); + return waitResponse() == 1; + } + + /* + * WiFi functions + */ + // No functions of this type supported + + /* + * GPRS functions + */ + protected: + bool gprsConnectImpl(const char* apn, const char* user = nullptr, + const char* pwd = nullptr) { + gprsDisconnect(); + + // Define the PDP context + sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"'); + waitResponse(); + + // Activate the PDP context + sendAT(GF("+CGACT=1,1")); + waitResponse(60000L); + + // Attach to GPRS + sendAT(GF("+CGATT=1")); + if (waitResponse(60000L) != 1) { return false; } + + // Set to get data manually + sendAT(GF("+CIPRXGET=1")); + if (waitResponse() != 1) { return false; } + + // Get Local IP Address, only assigned after connection + sendAT(GF("+CGPADDR=1")); + 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; } + + return true; + } + + bool gprsDisconnectImpl() { + // Shut the TCP/IP connection + sendAT(GF("+NETCLOSE")); + if (waitResponse(60000L) != 1) { return false; } + + sendAT(GF("+CGATT=0")); // Detach from GPRS + if (waitResponse(60000L) != 1) { return false; } + + return true; + } + + String getProviderImpl() { + sendAT(GF("+CSPN?")); + if (waitResponse(GF("+CSPN:")) != 1) { return ""; } + streamSkipUntil('"'); /* Skip mode and format */ + String res = stream.readStringUntil('"'); + waitResponse(); + return res; + } + + /* + * SIM card functions + */ + protected: + SimStatus getSimStatusImpl(uint32_t timeout_ms = 10000L) { + for (uint32_t start = millis(); millis() - start < timeout_ms;) { + sendAT(GF("+CPIN?")); + if (waitResponse(GF("+CPIN:")) != 1) { + delay(1000); + continue; + } + int8_t status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), + GF("SIM not inserted"), GF("SIM REMOVED")); + waitResponse(); + switch (status) { + case 2: + case 3: return SIM_LOCKED; + case 1: return SIM_READY; + default: return SIM_ERROR; + } + } + return SIM_ERROR; + } + + String getSimCCIDImpl() { + sendAT(GF("+CICCID")); + if (waitResponse(GF(AT_NL "+ICCID:")) != 1) { return ""; } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + return res; + } + + /* + * Phone Call functions + */ + public: + bool setGsmBusy(bool busy = true) { + sendAT(GF("+CCFC=1,"), busy ? 1 : 0); + return waitResponse() == 1; + } + + /* + * Audio functions + */ + // No functions of this type supported + + /* + * Text messaging (SMS) functions + */ + // Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp + + /* + * GSM Location functions + */ + // No functions of this type supported + /* + * GPS/GNSS/GLONASS location functions + */ + // No functions of this type supported + + /* + * Time functions + */ + // No functions of this type supported + /* + * NTP server functions + */ + // No functions of this type supported + + /* + * BLE functions + */ + // No functions of this type supported + + /* + * Battery functions + */ + // No functions of this type supported + + /* + * Temperature functions + */ + protected: + float getTemperatureImpl() { + String res = ""; + sendAT(GF("+CPMUTEMP")); + if (waitResponse(1000L, res)) { return 0; } + res = res.substring(res.indexOf(':'), res.indexOf('\r')); + float temp = res.toFloat(); + waitResponse(); + return temp; + } + /* + * Client related functions + */ + protected: + bool modemConnect(const char* host, uint16_t port, uint8_t mux, + bool ssl = false, int timeout_s = 75) { + int8_t rsp; + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + + // +CTCPKA:,,, + sendAT(GF("+CTCPKA=1,2,5,1")); + if (waitResponse(2000L) != 1) { return false; } + + if (ssl) { + hasSSL = true; + // set the ssl version + // AT+CSSLCFG="sslversion",, + // PDP context identifier + // 0: QAPI_NET_SSL_PROTOCOL_UNKNOWN + // 1: QAPI_NET_SSL_PROTOCOL_TLS_1_0 + // 2: QAPI_NET_SSL_PROTOCOL_TLS_1_1 + // 3: QAPI_NET_SSL_PROTOCOL_TLS_1_2 + // 4: QAPI_NET_SSL_PROTOCOL_DTLS_1_0 + // 5: QAPI_NET_SSL_PROTOCOL_DTLS_1_2 + // NOTE: despite docs using caps, "sslversion" must be in lower case + sendAT(GF("+CSSLCFG=\"sslversion\",0,3")); // TLS 1.2 + if (waitResponse(5000L) != 1) return false; + + + if (certificates[mux] != "") { + /* Configure the server root CA of the specified SSL context + AT + CSSLCFG = "cacert", , */ + sendAT(GF("+CSSLCFG=\"cacert\",0,"), certificates[mux].c_str()); + if (waitResponse(5000L) != 1) return false; + } + + // set the SSL SNI (server name indication) + // AT+CSSLCFG="enableSNI",, + // NOTE: despite docs using caps, "sni" must be in lower case + sendAT(GF("+CSSLCFG=\"enableSNI\",0,1")); + if (waitResponse(2000L) != 1) { return false; } + + // Configure the report mode of sending and receiving data + /* +CCHSET=, + * Whether to report result of CCHSEND, the default + * value is 0: 0 No. 1 Yes. Module will report +CCHSEND: + * , to MCU when complete sending data. + * + * The receiving mode, the default value is 0: + * 0 Output the data to MCU whenever received data. + * 1 Module caches the received data and notifies MCU with+CCHEVENT: + * ,RECV EVENT. MCU can use AT+CCHRECV to receive the cached + * data (only in manual receiving mode). + */ + sendAT(GF("+CCHSET=1,1")); + if (waitResponse(2000L) != 1) { return false; } + + sendAT(GF("+CCHSTART")); + if (waitResponse(2000L) != 1) { return false; } + + sendAT(GF("+CCHSSLCFG="), mux, GF(",0")); + if (waitResponse(2000L) != 1) { return false; } + + sendAT(GF("+CCHOPEN="), 0, GF(",\""), host, GF("\","), port, GF(",2")); + rsp = waitResponse(timeout_ms, GF("+CCHOPEN: 0,0" AT_NL), + GF("+CCHOPEN: 0,1" AT_NL), GF("+CCHOPEN: 0,4" AT_NL), + GF("ERROR" AT_NL), GF("CLOSE OK" AT_NL)); + } else { + sendAT(GF("+NETOPEN")); + if (waitResponse(2000L) != 1) { return false; } + + sendAT(GF("+NETOPEN?")); + if (waitResponse(2000L) != 1) { return false; } + + sendAT(GF("+CIPOPEN="), 0, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), + port); + + rsp = waitResponse( + timeout_ms, GF("+CIPOPEN: 0,0" AT_NL), GF("+CIPOPEN: 0,1" AT_NL), + GF("+CIPOPEN: 0,4" AT_NL), GF("ERROR" AT_NL), + GF("CLOSE OK" AT_NL)); // Happens when HTTPS handshake fails + } + + return (1 == rsp); + } + + int16_t modemSend(const void* buff, size_t len, uint8_t mux) { + if (hasSSL) + sendAT(GF("+CCHSEND="), mux, ',', (uint16_t)len); + else + sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len); + if (waitResponse(GF(">")) != 1) { return 0; } + stream.write(reinterpret_cast(buff), len); + stream.flush(); + + if (waitResponse() != 1) { return 0; } + if (waitResponse(10000L, GF("+CCHSEND: 0,0" AT_NL), + GF("+CCHSEND: 0,4" AT_NL), GF("+CCHSEND: 0,9" AT_NL), + GF("ERROR" AT_NL), GF("CLOSE OK" AT_NL)) != 1) { + return 0; + } + return len; + } + + size_t modemRead(size_t size, uint8_t mux) { + int16_t len_requested = 0; + int16_t len_confirmed = 0; + if (!sockets[mux]) return 0; + if (hasSSL) { + sendAT(GF("+CCHRECV?")); // TODO(Rosso): Optimize this! + String res = ""; + waitResponse(2000L, res); + int16_t len = + res.substring(res.indexOf(',') + 1, res.lastIndexOf(',')).toInt(); + sendAT(GF("+CCHRECV="), mux, ',', (uint16_t)size); + if (waitResponse(GF("+CCHRECV:")) != 1) { return 0; } + //+CCHRECV: DATA,0, + streamSkipUntil(','); // Skip DATA + streamSkipUntil(','); // Skip mux + len_requested = streamGetIntBefore('\n'); + len_confirmed = len; // streamGetIntBefore(','); + } else { + sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size); + if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } + streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX + streamSkipUntil(','); // Skip mux + len_requested = streamGetIntBefore(','); + // ^^ Requested number of data bytes (1-1460 bytes)to be read + len_confirmed = streamGetIntBefore('\n'); + // ^^ Confirmed number of data bytes to be read, which may be less than + // requested. 0 indicates that no data can be read. + // SRGD NOTE: Contrary to above (which is copied from AT command manual) + // this is actually be the number of bytes that will be remaining in the + // buffer after the read. + } + for (int i = 0; i < len_requested; i++) { + uint32_t startMillis = millis(); + while (!stream.available() && + (millis() - startMillis < sockets[mux]->_timeout)) { + TINY_GSM_YIELD(); + } + char c = stream.read(); + sockets[mux]->rx.put(c); + } + // DBG("### READ:", len_requested, " bytes from connection ", mux); + // sockets[mux]->sock_available = modemGetAvailable(mux); + sockets[mux]->sock_available = len_confirmed; + waitResponse(); + return len_requested; + } + + size_t modemGetAvailable(uint8_t mux) { + if (!sockets[mux]) return 0; + size_t result = 0; + if (hasSSL) { + sendAT(GF("+CCHRECV?")); // TODO(Rosso): Optimize this! + String res = ""; + waitResponse(2000L, res); + result = + res.substring(res.indexOf(',') + 1, res.lastIndexOf(',')).toInt(); + } else { + sendAT(GF("+CIPRXGET=4,"), mux); + result = 0; + if (waitResponse(GF("+CIPRXGET:")) == 1) { + streamSkipUntil(','); // Skip mode 4 + streamSkipUntil(','); // Skip mux + result = streamGetIntBefore('\n'); + waitResponse(); + } + } + // DBG("### Available:", result, "on", mux); + if (result == 0) { sockets[mux]->sock_connected = modemGetConnected(mux); } + return result; + } + + bool modemGetConnected(uint8_t mux) { + int8_t res = 0; + if (hasSSL) { + bool connected = this->sockets[mux]->sock_connected; + // DBG("### Connected:", connected); + return connected; + } else { + sendAT(GF("+CIPACK="), mux); + waitResponse(GF("+CIPACK:")); + res = waitResponse(2000L); //(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), + // GF(",\"CLOSING\""), GF(",\"REMOTE + // CLOSING\""), GF(",\"INITIAL\"")); + waitResponse(); + } + return 1 == res; + } + + /* + * Utilities + */ + public: + bool handleURCs(String& data) { + if (data.endsWith(GF(AT_NL "+CIPRXGET:"))) { + 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); + return true; + } else { + data += mode; + return false; + } + } else if (data.endsWith(GF("RECV EVENT" AT_NL))) { + sendAT(GF("+CCHRECV?")); + String res = ""; + waitResponse(2000L, res); + int8_t mux = res.substring(res.lastIndexOf(',') + 1).toInt(); + int16_t len = + res.substring(res.indexOf(',') + 1, res.lastIndexOf(',')).toInt(); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->got_data = true; + if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } + } + data = ""; + DBG("### Got Data:", len, "on", mux); + return true; + } else if (data.endsWith(GF("+CCHRECV: 0,0" AT_NL))) { + int8_t mux = data.substring(data.lastIndexOf(',') + 1).toInt(); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->sock_connected = true; + } + data = ""; + DBG("### ACK:", mux); + return true; + } else if (data.endsWith(GF("+IPCLOSE:"))) { + int8_t mux = streamGetIntBefore(','); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->sock_connected = false; + } + data = ""; + streamSkipUntil('\n'); + DBG("### TCP Closed: ", mux); + return true; + } else if (data.endsWith(GF("+CCHCLOSE:"))) { + int8_t mux = streamGetIntBefore(','); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->sock_connected = false; + } + data = ""; + streamSkipUntil('\n'); + DBG("### SSL Closed: ", mux); + return true; + } else if (data.endsWith(GF("+CCH_PEER_CLOSED:"))) { + int8_t mux = streamGetIntBefore('\n'); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->sock_connected = false; + } + data = ""; + DBG("### SSL Closed: ", mux); + return true; + } else if (data.endsWith(GF("*PSNWID:"))) { + streamSkipUntil('\n'); // Refresh network name by network + data = ""; + DBG("### Network name updated."); + return true; + } else if (data.endsWith(GF("*PSUTTZ:"))) { + streamSkipUntil('\n'); // Refresh time and time zone by network + data = ""; + DBG("### Network time and time zone updated."); + return true; + } else if (data.endsWith(GF("+CTZV:"))) { + streamSkipUntil('\n'); // Refresh network time zone by network + data = ""; + DBG("### Network time zone updated."); + return true; + } else if (data.endsWith(GF("DST:"))) { + streamSkipUntil('\n'); // Refresh Network Daylight Saving Time by network + data = ""; + DBG("### Daylight savings time state updated."); + return true; + } + return false; + } + + public: + Stream& stream; + + protected: + GsmClientA7672X* sockets[TINY_GSM_MUX_COUNT]; + bool hasSSL = false; + String certificates[TINY_GSM_MUX_COUNT]; +}; + +#endif // SRC_TINYGSMCLIENTA7672X_H_ diff --git a/src/TinyGsmClientBG96.h b/src/TinyGsmClientBG96.h index e6964040..7ee19598 100644 --- a/src/TinyGsmClientBG96.h +++ b/src/TinyGsmClientBG96.h @@ -1,9 +1,9 @@ /** * @file TinyGsmClientBG96.h - * @author Volodymyr Shymanskyy + * @author Volodymyr Shymanskyy and Aurelien BOUIN (SSL) * @license LGPL-3.0 * @copyright Copyright (c) 2016 Volodymyr Shymanskyy - * @date Apr 2018 + * @date Apr 2018, Aug 2023 (SSL) */ #ifndef SRC_TINYGSMCLIENTBG96_H_ @@ -14,27 +14,38 @@ #define TINY_GSM_MUX_COUNT 12 #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE +#ifdef AT_NL +#undef AT_NL +#endif +#define AT_NL "\r\n" + +#ifdef MODEM_MANUFACTURER +#undef MODEM_MANUFACTURER +#endif +#define MODEM_MANUFACTURER "Quectel" + +#ifdef MODEM_MODEL +#undef MODEM_MODEL +#endif +#if defined(TINY_GSM_MODEM_BG95) || defined(TINY_GSM_MODEM_BG95SSL) +#define MODEM_MODEL "BG95" +#else +#define MODEM_MODEL "BG96" +#endif -#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 "TinyGsmSSL.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmCalling.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmGPS.tpp" #include "TinyGsmTime.tpp" #include "TinyGsmNTP.tpp" +#include "TinyGsmBattery.tpp" +#include "TinyGsmTemperature.tpp" -#define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; -#if defined TINY_GSM_DEBUG -static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; -static const char GSM_CMS_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CMS ERROR:"; -#endif - -enum RegStatus { +enum BG96RegStatus { REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, @@ -47,21 +58,23 @@ enum RegStatus { class TinyGsmBG96 : public TinyGsmModem, public TinyGsmGPRS, public TinyGsmTCP, + public TinyGsmSSL, public TinyGsmCalling, public TinyGsmSMS, + public TinyGsmGPS, public TinyGsmTime, public TinyGsmNTP, - public TinyGsmGPS, public TinyGsmBattery, public TinyGsmTemperature { friend class TinyGsmModem; friend class TinyGsmGPRS; friend class TinyGsmTCP; + friend class TinyGsmSSL; friend class TinyGsmCalling; friend class TinyGsmSMS; + friend class TinyGsmGPS; friend class TinyGsmTime; friend class TinyGsmNTP; - friend class TinyGsmGPS; friend class TinyGsmBattery; friend class TinyGsmTemperature; @@ -76,6 +89,7 @@ class TinyGsmBG96 : public TinyGsmModem, GsmClientBG96() {} explicit GsmClientBG96(TinyGsmBG96& modem, uint8_t mux = 0) { + ssl_sock = false; init(&modem, mux); } @@ -96,17 +110,16 @@ class TinyGsmBG96 : public TinyGsmModem, 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); + sock_connected = at->modemConnect(host, port, mux, timeout_s); return sock_connected; } TINY_GSM_CLIENT_CONNECT_OVERRIDES - void stop(uint32_t maxWaitMs) { + virtual void stop(uint32_t maxWaitMs) { uint32_t startMillis = millis(); dumpModemBuffer(maxWaitMs); at->sendAT(GF("+QICLOSE="), mux); @@ -122,34 +135,39 @@ class TinyGsmBG96 : public TinyGsmModem, */ String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + + protected: + bool ssl_sock; }; /* * Inner Secure Client */ + public: + class GsmClientSecureBG96 : public GsmClientBG96 { + public: + GsmClientSecureBG96() {} - /* - class GsmClientSecureBG96 : public GsmClientBG96 - { - public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 0) - : public GsmClient(modem, mux) - {} + explicit GsmClientSecureBG96(TinyGsmBG96& modem, uint8_t mux = 0) + : GsmClientBG96(modem, mux) { + ssl_sock = true; + } + bool setCertificate(const String& certificateName) { + return at->setCertificate(certificateName, mux); + } - 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; + void stop(uint32_t maxWaitMs) override { + uint32_t startMillis = millis(); + dumpModemBuffer(maxWaitMs); + at->sendAT(GF("+QSSLCLOSE="), mux); + sock_connected = false; + at->waitResponse((maxWaitMs - (millis() - startMillis))); + } + void stop() override { + stop(15000L); } - TINY_GSM_CLIENT_CONNECT_OVERRIDES }; - */ /* * Constructor @@ -163,7 +181,7 @@ class TinyGsmBG96 : public TinyGsmModem, * Basic functions */ protected: - bool initImpl(const char* pin = NULL) { + bool initImpl(const char* pin = nullptr) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); DBG(GF("### TinyGSM Compiled Module: TinyGsmClientBG96")); @@ -191,7 +209,7 @@ class TinyGsmBG96 : public TinyGsmModem, 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) { + if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); } else { @@ -205,7 +223,7 @@ class TinyGsmBG96 : public TinyGsmModem, * Power functions */ protected: - bool restartImpl(const char* pin = NULL) { + bool restartImpl(const char* pin = nullptr) { if (!testAT()) { return false; } if (!setPhoneFunctionality(1, true)) { return false; } waitResponse(10000L, GF("APP RDY")); @@ -228,40 +246,51 @@ class TinyGsmBG96 : public TinyGsmModem, return waitResponse() == 1; } - bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) { + bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false, + uint32_t timeout_ms = 15500L) { sendAT(GF("+CFUN="), fun, reset ? ",1" : ""); - return waitResponse(10000L, GF("OK")) == 1; + return waitResponse(timeout_ms, GF("OK")) == 1; } /* * Generic network functions */ public: - RegStatus getRegistrationStatus() { + BG96RegStatus getRegistrationStatus() { // Check first for EPS registration - RegStatus epsStatus = (RegStatus)getRegistrationStatusXREG("CEREG"); + BG96RegStatus epsStatus = (BG96RegStatus)getRegistrationStatusXREG("CEREG"); // If we're connected on EPS, great! if (epsStatus == REG_OK_HOME || epsStatus == REG_OK_ROAMING) { return epsStatus; } else { // Otherwise, check generic network status - return (RegStatus)getRegistrationStatusXREG("CREG"); + return (BG96RegStatus)getRegistrationStatusXREG("CREG"); } } protected: bool isNetworkConnectedImpl() { - RegStatus s = getRegistrationStatus(); + BG96RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } + /* + * Secure socket layer (SSL) functions + */ + // Follows functions as inherited from TinyGsmSSL.tpp + + /* + * WiFi functions + */ + // No functions of this type supported + /* * GPRS functions */ protected: - bool gprsConnectImpl(const char* apn, const char* user = NULL, - const char* pwd = NULL) { + bool gprsConnectImpl(const char* apn, const char* user = nullptr, + const char* pwd = nullptr) { gprsDisconnect(); // Configure the TCPIP Context @@ -287,13 +316,22 @@ class TinyGsmBG96 : public TinyGsmModem, return true; } + String getProviderImpl() { + sendAT(GF("+QSPN?")); + if (waitResponse(GF("+QSPN:")) != 1) { return ""; } + streamSkipUntil('"'); // Skip mode and format + String res = stream.readStringUntil('"'); // read the provider + waitResponse(); // skip anything else + return res; + } + /* * SIM card functions */ protected: String getSimCCIDImpl() { sendAT(GF("+QCCID")); - if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { return ""; } + if (waitResponse(GF(AT_NL "+QCCID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -303,14 +341,17 @@ class TinyGsmBG96 : public TinyGsmModem, /* * Phone Call functions */ - protected: - // Can follow all of the phone call functions from the template + // Follows all phone call functions as inherited from TinyGsmCalling.tpp /* - * Messaging functions + * Audio functions */ - protected: - // Follows all messaging functions per template + // No functions of this type supported + + /* + * Text messaging (SMS) functions + */ + // Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp /* * GSM Location functions @@ -341,7 +382,7 @@ class TinyGsmBG96 : public TinyGsmModem, // get the RAW GPS output String getGPSrawImpl() { sendAT(GF("+QGPSLOC=2")); - if (waitResponse(10000L, GF(GSM_NL "+QGPSLOC:")) != 1) { return ""; } + if (waitResponse(10000L, GF(AT_NL "+QGPSLOC: ")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -354,7 +395,7 @@ class TinyGsmBG96 : public TinyGsmModem, 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) { + if (waitResponse(10000L, GF(AT_NL "+QGPSLOC: ")) != 1) { // NOTE: Will return an error if the position isn't fixed return false; } @@ -396,20 +437,20 @@ class TinyGsmBG96 : public TinyGsmModem, // 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 (lat != nullptr) *lat = ilat; + if (lon != nullptr) *lon = ilon; + if (speed != nullptr) *speed = ispeed; + if (alt != nullptr) *alt = ialt; + if (vsat != nullptr) *vsat = 0; + if (usat != nullptr) *usat = iusat; + if (accuracy != nullptr) *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); + if (year != nullptr) *year = iyear; + if (month != nullptr) *month = imonth; + if (day != nullptr) *day = iday; + if (hour != nullptr) *hour = ihour; + if (minute != nullptr) *minute = imin; + if (second != nullptr) *second = static_cast(secondWithSS); waitResponse(); // Final OK return true; @@ -437,6 +478,49 @@ class TinyGsmBG96 : public TinyGsmModem, 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 getNetworkUTCTimeImpl(int* year, int* month, int* day, int* hour, + int* minute, int* second, float* timezone) { + sendAT(GF("+QLTS=1")); + 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 != nullptr) *year = iyear; + if (month != nullptr) *month = imonth; + if (day != nullptr) *day = iday; + if (hour != nullptr) *hour = ihour; + if (minute != nullptr) *minute = imin; + if (second != nullptr) *second = isec; + if (timezone != nullptr) *timezone = static_cast(itimezone) / 4.0; + + // Final OK + waitResponse(); // Ends with OK + return true; + } + // 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. @@ -467,13 +551,13 @@ class TinyGsmBG96 : public TinyGsmModem, // 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; + if (year != nullptr) *year = iyear; + if (month != nullptr) *month = imonth; + if (day != nullptr) *day = iday; + if (hour != nullptr) *hour = ihour; + if (minute != nullptr) *minute = imin; + if (second != nullptr) *second = isec; + if (timezone != nullptr) *timezone = static_cast(itimezone) / 4.0; // Final OK waitResponse(); // Ends with OK @@ -501,11 +585,15 @@ class TinyGsmBG96 : public TinyGsmModem, String ShowNTPErrorImpl(byte error) TINY_GSM_ATTR_NOT_IMPLEMENTED; + /* + * BLE functions + */ + // No functions of this type supported + /* * Battery functions */ - protected: - // Can follow CBC as in the template + // Follows all battery functions as inherited from TinyGsmBattery.tpp /* * Temperature functions @@ -514,7 +602,7 @@ class TinyGsmBG96 : public TinyGsmModem, // get temperature in degree celsius uint16_t getTemperatureImpl() { sendAT(GF("+QTEMP")); - if (waitResponse(GF(GSM_NL "+QTEMP:")) != 1) { return 0; } + if (waitResponse(GF(AT_NL "+QTEMP:")) != 1) { return 0; } // return temperature in C uint16_t res = streamGetIntBefore(','); // read PMIC (primary ic) temperature @@ -530,39 +618,121 @@ class TinyGsmBG96 : public TinyGsmModem, */ 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!"); } - + int timeout_s = 150) { uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + bool ssl = sockets[mux]->ssl_sock; + + if (ssl) { + // set the ssl version + // AT+QSSLCFG="sslversion",, + // PDP context identifier + // 0: QAPI_NET_SSL_3.0 + // 1: QAPI_NET_SSL_PROTOCOL_TLS_1_0 + // 2: QAPI_NET_SSL_PROTOCOL_TLS_1_1 + // 3: QAPI_NET_SSL_PROTOCOL_TLS_1_2 + // 4: ALL + // NOTE: despite docs using caps, "sslversion" must be in lower case + sendAT(GF("+QSSLCFG=\"sslversion\",0,3")); // TLS 1.2 + if (waitResponse(5000L) != 1) return false; + // set the ssl cipher_suite + // AT+QSSLCFG="ciphersuite",, + // PDP context identifier + // 0: TODO + // 1: TODO + // 0X0035: TLS_RSA_WITH_AES_256_CBC_SHA + // 0XFFFF: ALL + // NOTE: despite docs using caps, "sslversion" must be in lower case + sendAT(GF( + "+QSSLCFG=\"ciphersuite\",0,0X0035")); // TLS_RSA_WITH_AES_256_CBC_SHA + if (waitResponse(5000L) != 1) return false; + // set the ssl sec level + // AT+QSSLCFG="seclevel",, + // PDP context identifier + // 0: TODO + // 1: TODO + // 0X0035: TLS_RSA_WITH_AES_256_CBC_SHA + // 0XFFFF: ALL + // NOTE: despite docs using caps, "sslversion" must be in lower case + sendAT(GF("+QSSLCFG=\"seclevel\",0,1")); + if (waitResponse(5000L) != 1) return false; + + + if (certificates[mux] != "") { + // apply the correct certificate to the connection + // AT+QSSLCFG="cacert",, + // PDP context identifier + // certificate name + + // sendAT(GF("+CASSLCFG="), mux, ",CACERT,\"", + // certificates[mux].c_str(), + // "\""); + sendAT(GF("+QSSLCFG=\"cacert\",0,\""), certificates[mux].c_str(), + GF("\"")); + if (waitResponse(5000L) != 1) return false; + } - // (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(); + // (1-16), (0-11), + // "TCP/UDP/TCP LISTENER/UDPSERVICE", "/", + // ,,(0-2; 0=buffer) + // may need previous AT+QSSLCFG + sendAT(GF("+QSSLOPEN=1,1,"), mux, GF(",\""), host, GF("\","), port, + GF(",0")); + waitResponse(); + + if (waitResponse(timeout_ms, GF(AT_NL "+QSSLOPEN:")) != 1) { + return false; + } + // 20230629 -> +QSSLOPEN: , + // clientID is mux + // err must be 0 + if (streamGetIntBefore(',') != mux) { return false; } + // Read status + return (0 == streamGetIntBefore('\n')); + } else { + // AT+QIOPEN=1,0,"TCP","220.180.239.212",8009,0,0 + // (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(); - if (waitResponse(timeout_ms, GF(GSM_NL "+QIOPEN:")) != 1) { return false; } + if (waitResponse(timeout_ms, GF(AT_NL "+QIOPEN:")) != 1) { return false; } - if (streamGetIntBefore(',') != mux) { return false; } + if (streamGetIntBefore(',') != mux) { return false; } + } // Read status return (0 == streamGetIntBefore('\n')); } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { - sendAT(GF("+QISEND="), mux, ',', (uint16_t)len); + bool ssl = sockets[mux]->ssl_sock; + if (ssl) { + sendAT(GF("+QSSLSEND="), mux, ',', (uint16_t)len); + } else { + sendAT(GF("+QISEND="), mux, ',', (uint16_t)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(AT_NL "SEND OK")) != 1) { return 0; } + // TODO(?): Wait for ACK? (AT+QISEND=id,0 or AT+QSSLSEND=id,0) return len; } size_t modemRead(size_t size, uint8_t mux) { if (!sockets[mux]) return 0; - sendAT(GF("+QIRD="), mux, ',', (uint16_t)size); - if (waitResponse(GF("+QIRD:")) != 1) { return 0; } + bool ssl = sockets[mux]->ssl_sock; + if (ssl) { + sendAT(GF("+QSSLRECV="), mux, ',', (uint16_t)size); + if (waitResponse(GF("+QSSLRECV:")) != 1) { + DBG("### READ: For unknown reason close"); + return 0; + } + } else { + sendAT(GF("+QIRD="), mux, ',', (uint16_t)size); + if (waitResponse(GF("+QIRD:")) != 1) { return 0; } + } int16_t len = streamGetIntBefore('\n'); for (int i = 0; i < len; i++) { moveCharFromStreamToFifo(mux); } @@ -574,146 +744,98 @@ class TinyGsmBG96 : public TinyGsmModem, size_t modemGetAvailable(uint8_t mux) { if (!sockets[mux]) return 0; - sendAT(GF("+QIRD="), mux, GF(",0")); + bool ssl = sockets[mux]->ssl_sock; size_t result = 0; - if (waitResponse(GF("+QIRD:")) == 1) { - streamSkipUntil(','); // Skip total received - streamSkipUntil(','); // Skip have read - result = streamGetIntBefore('\n'); - if (result) { DBG("### DATA AVAILABLE:", result, "on", mux); } - waitResponse(); + if (ssl) { + sendAT(GF("+QSSLRECV="), mux, GF(",0")); + if (waitResponse(GF("+QSSLRECV:")) == 1) { + streamSkipUntil(','); // Skip total received + streamSkipUntil(','); // Skip have read + result = streamGetIntBefore('\n'); + if (result) { DBG("### DATA AVAILABLE:", result, "on", mux); } + waitResponse(); + } + } else { + sendAT(GF("+QIRD="), mux, GF(",0")); + if (waitResponse(GF("+QIRD:")) == 1) { + 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); } 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" + bool ssl = sockets[mux]->ssl_sock; + if (ssl) { + sendAT(GF("+QSSLSTATE=1,"), mux); + // +QSSLSTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1" - if (waitResponse(GF("+QISTATE:")) != 1) { return false; } + if (waitResponse(GF("+QSSLSTATE:")) != 1) { return false; } - 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 + streamSkipUntil(','); // Skip clientID + streamSkipUntil(','); // Skip "SSLClient" + streamSkipUntil(','); // Skip remote ip + streamSkipUntil(','); // Skip remote port + streamSkipUntil(','); // Skip local port + int8_t res = streamGetIntBefore(','); // socket state - waitResponse(); + waitResponse(); + + // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing + return 2 == res; + } else { + sendAT(GF("+QISTATE=1,"), mux); + // +QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1" + + 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 + int8_t res = streamGetIntBefore(','); // socket state + + waitResponse(); - // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing - return 2 == res; + // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing + return 2 == res; + } } /* * Utilities */ public: - // TODO(vshymanskyy): Optimize this! - int8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - /*String r1s(r1); r1s.trim(); - String r2s(r2); r2s.trim(); - String r3s(r3); r3s.trim(); - String r4s(r4); r4s.trim(); - String r5s(r5); r5s.trim(); - DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ - data.reserve(64); - uint8_t index = 0; - uint32_t startMillis = millis(); - do { - TINY_GSM_YIELD(); - while (stream.available() > 0) { - TINY_GSM_YIELD(); - int8_t a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += static_cast(a); - if (r1 && data.endsWith(r1)) { - index = 1; - goto finish; - } else if (r2 && data.endsWith(r2)) { - index = 2; - goto finish; - } else if (r3 && data.endsWith(r3)) { -#if defined TINY_GSM_DEBUG - if (r3 == GFP(GSM_CME_ERROR)) { - streamSkipUntil('\n'); // Read out the error - } -#endif - index = 3; - goto finish; - } else if (r4 && data.endsWith(r4)) { - index = 4; - goto finish; - } else if (r5 && data.endsWith(r5)) { - index = 5; - goto finish; - } else if (data.endsWith(GF(GSM_NL "+QIURC:"))) { - streamSkipUntil('\"'); - String urc = stream.readStringUntil('\"'); - streamSkipUntil(','); - if (urc == "recv") { - 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") { - 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 { - streamSkipUntil('\n'); - } - data = ""; + bool handleURCs(String& data) { + if (data.endsWith(GF(AT_NL "+QIURC:"))) { + streamSkipUntil('\"'); + String urc = stream.readStringUntil('\"'); + streamSkipUntil(','); + if (urc == "recv") { + 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") { + 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 { + streamSkipUntil('\n'); } - } while (millis() - startMillis < timeout_ms); - finish: - if (!index) { - data.trim(); - if (data.length()) { DBG("### Unhandled:", data); } data = ""; + return true; } - // data.replace(GSM_NL, "/"); - // DBG('<', index, '>', data); - return index; - } - - int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - String data; - return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); - } - - int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - return waitResponse(1000, r1, r2, r3, r4, r5); + return false; } public: @@ -721,7 +843,7 @@ class TinyGsmBG96 : public TinyGsmModem, protected: GsmClientBG96* sockets[TINY_GSM_MUX_COUNT]; - const char* gsmNL = GSM_NL; + String certificates[TINY_GSM_MUX_COUNT]; }; #endif // SRC_TINYGSMCLIENTBG96_H_ diff --git a/src/TinyGsmClientESP8266.h b/src/TinyGsmClientESP8266.h index 92763a0c..85f352ca 100644 --- a/src/TinyGsmClientESP8266.h +++ b/src/TinyGsmClientESP8266.h @@ -14,38 +14,57 @@ #define TINY_GSM_MUX_COUNT 5 #define TINY_GSM_NO_MODEM_BUFFER +#ifdef AT_NL +#undef AT_NL +#endif +#define AT_NL "\r\n" + +#ifdef MODEM_MANUFACTURER +#undef MODEM_MANUFACTURER +#endif +#define MODEM_MANUFACTURER "Espressif" + +#ifdef MODEM_MODEL +#undef MODEM_MODEL +#endif +#if defined(TINY_GSM_MODEM_ESP8266) +#define MODEM_MODEL "ESP8266" +#else +#define MODEM_MODEL "ESP32" +#endif #include "TinyGsmModem.tpp" -#include "TinyGsmSSL.tpp" #include "TinyGsmTCP.tpp" +#include "TinyGsmSSL.tpp" #include "TinyGsmWifi.tpp" -#define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; -static uint8_t TINY_GSM_TCP_KEEP_ALIVE = 120; +static uint8_t TINY_GSM_TCP_KEEP_ALIVE = 120; // status of ESP8266 station interface +// 0: ESP8266 station is not initialized. +// 1: ESP8266 station is initialized, but not started a Wi-Fi connection yet. // 2 : ESP8266 station connected to an AP and has obtained IP // 3 : ESP8266 station created a TCP or UDP transmission // 4 : the TCP or UDP transmission of ESP8266 station disconnected // 5 : ESP8266 station did NOT connect to an AP -enum RegStatus { - REG_OK_IP = 2, - REG_OK_TCP = 3, - REG_OK_NO_TCP = 4, - REG_DENIED = 5, - REG_UNKNOWN = 6, +enum ESP8266RegStatus { + REG_UNINITIALIZED = 0, + REG_UNREGISTERED = 1, + REG_OK_IP = 2, + REG_OK_TCP = 3, + REG_OK_NO_TCP = 4, + REG_DENIED = 5, + REG_UNKNOWN = 6, }; class TinyGsmESP8266 : public TinyGsmModem, - public TinyGsmWifi, public TinyGsmTCP, - public TinyGsmSSL { + public TinyGsmSSL, + public TinyGsmWifi { friend class TinyGsmModem; - friend class TinyGsmWifi; friend class TinyGsmTCP; - friend class TinyGsmSSL; + friend class TinyGsmSSL; + friend class TinyGsmWifi; /* * Inner Client @@ -137,7 +156,7 @@ class TinyGsmESP8266 : public TinyGsmModem, * Basic functions */ protected: - bool initImpl(const char* pin = NULL) { + bool initImpl(const char* pin = nullptr) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); DBG(GF("### TinyGSM Compiled Module: TinyGsmClientESP8266")); @@ -159,11 +178,7 @@ class TinyGsmESP8266 : public TinyGsmModem, return true; } - String getModemNameImpl() { - return "ESP8266"; - } - - void setBaudImpl(uint32_t baud) { + bool setBaudImpl(uint32_t baud) { sendAT(GF("+UART_CUR="), baud, "8,1,0,0"); if (waitResponse() != 1) { sendAT(GF("+UART="), baud, @@ -171,35 +186,69 @@ class TinyGsmESP8266 : public TinyGsmModem, // if (waitResponse() != 1) { // sendAT(GF("+IPR="), baud); // First release firmwares might need // this - waitResponse(); + return waitResponse() == 1; // } } + return false; } - bool factoryDefaultImpl() { - sendAT(GF("+RESTORE")); - return waitResponse() == 1; + String getModemInfoImpl() { + sendAT(GF("+GMR")); + String res; + if (waitResponse(1000L, res) != 1) { return ""; } + cleanResponseString(res); + return res; } - String getModemInfoImpl() { + // Gets the modem hardware version + String getModemManufacturerImpl() { + return MODEM_MANUFACTURER; + } + + // Gets the modem hardware version + String getModemModelImpl() { + String model = MODEM_MODEL; sendAT(GF("+GMR")); + streamSkipUntil('\n'); // skip the AT version + streamSkipUntil('\n'); // skip the SDK version + streamSkipUntil('\n'); // skip the compile time + // read the hardware from the Bin version + streamSkipUntil('('); // skip the text "Bin version" + String wroom = stream.readStringUntil( + ')'); // read the WRoom version in the parethesis + streamSkipUntil('('); // skip the bin version itself + if (waitResponse(1000L) == 1) { // wait for the ending OK + return wroom; + } + return model; + } + + // Gets the modem firmware version + String getModemRevisionImpl() { + sendAT(GF("GMR")); // GMR instead of CGMR String res; if (waitResponse(1000L, res) != 1) { return ""; } - res.replace(GSM_NL "OK" GSM_NL, ""); - res.replace(GSM_NL, " "); - res.trim(); + cleanResponseString(res); return res; } + // Gets the modem serial number + String getModemSerialNumberImpl() TINY_GSM_ATTR_NOT_AVAILABLE; + + bool factoryDefaultImpl() { + sendAT(GF("+RESTORE")); + return waitResponse() == 1; + } + /* * Power functions */ protected: - bool restartImpl(const char* pin = NULL) { + bool restartImpl(const char* pin = nullptr) { 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, GF(AT_NL "ready" AT_NL)) != 1) { return false; } delay(500); return init(pin); } @@ -220,13 +269,18 @@ class TinyGsmESP8266 : public TinyGsmModem, * Generic network functions */ public: - RegStatus getRegistrationStatus() { + ESP8266RegStatus getRegistrationStatus() { sendAT(GF("+CIPSTATUS")); if (waitResponse(3000, GF("STATUS:")) != 1) return REG_UNKNOWN; - 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; + // after "STATUS:" it should return the status number (0,1,2,3,4,5), + // followed by an OK + // Since there are more possible status number codes than the arguments for + // waitResponse, we'll capture the response in a string and then parse it. + String res; + if (waitResponse(3000L, res) != 1) { return REG_UNKNOWN; } + res.trim(); + int8_t status = res.toInt(); + return (ESP8266RegStatus)status; } protected: @@ -252,7 +306,7 @@ class TinyGsmESP8266 : public TinyGsmModem, } bool isNetworkConnectedImpl() { - RegStatus s = getRegistrationStatus(); + ESP8266RegStatus s = getRegistrationStatus(); if (s == REG_OK_IP || s == REG_OK_TCP) { // with these, we're definitely connected return true; @@ -285,6 +339,12 @@ class TinyGsmESP8266 : public TinyGsmModem, return res2; } + /* + * Secure socket layer (SSL) functions + */ + // Follows functions as inherited from TinyGsmSSL.tpp + + /* * WiFi functions */ @@ -293,9 +353,9 @@ class TinyGsmESP8266 : public TinyGsmModem, // attempt first without than with the 'current' flag used in some firmware // versions sendAT(GF("+CWJAP=\""), ssid, GF("\",\""), pwd, GF("\"")); - if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) { + if (waitResponse(30000L, GFP(GSM_OK), GF(AT_NL "FAIL" AT_NL)) != 1) { sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\"")); - if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) { + if (waitResponse(30000L, GFP(GSM_OK), GF(AT_NL "FAIL" AT_NL)) != 1) { return false; } } @@ -310,6 +370,62 @@ class TinyGsmESP8266 : public TinyGsmModem, return retVal; } + + /* + * GPRS functions + */ + // No functions of this type supported + + /* + * SIM card functions + */ + // No functions of this type supported + + /* + * Audio functions + */ + // No functions of this type supported + + /* + * Text messaging (SMS) functions + */ + // No functions of this type supported + + /* + * GSM Location functions + */ + // No functions of this type supported + + /* + * GPS/GNSS/GLONASS location functions + */ + // No functions of this type supported + + /* + * Time functions + */ + // No functions of this type supported + + /* + * NTP server functions + */ + // No functions of this type supported + + /* + * BLE functions + */ + // No functions of this type supported + + /* + * Battery functions + */ + // No functions of this type supported + + /* + * Temperature functions + */ + // No functions of this type supported + /* * Client related functions */ @@ -337,18 +453,19 @@ class TinyGsmESP8266 : public TinyGsmModem, 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(AT_NL "SEND OK" AT_NL)) != 1) { return 0; } return len; } bool modemGetConnected(uint8_t mux) { sendAT(GF("+CIPSTATUS")); 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 + // after "STATUS:" it should return the status number (0,1,2,3,4,5), + // followed by an OK + // Hopefully we'll catch the "3" here, but fall back to the OK or Error + int8_t status = waitResponse(GF("3"), GFP(GSM_OK), GFP(GSM_ERROR)); + // if the status is anything but 3, there are no connections open + if (status != 1) { for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) { if (sockets[muxNo]) { sockets[muxNo]->sock_connected = false; } } @@ -382,96 +499,41 @@ class TinyGsmESP8266 : public TinyGsmModem, * Utilities */ public: - // TODO(vshymanskyy): Optimize this! - int8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL, - GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { - /*String r1s(r1); r1s.trim(); - String r2s(r2); r2s.trim(); - String r3s(r3); r3s.trim(); - String r4s(r4); r4s.trim(); - String r5s(r5); r5s.trim(); - DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ - data.reserve(64); - uint8_t index = 0; - uint32_t startMillis = millis(); - do { - TINY_GSM_YIELD(); - while (stream.available() > 0) { - TINY_GSM_YIELD(); - int8_t a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += static_cast(a); - if (r1 && data.endsWith(r1)) { - index = 1; - goto finish; - } else if (r2 && data.endsWith(r2)) { - index = 2; - goto finish; - } else if (r3 && data.endsWith(r3)) { - index = 3; - goto finish; - } else if (r4 && data.endsWith(r4)) { - index = 4; - goto finish; - } else if (r5 && data.endsWith(r5)) { - index = 5; - goto finish; - } else if (data.endsWith(GF("+IPD,"))) { - int8_t mux = streamGetIntBefore(','); - int16_t len = streamGetIntBefore(':'); - int16_t len_orig = len; - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - if (len > sockets[mux]->rx.free()) { - DBG("### Buffer overflow: ", len, "received vs", - sockets[mux]->rx.free(), "available"); - } else { - // DBG("### Got Data: ", len, "on", mux); - } - 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"))) { - 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; - } - data = ""; - DBG("### Closed: ", mux); + bool handleURCs(String& data) { + if (data.endsWith(GF("+IPD,"))) { + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore(':'); + int16_t len_orig = len; + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + if (len > sockets[mux]->rx.free()) { + DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free()); + // reset the len to read to the amount free + len = sockets[mux]->rx.free(); + } + while (len--) { moveCharFromStreamToFifo(mux); } + // TODO(SRGDamia1): deal with buffer overflow/missed characters + if (len_orig != sockets[mux]->available()) { + DBG("### Different number of characters received than expected: ", + sockets[mux]->available(), " vs ", len_orig); } } - } while (millis() - startMillis < timeout_ms); - finish: - if (!index) { - data.trim(); - if (data.length()) { DBG("### Unhandled:", data); } data = ""; + DBG("### Got Data: ", len_orig, "on", mux); + return true; + } else if (data.endsWith(GF("CLOSED"))) { + int8_t muxStart = TinyGsmMax(0, + data.lastIndexOf(AT_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; + } + streamSkipUntil('\n'); // throw away the new line + data = ""; + DBG("### Closed: ", mux); + return true; } - // data.replace(GSM_NL, "/"); - // DBG('<', index, '>', data); - return index; - } - - int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL, - GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { - String data; - return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); - } - - int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL, - GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) { - return waitResponse(1000, r1, r2, r3, r4, r5); + return false; } public: @@ -479,7 +541,6 @@ class TinyGsmESP8266 : public TinyGsmModem, protected: GsmClientESP8266* sockets[TINY_GSM_MUX_COUNT]; - const char* gsmNL = GSM_NL; }; #endif // SRC_TINYGSMCLIENTESP8266_H_ diff --git a/src/TinyGsmClientM590.h b/src/TinyGsmClientM590.h index e9214471..14586132 100644 --- a/src/TinyGsmClientM590.h +++ b/src/TinyGsmClientM590.h @@ -14,22 +14,28 @@ #define TINY_GSM_MUX_COUNT 2 #define TINY_GSM_NO_MODEM_BUFFER +#ifdef AT_NL +#undef AT_NL +#endif +#define AT_NL "\r\n" + +#ifdef MODEM_MANUFACTURER +#undef MODEM_MANUFACTURER +#endif +#define MODEM_MANUFACTURER "Neoway" + +#ifdef MODEM_MODEL +#undef MODEM_MODEL +#endif +#define MODEM_MODEL "M590" -#include "TinyGsmGPRS.tpp" #include "TinyGsmModem.tpp" -#include "TinyGsmSMS.tpp" #include "TinyGsmTCP.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmSMS.tpp" #include "TinyGsmTime.tpp" -#define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; -#if defined TINY_GSM_DEBUG -static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; -static const char GSM_CMS_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CMS ERROR:"; -#endif - -enum RegStatus { +enum M590RegStatus { REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 3, @@ -106,6 +112,11 @@ class TinyGsmM590 : public TinyGsmModem, String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; }; + /* + * Inner Secure Client + */ + // NOT SUPPORTED + /* * Constructor */ @@ -118,7 +129,7 @@ class TinyGsmM590 : public TinyGsmModem, * Basic functions */ protected: - bool initImpl(const char* pin = NULL) { + bool initImpl(const char* pin = nullptr) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); DBG(GF("### TinyGSM Compiled Module: TinyGsmClientM590")); @@ -138,7 +149,7 @@ class TinyGsmM590 : public TinyGsmModem, 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) { + if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); } else { @@ -148,9 +159,50 @@ class TinyGsmM590 : public TinyGsmModem, } } - // Doesn't support CGMI + // This is extracted from the modem info String getModemNameImpl() { - return "Neoway M590"; + sendAT(GF("I")); + String factory = stream.readStringUntil('\n'); // read the factory + factory.trim(); + String model = stream.readStringUntil('\n'); // read the model + model.trim(); + streamSkipUntil('\n'); // skip the revision + waitResponse(); // wait for the OK + return factory + String(" ") + model; + } + + // This is extracted from the modem info + String getModemManufacturerImpl() { + sendAT(GF("I")); + String factory = stream.readStringUntil('\n'); // read the factory + factory.trim(); + streamSkipUntil('\n'); // skip the model + streamSkipUntil('\n'); // skip the revision + if (waitResponse() == 1) { return factory; } + return MODEM_MANUFACTURER; + } + + // This is extracted from the modem info + String getModemModelImpl() { + sendAT(GF("I")); + streamSkipUntil('\n'); // skip the factory + String model = stream.readStringUntil('\n'); // read the model + model.trim(); + streamSkipUntil('\n'); // skip the revision + if (waitResponse() == 1) { return model; } + return MODEM_MODEL; + } + + // Gets the modem firmware version + // This is extracted from the modem info + String getModemRevisionImpl() { + sendAT(GF("I")); + streamSkipUntil('\n'); // skip the factory + streamSkipUntil('\n'); // skip the model + String res = stream.readStringUntil('\n'); // read the revision + res.trim(); + waitResponse(); // wait for the OK + return res; } // Extra stuff here - pwr save, internal stack @@ -171,11 +223,11 @@ class TinyGsmM590 : public TinyGsmModem, * Power functions */ protected: - bool restartImpl(const char* pin = NULL) { + bool restartImpl(const char* pin = nullptr) { if (!testAT()) { return false; } if (!setPhoneFunctionality(15)) { return false; } // MODEM:STARTUP - waitResponse(60000L, GF(GSM_NL "+PBREADY" GSM_NL)); + waitResponse(60000L, GF(AT_NL "+PBREADY" AT_NL)); return init(pin); } @@ -198,19 +250,19 @@ class TinyGsmM590 : public TinyGsmModem, * Generic network functions */ public: - RegStatus getRegistrationStatus() { - return (RegStatus)getRegistrationStatusXREG("CREG"); + M590RegStatus getRegistrationStatus() { + return (M590RegStatus)getRegistrationStatusXREG("CREG"); } protected: bool isNetworkConnectedImpl() { - RegStatus s = getRegistrationStatus(); + M590RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } String getLocalIPImpl() { sendAT(GF("+XIIC?")); - if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { return ""; } + if (waitResponse(GF(AT_NL "+XIIC:")) != 1) { return ""; } streamSkipUntil(','); String res = stream.readStringUntil('\n'); waitResponse(); @@ -218,12 +270,22 @@ class TinyGsmM590 : public TinyGsmModem, return res; } + /* + * Secure socket layer (SSL) functions + */ + // No functions of this type supported + + /* + * WiFi functions + */ + // No functions of this type supported + /* * GPRS functions */ protected: - bool gprsConnectImpl(const char* apn, const char* user = NULL, - const char* pwd = NULL) { + bool gprsConnectImpl(const char* apn, const char* user = nullptr, + const char* pwd = nullptr) { gprsDisconnect(); sendAT(GF("+XISP=0")); @@ -268,7 +330,7 @@ class TinyGsmM590 : public TinyGsmModem, bool isGprsConnectedImpl() { sendAT(GF("+XIIC?")); - if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { return false; } + if (waitResponse(GF(AT_NL "+XIIC:")) != 1) { return false; } int8_t res = streamGetIntBefore(','); waitResponse(); return res == 1; @@ -278,20 +340,69 @@ class TinyGsmM590 : public TinyGsmModem, * SIM card functions */ protected: - // Able to follow all SIM card functions as inherited from the template + // Able to follow all SIM card functions as inherited from TinyGsmGPRS.tpp + /* - * Messaging functions + * Phone Call functions + */ + // No functions of this type supported + + /* + * Audio functions + */ + // No functions of this type supported + + /* + * Text messaging (SMS) functions */ protected: bool sendSMS_UTF16Impl(const String& number, const void* text, size_t len) TINY_GSM_ATTR_NOT_AVAILABLE; + /* + * GSM Location functions + */ + // No functions of this type supported + + /* + * GPS/GNSS/GLONASS location functions + */ + // No functions of this type supported + /* * Time functions */ - protected: - // Can follow the standard CCLK function in the template + // Follows all clock functions as inherited from TinyGsmTime.tpp + /* + * NTP server functions + */ + // Follows all NTP server functions as inherited from TinyGsmNTP.tpp + + /* + * BLE functions + */ + // No functions of this type supported + + /* + * NTP server functions + */ + // No functions of this type supported + + /* + * BLE functions + */ + // No functions of this type supported + + /* + * Battery functions + */ + // No functions of this type supported + + /* + * Temperature functions + */ + // No functions of this type supported /* * Client related functions @@ -304,9 +415,8 @@ class TinyGsmM590 : public TinyGsmModem, String ip = dnsIpQuery(host); sendAT(GF("+TCPSETUP="), mux, GF(","), ip, GF(","), port); - int8_t 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" AT_NL), GF(",FAIL" AT_NL), + GF("+TCPSETUP:Error" AT_NL)); if (1 == rsp) { return true; } else if (3 == rsp) { @@ -324,7 +434,7 @@ class TinyGsmM590 : public TinyGsmModem, stream.write(reinterpret_cast(buff), len); stream.write(static_cast(0x0D)); stream.flush(); - if (waitResponse(30000L, GF(GSM_NL "+TCPSEND:")) != 1) { return 0; } + if (waitResponse(30000L, GF(AT_NL "+TCPSEND:")) != 1) { return 0; } streamSkipUntil('\n'); return len; } @@ -339,9 +449,9 @@ class TinyGsmM590 : public TinyGsmModem, String dnsIpQuery(const char* host) { sendAT(GF("+DNS=\""), host, GF("\"")); - if (waitResponse(10000L, GF(GSM_NL "+DNS:")) != 1) { return ""; } + if (waitResponse(10000L, GF(AT_NL "+DNS:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); - waitResponse(GF("+DNS:OK" GSM_NL)); + waitResponse(GF("+DNS:OK" AT_NL)); res.trim(); return res; } @@ -350,116 +460,38 @@ class TinyGsmM590 : public TinyGsmModem, * Utilities */ public: - // TODO(vshymanskyy): Optimize this! - int8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - /*String r1s(r1); r1s.trim(); - String r2s(r2); r2s.trim(); - String r3s(r3); r3s.trim(); - String r4s(r4); r4s.trim(); - String r5s(r5); r5s.trim(); - DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ - data.reserve(64); - uint8_t index = 0; - uint32_t startMillis = millis(); - do { - TINY_GSM_YIELD(); - while (stream.available() > 0) { - TINY_GSM_YIELD(); - int8_t a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += static_cast(a); - if (r1 && data.endsWith(r1)) { - index = 1; - goto finish; - } else if (r2 && data.endsWith(r2)) { - index = 2; - goto finish; - } else if (r3 && data.endsWith(r3)) { -#if defined TINY_GSM_DEBUG - if (r3 == GFP(GSM_CME_ERROR)) { - streamSkipUntil('\n'); // Read out the error - } -#endif - index = 3; - goto finish; - } else if (r4 && data.endsWith(r4)) { - index = 4; - goto finish; - } else if (r5 && data.endsWith(r5)) { - index = 5; - goto finish; - } else if (data.endsWith(GF("+TCPRECV:"))) { - int8_t mux = streamGetIntBefore(','); - int16_t len = streamGetIntBefore(','); - int16_t len_orig = len; - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - if (len > sockets[mux]->rx.free()) { - DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free()); - } else { - DBG("### Got: ", len, "->", sockets[mux]->rx.free()); - } - 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:"))) { - int8_t mux = streamGetIntBefore(','); - streamSkipUntil('\n'); - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->sock_connected = false; - } - data = ""; - DBG("### Closed: ", mux); + bool handleURCs(String& data) { + if (data.endsWith(GF("+TCPRECV:"))) { + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore(','); + int16_t len_orig = len; + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + if (len > sockets[mux]->rx.free()) { + DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free()); + // reset the len to read to the amount free + len = sockets[mux]->rx.free(); + } + while (len--) { moveCharFromStreamToFifo(mux); } + // TODO(?): Handle lost characters + if (len_orig != sockets[mux]->available()) { + DBG("### Different number of characters received than expected: ", + sockets[mux]->available(), " vs ", len_orig); } } - } while (millis() - startMillis < timeout_ms); - finish: - if (!index) { - data.trim(); - if (data.length()) { DBG("### Unhandled:", data); } data = ""; + DBG("### Got Data: ", len_orig, "on", mux); + return true; + } else if (data.endsWith(GF("+TCPCLOSE:"))) { + int8_t mux = streamGetIntBefore(','); + streamSkipUntil('\n'); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->sock_connected = false; + } + data = ""; + DBG("### Closed: ", mux); + return true; } - // data.replace(GSM_NL, "/"); - // DBG('<', index, '>', data); - return index; - } - - int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - String data; - return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); - } - - int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - return waitResponse(1000, r1, r2, r3, r4, r5); + return false; } public: @@ -467,7 +499,6 @@ class TinyGsmM590 : public TinyGsmModem, protected: GsmClientM590* sockets[TINY_GSM_MUX_COUNT]; - const char* gsmNL = GSM_NL; }; #endif // SRC_TINYGSMCLIENTM590_H_ diff --git a/src/TinyGsmClientM95.h b/src/TinyGsmClientM95.h index 538969a9..0104392e 100644 --- a/src/TinyGsmClientM95.h +++ b/src/TinyGsmClientM95.h @@ -15,25 +15,31 @@ #define TINY_GSM_MUX_COUNT 6 #define TINY_GSM_BUFFER_READ_NO_CHECK +#ifdef AT_NL +#undef AT_NL +#endif +#define AT_NL "\r\n" + +#ifdef MODEM_MANUFACTURER +#undef MODEM_MANUFACTURER +#endif +#define MODEM_MANUFACTURER "Quectel" + +#ifdef MODEM_MODEL +#undef MODEM_MODEL +#endif +#define MODEM_MODEL "M95" -#include "TinyGsmBattery.tpp" -#include "TinyGsmCalling.tpp" -#include "TinyGsmGPRS.tpp" #include "TinyGsmModem.tpp" -#include "TinyGsmSMS.tpp" #include "TinyGsmTCP.tpp" -#include "TinyGsmTemperature.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmCalling.tpp" +#include "TinyGsmSMS.tpp" #include "TinyGsmTime.tpp" +#include "TinyGsmBattery.tpp" +#include "TinyGsmTemperature.tpp" -#define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; -#if defined TINY_GSM_DEBUG -static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; -static const char GSM_CMS_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CMS ERROR:"; -#endif - -enum RegStatus { +enum M95RegStatus { REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, @@ -121,29 +127,7 @@ class TinyGsmM95 : public TinyGsmModem, /* * Inner Secure Client */ - - /* - class GsmClientSecureM95 : public GsmClientM95 - { - public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmm95& modem, uint8_t mux = 0) - : GsmClient(modem, mux) - {} - - - 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 - }; - */ + // NOT SUPPORTED /* * Constructor @@ -157,7 +141,7 @@ class TinyGsmM95 : public TinyGsmModem, * Basic functions */ protected: - bool initImpl(const char* pin = NULL) { + bool initImpl(const char* pin = nullptr) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); DBG(GF("### TinyGSM Compiled Module: TinyGsmClientM95")); @@ -184,7 +168,7 @@ class TinyGsmM95 : public TinyGsmModem, 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) { + if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); } else { @@ -198,7 +182,7 @@ class TinyGsmM95 : public TinyGsmModem, * Power functions */ protected: - bool restartImpl(const char* pin = NULL) { + bool restartImpl(const char* pin = nullptr) { if (!testAT()) { return false; } sendAT(GF("+CFUN=0")); if (waitResponse(10000L, GF("NORMAL POWER DOWN"), GF("OK"), GF("FAIL")) == @@ -234,13 +218,13 @@ class TinyGsmM95 : public TinyGsmModem, * Generic network functions */ public: - RegStatus getRegistrationStatus() { - return (RegStatus)getRegistrationStatusXREG("CREG"); + M95RegStatus getRegistrationStatus() { + return (M95RegStatus)getRegistrationStatusXREG("CREG"); } protected: bool isNetworkConnectedImpl() { - RegStatus s = getRegistrationStatus(); + M95RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } @@ -261,12 +245,22 @@ class TinyGsmM95 : public TinyGsmModem, return res; } + /* + * Secure socket layer (SSL) functions + */ + // No functions of this type supported + + /* + * WiFi functions + */ + // No functions of this type supported + /* * GPRS functions */ protected: - bool gprsConnectImpl(const char* apn, const char* user = NULL, - const char* pwd = NULL) { + bool gprsConnectImpl(const char* apn, const char* user = nullptr, + const char* pwd = nullptr) { gprsDisconnect(); // select foreground context 0 = VIRTUAL_UART_1 @@ -340,13 +334,22 @@ class TinyGsmM95 : public TinyGsmModem, return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; } + String getProviderImpl() { + sendAT(GF("+QSPN?")); + if (waitResponse(GF("+QSPN:")) != 1) { return ""; } + streamSkipUntil('"'); // Skip mode and format + String res = stream.readStringUntil('"'); // read the provider + waitResponse(); // skip anything else + return res; + } + /* * SIM card functions */ protected: String getSimCCIDImpl() { sendAT(GF("+QCCID")); - if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { return ""; } + if (waitResponse(GF(AT_NL "+QCCID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -356,14 +359,17 @@ class TinyGsmM95 : public TinyGsmModem, /* * Phone Call functions */ - protected: - // Can follow all of the phone call functions from the template + // Follows all phone call functions as inherited from TinyGsmCalling.tpp + /* - * Messaging functions + * Audio functions */ - protected: - // Can follow all template functions + // No functions of this type supported + /* + * Text messaging (SMS) functions + */ + // Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp public: /** Delete all SMS */ @@ -375,16 +381,45 @@ class TinyGsmM95 : public TinyGsmModem, return false; } + /* + * GSM Location functions + */ + // No functions of this type supported + + /* + * GPS/GNSS/GLONASS location functions + */ + // No functions of this type supported + /* * Time functions */ - protected: - // Can follow the standard CCLK function in the template + // Follows all clock functions as inherited from TinyGsmTime.tpp + + /* + * NTP server functions + */ + // Follows all NTP server functions as inherited from TinyGsmNTP.tpp + + /* + * BLE functions + */ + // No functions of this type supported + + /* + * NTP server functions + */ + // No functions of this type supported + + /* + * BLE functions + */ + // No functions of this type supported /* * Battery functions */ - // Can follow the battery functions in the template + // Follows all battery functions as inherited from TinyGsmBattery.tpp /* * Temperature functions @@ -392,7 +427,7 @@ class TinyGsmM95 : public TinyGsmModem, protected: float getTemperatureImpl() { sendAT(GF("+QTEMP?")); - if (waitResponse(GF(GSM_NL "+QTEMP:")) != 1) { + if (waitResponse(GF(AT_NL "+QTEMP:")) != 1) { return static_cast(-9999); } streamSkipUntil(','); // Skip mode @@ -415,9 +450,9 @@ class TinyGsmM95 : public TinyGsmModem, 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)); + int8_t rsp = waitResponse(timeout_ms, GF("CONNECT OK" AT_NL), + GF("CONNECT FAIL" AT_NL), + GF("ALREADY CONNECT" AT_NL)); return (1 == rsp); } @@ -426,13 +461,13 @@ class TinyGsmM95 : public TinyGsmModem, 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(AT_NL "SEND OK")) != 1) { return 0; } // bool allAcknowledged = false; // // bool failed = false; // while ( !allAcknowledged ) { // sendAT( GF("+QISACK")); - // if (waitResponse(5000L, GF(GSM_NL "+QISACK:")) != 1) { + // if (waitResponse(5000L, GF(AT_NL "+QISACK:")) != 1) { // return -1; // } else { // streamSkipUntil(','); // Skip total length sent on connection @@ -515,114 +550,36 @@ class TinyGsmM95 : public TinyGsmModem, * Utilities */ public: - // TODO(vshymanskyy): Optimize this! - int8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - /*String r1s(r1); r1s.trim(); - String r2s(r2); r2s.trim(); - String r3s(r3); r3s.trim(); - String r4s(r4); r4s.trim(); - String r5s(r5); r5s.trim(); - DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ - data.reserve(64); - uint8_t index = 0; - uint32_t startMillis = millis(); - do { - TINY_GSM_YIELD(); - while (stream.available() > 0) { - TINY_GSM_YIELD(); - int8_t a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += static_cast(a); - if (r1 && data.endsWith(r1)) { - index = 1; - goto finish; - } else if (r2 && data.endsWith(r2)) { - index = 2; - goto finish; - } else if (r3 && data.endsWith(r3)) { -#if defined TINY_GSM_DEBUG - if (r3 == GFP(GSM_CME_ERROR)) { - streamSkipUntil('\n'); // Read out the error - } -#endif - index = 3; - goto finish; - } else if (r4 && data.endsWith(r4)) { - index = 4; - goto finish; - } else if (r5 && data.endsWith(r5)) { - index = 5; - goto finish; - } else if (data.endsWith(GF(GSM_NL "+QIRDI:"))) { - streamSkipUntil(','); // Skip the context - streamSkipUntil(','); // Skip the role - 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. - sockets[mux]->sock_available = 1500; - } - data = ""; - } else if (data.endsWith(GF("CLOSED" GSM_NL))) { - 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."); - } + bool handleURCs(String& data) { + if (data.endsWith(GF(AT_NL "+QIRDI:"))) { + streamSkipUntil(','); // Skip the context + streamSkipUntil(','); // Skip the role + 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. + sockets[mux]->sock_available = 1500; } - } while (millis() - startMillis < timeout_ms); - finish: - if (!index) { - data.trim(); - if (data.length()) { DBG("### Unhandled:", data); } data = ""; + return true; + } else if (data.endsWith(GF("CLOSED" AT_NL))) { + int8_t nl = data.lastIndexOf(AT_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); + return true; + } else if (data.endsWith(GF("+QNITZ:"))) { + streamSkipUntil('\n'); // URC for time sync + data = ""; + DBG("### Network time updated."); + return true; } - // data.replace(GSM_NL, "/"); - // DBG('<', index, '>', data); - return index; - } - - int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - String data; - return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); - } - - int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - return waitResponse(1000, r1, r2, r3, r4, r5); + return false; } public: @@ -630,7 +587,6 @@ class TinyGsmM95 : public TinyGsmModem, protected: GsmClientM95* sockets[TINY_GSM_MUX_COUNT]; - const char* gsmNL = GSM_NL; }; #endif // SRC_TINYGSMCLIENTM95_H_ diff --git a/src/TinyGsmClientMC60.h b/src/TinyGsmClientMC60.h index abc54884..11560687 100644 --- a/src/TinyGsmClientMC60.h +++ b/src/TinyGsmClientMC60.h @@ -17,24 +17,34 @@ #define TINY_GSM_MUX_COUNT 6 #define TINY_GSM_BUFFER_READ_NO_CHECK +#ifdef AT_NL +#undef AT_NL +#endif +#define AT_NL "\r\n" + +#ifdef MODEM_MANUFACTURER +#undef MODEM_MANUFACTURER +#endif +#define MODEM_MANUFACTURER "Quectel" + +#ifdef MODEM_MODEL +#undef MODEM_MODEL +#endif +#if defined(TINY_GSM_MODEM_MC60E) +#define MODEM_MODEL "MC60E" +#else +#define MODEM_MODEL "MC60" +#endif -#include "TinyGsmBattery.tpp" -#include "TinyGsmCalling.tpp" -#include "TinyGsmGPRS.tpp" #include "TinyGsmModem.tpp" -#include "TinyGsmSMS.tpp" #include "TinyGsmTCP.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmCalling.tpp" +#include "TinyGsmSMS.tpp" #include "TinyGsmTime.tpp" +#include "TinyGsmBattery.tpp" -#define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; -#if defined TINY_GSM_DEBUG -static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; -static const char GSM_CMS_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CMS ERROR:"; -#endif - -enum RegStatus { +enum MC60RegStatus { REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, @@ -120,29 +130,7 @@ class TinyGsmMC60 : public TinyGsmModem, /* * Inner Secure Client */ - - /* - class GsmClientSecureMC60 : public GsmClientMC60 - { - public: - GsmClientSecure() {} - - GsmClientSecure(TinyGsmMC60& modem, uint8_t mux = 0) - : GsmClient(modem, mux) - {} - - - 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 - }; - */ + // NOT SUPPORTED /* * Constructor @@ -156,7 +144,7 @@ class TinyGsmMC60 : public TinyGsmModem, * Basic functions */ protected: - bool initImpl(const char* pin = NULL) { + bool initImpl(const char* pin = nullptr) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); DBG(GF("### TinyGSM Compiled Module: TinyGsmClientMC60")); @@ -183,7 +171,7 @@ class TinyGsmMC60 : public TinyGsmModem, 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) { + if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); } else { @@ -197,7 +185,7 @@ class TinyGsmMC60 : public TinyGsmModem, * Power functions */ protected: - bool restartImpl(const char* pin = NULL) { + bool restartImpl(const char* pin = nullptr) { if (!testAT()) { return false; } if (!setPhoneFunctionality(0)) { return false; } if (!setPhoneFunctionality(1, true)) { return false; } @@ -229,13 +217,13 @@ class TinyGsmMC60 : public TinyGsmModem, * Generic network functions */ public: - RegStatus getRegistrationStatus() { - return (RegStatus)getRegistrationStatusXREG("CREG"); + MC60RegStatus getRegistrationStatus() { + return (MC60RegStatus)getRegistrationStatusXREG("CREG"); } protected: bool isNetworkConnectedImpl() { - RegStatus s = getRegistrationStatus(); + MC60RegStatus s = getRegistrationStatus(); return (s == REG_OK_HOME || s == REG_OK_ROAMING); } @@ -247,12 +235,22 @@ class TinyGsmMC60 : public TinyGsmModem, return res; } + /* + * Secure socket layer (SSL) functions + */ + // No functions of this type supported + + /* + * WiFi functions + */ + // No functions of this type supported + /* * GPRS functions */ protected: - bool gprsConnectImpl(const char* apn, const char* user = NULL, - const char* pwd = NULL) { + bool gprsConnectImpl(const char* apn, const char* user = nullptr, + const char* pwd = nullptr) { gprsDisconnect(); // select foreground context 0 = VIRTUAL_UART_1 @@ -309,6 +307,15 @@ class TinyGsmMC60 : public TinyGsmModem, return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1; } + String getProviderImpl() { + sendAT(GF("+QSPN?")); + if (waitResponse(GF("+QSPN:")) != 1) { return ""; } + streamSkipUntil('"'); // Skip mode and format + String res = stream.readStringUntil('"'); // read the provider + waitResponse(); // skip anything else + return res; + } + /* * SIM card functions */ @@ -316,7 +323,7 @@ class TinyGsmMC60 : public TinyGsmModem, 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) { + if (waitResponse(GF(AT_NL "+CPIN:")) != 1) { delay(1000); continue; } @@ -339,14 +346,17 @@ class TinyGsmMC60 : public TinyGsmModem, /* * Phone Call functions */ - protected: - // Can follow all of the phone call functions from the template + // Follows all phone call functions as inherited from TinyGsmCalling.tpp /* - * Messaging functions + * Audio functions */ - protected: - // Can follow all template functions + // No functions of this type supported + + /* + * Text messaging (SMS) functions + */ + // Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp public: /** Delete all SMS */ @@ -358,16 +368,50 @@ class TinyGsmMC60 : public TinyGsmModem, return false; } + /* + * GSM Location functions + */ + // No functions of this type supported + + /* + * GPS/GNSS/GLONASS location functions + */ + // No functions of this type supported + /* * Time functions */ - protected: - // Can follow the standard CCLK function in the template + // Follows all clock functions as inherited from TinyGsmTime.tpp + + /* + * NTP server functions + */ + // No functions of this type supported + + /* + * BLE functions + */ + // No functions of this type supported + + /* + * NTP server functions + */ + // No functions of this type supported + + /* + * BLE functions + */ + // No functions of this type supported /* * Battery functions */ - // Can follow battery functions as in the template + // Follows all battery functions as inherited from TinyGsmBattery.tpp + + /* + * Temperature functions + */ + // No functions of this type supported /* * Client related functions @@ -388,9 +432,9 @@ class TinyGsmMC60 : public TinyGsmModem, 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)); + int8_t rsp = waitResponse(timeout_ms, GF("CONNECT OK" AT_NL), + GF("CONNECT FAIL" AT_NL), + GF("ALREADY CONNECT" AT_NL)); return (1 == rsp); } @@ -399,14 +443,14 @@ class TinyGsmMC60 : public TinyGsmModem, 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(AT_NL "SEND OK")) != 1) { return 0; } bool allAcknowledged = false; // bool failed = false; while (!allAcknowledged) { sendAT(GF("+QISACK="), mux); // If 'mux' is not specified, MC60 returns // 'ERRROR' (for QIMUX == 1) - if (waitResponse(5000L, GF(GSM_NL "+QISACK:")) != 1) { + if (waitResponse(5000L, GF(AT_NL "+QISACK:")) != 1) { return -1; } else { streamSkipUntil(','); /** Skip total */ @@ -488,127 +532,43 @@ class TinyGsmMC60 : public TinyGsmModem, * Utilities */ public: - // TODO(vshymanskyy): Optimize this! - int8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL, GsmConstStr r6 = NULL) { - /*String r1s(r1); r1s.trim(); - String r2s(r2); r2s.trim(); - String r3s(r3); r3s.trim(); - String r4s(r4); r4s.trim(); - String r5s(r5); r5s.trim(); - String r6s(r6); r6s.trim(); - DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s, ",", r6s);*/ - data.reserve(64); - uint8_t index = 0; - uint32_t startMillis = millis(); - do { - TINY_GSM_YIELD(); - while (stream.available() > 0) { - TINY_GSM_YIELD(); - int8_t a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += static_cast(a); - if (r1 && data.endsWith(r1)) { - index = 1; - goto finish; - } else if (r2 && data.endsWith(r2)) { - index = 2; - goto finish; - } else if (r3 && data.endsWith(r3)) { -#if defined TINY_GSM_DEBUG - if (r3 == GFP(GSM_CME_ERROR)) { - streamSkipUntil('\n'); // Read out the error - } -#endif - index = 3; - goto finish; - } else if (r4 && data.endsWith(r4)) { - index = 4; - goto finish; - } else if (r5 && data.endsWith(r5)) { - index = 5; - goto finish; - } else if (r6 && data.endsWith(r6)) { - index = 6; - goto finish; - } 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 - int8_t mux = streamGetIntBefore(','); - // read the number of packets in the buffer - int8_t num_packets = streamGetIntBefore(','); - // read the length of the current packet - streamSkipUntil( - ','); // Skip the length of the current package in the buffer - int16_t len_total = - streamGetIntBefore('\n'); // Total length of all packages - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux] && - num_packets >= 0 && len_total >= 0) { - sockets[mux]->sock_available = len_total; - } - data = ""; - // DBG("### Got Data:", len_total, "on", mux); - } else if (data.endsWith(GF("CLOSED" GSM_NL))) { - 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 = ""; - } + bool handleURCs(String& data) { + if (data.endsWith(GF(AT_NL "+QIRDI:"))) { // TODO(?): QIRD? or QIRDI? + // +QIRDI: ,,,,,< tlen> + streamSkipUntil(','); // Skip the context + streamSkipUntil(','); // Skip the role + // read the connection id + int8_t mux = streamGetIntBefore(','); + // read the number of packets in the buffer + int8_t num_packets = streamGetIntBefore(','); + // read the length of the current packet + streamSkipUntil( + ','); // Skip the length of the current package in the buffer + int16_t len_total = + streamGetIntBefore('\n'); // Total length of all packages + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux] && + num_packets >= 0 && len_total >= 0) { + sockets[mux]->sock_available = len_total; } - } while (millis() - startMillis < timeout_ms); - finish: - if (!index) { - data.trim(); - if (data.length()) { DBG("### Unhandled:", data); } + data = ""; + // DBG("### Got Data:", len_total, "on", mux); + return true; + } else if (data.endsWith(GF("CLOSED" AT_NL))) { + int8_t nl = data.lastIndexOf(AT_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); + return true; + } else if (data.endsWith(GF("+QNITZ:"))) { + streamSkipUntil('\n'); // URC for time sync + DBG("### Network time updated."); data = ""; } - // data.replace(GSM_NL, "/"); - // DBG('<', index, '>', data); - return index; - } - - int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL, GsmConstStr r6 = NULL) { - String data; - return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5, r6); - } - - int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL, GsmConstStr r6 = NULL) { - return waitResponse(1000, r1, r2, r3, r4, r5, r6); + return false; } public: @@ -616,7 +576,6 @@ class TinyGsmMC60 : public TinyGsmModem, protected: GsmClientMC60* sockets[TINY_GSM_MUX_COUNT]; - const char* gsmNL = GSM_NL; }; #endif // SRC_TINYGSMCLIENTMC60_H_ diff --git a/src/TinyGsmClientSIM5360.h b/src/TinyGsmClientSIM5360.h index f181036b..14e9d86a 100644 --- a/src/TinyGsmClientSIM5360.h +++ b/src/TinyGsmClientSIM5360.h @@ -14,26 +14,41 @@ #define TINY_GSM_MUX_COUNT 10 #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE +#ifdef AT_NL +#undef AT_NL +#endif +#define AT_NL "\r\n" + +#ifdef MODEM_MANUFACTURER +#undef MODEM_MANUFACTURER +#endif +#define MODEM_MANUFACTURER "SIMCom" + +#ifdef MODEM_MODEL +#undef MODEM_MODEL +#endif +#if defined(TINY_GSM_MODEM_SIM5320) +#define MODEM_MODEL "SIM5320"; +#elif defined(TINY_GSM_MODEM_SIM5300) +#define MODEM_MODEL "SIM5300"; +#elif defined(TINY_GSM_MODEM_SIM7100) +#define MODEM_MODEL "SIM7100"; +#else +#define MODEM_MODEL "SIM5360"; +#endif -#include "TinyGsmBattery.tpp" -#include "TinyGsmGPRS.tpp" -#include "TinyGsmGSMLocation.tpp" #include "TinyGsmModem.tpp" -#include "TinyGsmSMS.tpp" #include "TinyGsmTCP.tpp" -#include "TinyGsmTemperature.tpp" +#include "TinyGsmGPRS.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmGSMLocation.tpp" +#include "TinyGsmGPS.tpp" #include "TinyGsmTime.tpp" #include "TinyGsmNTP.tpp" +#include "TinyGsmBattery.tpp" +#include "TinyGsmTemperature.tpp" -#define GSM_NL "\r\n" -static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL; -static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL; -#if defined TINY_GSM_DEBUG -static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:"; -static const char GSM_CMS_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CMS ERROR:"; -#endif - -enum RegStatus { +enum SIM5360RegStatus { REG_NO_RESULT = -1, REG_UNREGISTERED = 0, REG_SEARCHING = 2, @@ -47,18 +62,20 @@ class TinyGsmSim5360 : public TinyGsmModem, public TinyGsmGPRS, public TinyGsmTCP, public TinyGsmSMS, + public TinyGsmGSMLocation, + public TinyGsmGPS, public TinyGsmTime, public TinyGsmNTP, - public TinyGsmGSMLocation, public TinyGsmBattery, public TinyGsmTemperature { friend class TinyGsmModem; friend class TinyGsmGPRS; friend class TinyGsmTCP; friend class TinyGsmSMS; + friend class TinyGsmGSMLocation; + friend class TinyGsmGPS; friend class TinyGsmTime; friend class TinyGsmNTP; - friend class TinyGsmGSMLocation; friend class TinyGsmBattery; friend class TinyGsmTemperature; @@ -123,27 +140,7 @@ class TinyGsmSim5360 : public TinyGsmModem, /* * Inner Secure Client */ - - // TODO(?): Add SSL support - /* - class GsmClientSecureSim5360 : public GsmClientSim5360 { - public: - GsmClientSecureSim5360() {} - - explicit GsmClientSecureSim5360(TinyGsmSim5360& modem, uint8_t mux = 0) - : GsmClientSim5360(modem, mux) {} - - 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 - }; - */ + // NOT SUPPORTED /* * Constructor @@ -157,7 +154,7 @@ class TinyGsmSim5360 : public TinyGsmModem, * Basic functions */ protected: - bool initImpl(const char* pin = NULL) { + bool initImpl(const char* pin = nullptr) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM5360")); @@ -185,7 +182,7 @@ class TinyGsmSim5360 : public TinyGsmModem, 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) { + if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); } else { @@ -195,21 +192,6 @@ class TinyGsmSim5360 : public TinyGsmModem, } } - String getModemNameImpl() { - String name = "SIMCom SIM5360"; - - sendAT(GF("+CGMM")); - String res2; - 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; - } - bool factoryDefaultImpl() { // these commands aren't supported return false; } @@ -218,14 +200,14 @@ class TinyGsmSim5360 : public TinyGsmModem, * Power functions */ protected: - bool restartImpl(const char* pin = NULL) { + bool restartImpl(const char* pin = nullptr) { if (!testAT()) { return false; } sendAT(GF("+REBOOT")); // Should return an 'OK' after reboot command is sent if (waitResponse(10000L) != 1) { return false; } // After booting, modem sends out messages as each of its // internal modules loads. The final message is "PB DONE". - if (waitResponse(40000L, GF(GSM_NL "PB DONE")) != 1) { return false; } + if (waitResponse(40000L, GF(AT_NL "PB DONE")) != 1) { return false; } return init(pin); } @@ -254,20 +236,20 @@ class TinyGsmSim5360 : public TinyGsmModem, * Generic network functions */ public: - RegStatus getRegistrationStatus() { - return (RegStatus)getRegistrationStatusXREG("CGREG"); + SIM5360RegStatus getRegistrationStatus() { + return (SIM5360RegStatus)getRegistrationStatusXREG("CGREG"); } protected: bool isNetworkConnectedImpl() { - RegStatus s = getRegistrationStatus(); + SIM5360RegStatus 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(AT_NL "+CNMP:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); return res; @@ -275,7 +257,7 @@ class TinyGsmSim5360 : public TinyGsmModem, int16_t getNetworkMode() { sendAT(GF("+CNMP?")); - if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return false; } + if (waitResponse(GF(AT_NL "+CNMP:")) != 1) { return false; } int16_t mode = streamGetIntBefore('\n'); waitResponse(); return mode; @@ -291,18 +273,26 @@ class TinyGsmSim5360 : public TinyGsmModem, // 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(); + cleanResponseString(res); return res; } + /* + * Secure socket layer (SSL) functions + */ + // No functions of this type supported + + /* + * WiFi functions + */ + // No functions of this type supported + /* * GPRS functions */ protected: - bool gprsConnectImpl(const char* apn, const char* user = NULL, - const char* pwd = NULL) { + bool gprsConnectImpl(const char* apn, const char* user = nullptr, + const char* pwd = nullptr) { gprsDisconnect(); // Make sure we're not connected first // Define the PDP context @@ -376,7 +366,7 @@ class TinyGsmSim5360 : public TinyGsmModem, // 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(AT_NL "+NETOPEN: 0")) != 1) { return false; } return true; } @@ -392,7 +382,7 @@ class TinyGsmSim5360 : public TinyGsmModem, // 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(AT_NL "+NETCLOSE: 0")) != 1) { return false; } return true; } @@ -400,7 +390,7 @@ class TinyGsmSim5360 : public TinyGsmModem, 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(AT_NL "+NETOPEN: 1")) != 1) { return false; } waitResponse(); sendAT(GF("+IPADDR")); // Inquire Socket PDP address @@ -410,6 +400,15 @@ class TinyGsmSim5360 : public TinyGsmModem, return true; } + String getProviderImpl() { + sendAT(GF("+CSPN?")); + if (waitResponse(GF("+CSPN:")) != 1) { return ""; } + streamSkipUntil('"'); /* Skip mode and format */ + String res = stream.readStringUntil('"'); + waitResponse(); + return res; + } + /* * SIM card functions */ @@ -417,7 +416,7 @@ class TinyGsmSim5360 : public TinyGsmModem, // Gets the CCID of a sim card via AT+CCID String getSimCCIDImpl() { sendAT(GF("+CICCID")); - if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { return ""; } + if (waitResponse(GF(AT_NL "+ICCID:")) != 1) { return ""; } String res = stream.readStringUntil('\n'); waitResponse(); res.trim(); @@ -425,37 +424,148 @@ class TinyGsmSim5360 : public TinyGsmModem, } /* - * Messaging functions + * Phone Call functions */ - protected: - // Follows all messaging functions per template + // No functions of this type supported + + /* + * Audio functions + */ + // No functions of this type supported + + /* + * Text messaging (SMS) functions + */ + // Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp /* * 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 + * GPS/GNSS/GLONASS location functions */ protected: - // Can follow the standard CCLK function in the template + // enable GPS + bool enableGPSImpl() { + sendAT(GF("+CGPS=1")); + if (waitResponse() != 1) { return false; } + return true; + } + + bool disableGPSImpl() { + sendAT(GF("+CGPS=0")); + if (waitResponse() != 1) { return false; } + return true; + } + + // get the RAW GPS output + String getGPSrawImpl() { + sendAT(GF("+CGPSINFO")); + if (waitResponse(GF(AT_NL "+CGPSINFO:")) != 1) { return ""; } + String res = stream.readStringUntil('\n'); + waitResponse(); + res.trim(); + return res; + } + + // 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("+CGPSINFO")); + if (waitResponse(GF(AT_NL "+CGPSINFO:")) != 1) { return false; } + delay(30); + + float ilat = 0; + char north; + float ilon = 0; + char east; + float ispeed = 0; + float ialt = 0; + int ivsat = 0; + int iusat = 0; + int iyear = 0; + int imonth = 0; + int iday = 0; + int ihour = 0; + int imin = 0; + float secondWithSS = 0; + + ilat = streamGetFloatBefore(','); // Latitude in ddmm.mmmmmm + north = stream.read(); // N/S Indicator, N=north or S=south + streamSkipUntil(','); // BEIDOU satellite valid numbers + ilon = streamGetFloatBefore(','); // Longitude in dddmm.mmmmmm + east = stream.read(); // E/W Indicator, E=east or W=west + streamSkipUntil(','); // BEIDOU satellite valid numbers + + // 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. + + if (ilat != -9999.0F) { + if (lat != nullptr) + *lat = (floor(ilat / 100) + fmod(ilat, 100.) / 60) * + (north == 'N' ? 1 : -1); + if (lon != nullptr) + *lon = (floor(ilon / 100) + fmod(ilon, 100.) / 60) * + (east == 'E' ? 1 : -1); + if (speed != nullptr) *speed = ispeed; + if (alt != nullptr) *alt = ialt; + if (vsat != nullptr) *vsat = ivsat; + if (usat != nullptr) *usat = iusat; + if (accuracy != nullptr) *accuracy = -9999; + if (iyear < 2000) iyear += 2000; + if (year != nullptr) *year = iyear; + if (month != nullptr) *month = imonth; + if (day != nullptr) *day = iday; + if (hour != nullptr) *hour = ihour; + if (minute != nullptr) *minute = imin; + if (second != nullptr) *second = static_cast(secondWithSS); + + waitResponse(); + return true; + } + + waitResponse(); + return false; + } + + /* + * Time functions + */ + // Follows all clock functions as inherited from TinyGsmTime.tpp /* * NTP server functions */ - // Can sync with server using CNTP as per template + // Follows all NTP server functions as inherited from TinyGsmNTP.tpp + + /* + * BLE functions + */ + // No functions of this type supported /* * Battery functions */ protected: // SRGD Note: Returns voltage in VOLTS instead of millivolts - uint16_t getBattVoltageImpl() { + int16_t getBattVoltageImpl() { sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return 0; } + if (waitResponse(GF(AT_NL "+CBC:")) != 1) { return 0; } streamSkipUntil(','); // Skip battery charge status streamSkipUntil(','); // Skip battery charge level // get voltage in VOLTS @@ -468,10 +578,10 @@ class TinyGsmSim5360 : public TinyGsmModem, } // SRGD Note: Returns voltage in VOLTS instead of millivolts - bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, - uint16_t& milliVolts) { + bool getBattStatsImpl(int8_t& chargeState, int8_t& percent, + int16_t& milliVolts) { sendAT(GF("+CBC")); - if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return false; } + if (waitResponse(GF(AT_NL "+CBC:")) != 1) { return false; } chargeState = streamGetIntBefore(','); percent = streamGetIntBefore(','); // get voltage in VOLTS @@ -493,7 +603,7 @@ class TinyGsmSim5360 : public TinyGsmModem, if (waitResponse() != 1) { return 0; } // Get Temparature Value sendAT(GF("+CMTE?")); - if (waitResponse(GF(GSM_NL "+CMTE:")) != 1) { return false; } + if (waitResponse(GF(AT_NL "+CMTE:")) != 1) { return false; } float res = streamGetFloatBefore('\n'); // Wait for final OK waitResponse(); @@ -516,7 +626,7 @@ class TinyGsmSim5360 : public TinyGsmModem, 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(AT_NL "+CIPOPEN:")) != 1) { return false; } return true; } @@ -525,7 +635,7 @@ class TinyGsmSim5360 : public TinyGsmModem, if (waitResponse(GF(">")) != 1) { return 0; } stream.write(reinterpret_cast(buff), len); stream.flush(); - if (waitResponse(GF(GSM_NL "+CIPSEND:")) != 1) { return 0; } + if (waitResponse(GF(AT_NL "+CIPSEND:")) != 1) { return 0; } streamSkipUntil(','); // Skip mux streamSkipUntil(','); // Skip requested bytes to send // TODO(?): make sure requested and confirmed bytes match @@ -559,7 +669,7 @@ class TinyGsmSim5360 : public TinyGsmModem, }; buf[0] = stream.read(); buf[1] = stream.read(); - char c = strtol(buf, NULL, 16); + char c = strtol(buf, nullptr, 16); #else while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { @@ -609,125 +719,49 @@ class TinyGsmSim5360 : public TinyGsmModem, * Utilities */ public: - // TODO(vshymanskyy): Optimize this! - int8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - /*String r1s(r1); r1s.trim(); - String r2s(r2); r2s.trim(); - String r3s(r3); r3s.trim(); - String r4s(r4); r4s.trim(); - String r5s(r5); r5s.trim(); - DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ - data.reserve(64); - uint8_t index = 0; - uint32_t startMillis = millis(); - do { - TINY_GSM_YIELD(); - while (stream.available() > 0) { - TINY_GSM_YIELD(); - int8_t a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += static_cast(a); - if (r1 && data.endsWith(r1)) { - index = 1; - goto finish; - } else if (r2 && data.endsWith(r2)) { - index = 2; - goto finish; - } else if (r3 && data.endsWith(r3)) { -#if defined TINY_GSM_DEBUG - if (r3 == GFP(GSM_CME_ERROR)) { - streamSkipUntil('\n'); // Read out the error - } -#endif - index = 3; - goto finish; - } else if (r4 && data.endsWith(r4)) { - index = 4; - goto finish; - } else if (r5 && data.endsWith(r5)) { - index = 5; - goto finish; - } else if (data.endsWith(GF(GSM_NL "+CIPRXGET:"))) { - 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); - } else { - data += mode; - } - } else if (data.endsWith(GF(GSM_NL "+RECEIVE:"))) { - int8_t mux = streamGetIntBefore(','); - int16_t len = streamGetIntBefore('\n'); - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; - if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } - } - data = ""; - // DBG("### Got Data:", len, "on", mux); - } else if (data.endsWith(GF("+IPCLOSE:"))) { - 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; - } - data = ""; - DBG("### Closed: ", mux); - } else if (data.endsWith(GF("+CIPEVENT:"))) { - // Need to close all open sockets and release the network library. - // User will then need to reconnect. - DBG("### Network error!"); - if (!isGprsConnected()) { gprsDisconnect(); } - data = ""; + bool handleURCs(String& data) { + if (data.endsWith(GF(AT_NL "+CIPRXGET:"))) { + 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); + return true; + } else { + data += mode; + return false; + } + } else if (data.endsWith(GF(AT_NL "+RECEIVE:"))) { + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore('\n'); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->got_data = true; + if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } } - } while (millis() - startMillis < timeout_ms); - finish: - if (!index) { - data.trim(); - if (data.length()) { DBG("### Unhandled:", data); } data = ""; + // DBG("### Got Data:", len, "on", mux); + return true; + } else if (data.endsWith(GF("+IPCLOSE:"))) { + 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; + } + data = ""; + DBG("### Closed: ", mux); + return true; + } else if (data.endsWith(GF("+CIPEVENT:"))) { + // Need to close all open sockets and release the network library. + // User will then need to reconnect. + DBG("### Network error!"); + if (!isGprsConnected()) { gprsDisconnect(); } + data = ""; + return true; } - // data.replace(GSM_NL, "/"); - // DBG('<', index, '>', data); - return index; - } - - int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - String data; - return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); - } - - int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - return waitResponse(1000, r1, r2, r3, r4, r5); + return false; } public: @@ -735,7 +769,6 @@ class TinyGsmSim5360 : public TinyGsmModem, protected: GsmClientSim5360* sockets[TINY_GSM_MUX_COUNT]; - const char* gsmNL = GSM_NL; }; #endif // SRC_TINYGSMCLIENTSIM5360_H_ diff --git a/src/TinyGsmClientSIM7000.h b/src/TinyGsmClientSIM7000.h index 60b248e6..6200f52a 100644 --- a/src/TinyGsmClientSIM7000.h +++ b/src/TinyGsmClientSIM7000.h @@ -17,12 +17,29 @@ #include "TinyGsmClientSIM70xx.h" #include "TinyGsmTCP.tpp" - +#include "TinyGsmSMS.tpp" +#include "TinyGsmGSMLocation.tpp" +#include "TinyGsmTime.tpp" +#include "TinyGsmNTP.tpp" +#include "TinyGsmBattery.tpp" class TinyGsmSim7000 : public TinyGsmSim70xx, - public TinyGsmTCP { - friend class TinyGsmSim70xx; - friend class TinyGsmTCP; + public TinyGsmTCP, + public TinyGsmSMS, + public TinyGsmTime, + public TinyGsmNTP, + public TinyGsmGSMLocation, + public TinyGsmBattery { + friend class TinyGsmSim70xx; + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmSMS; + friend class TinyGsmGSMLocation; + friend class TinyGsmGPS; + friend class TinyGsmTime; + friend class TinyGsmNTP; + friend class TinyGsmBattery; /* * Inner Client @@ -349,21 +366,23 @@ class TinyGsmSim7000 : public TinyGsmSim70xx, protected: // Follows all messaging functions per template - /* - * GPS/GNSS/GLONASS location functions - */ - protected: - // Follows the SIM70xx template + /* + * Inner Secure Client + */ + // NOTE: Use modem TinyGsmSim7000SSL for a secure client! /* * Time functions */ // Can follow CCLK as per template - /* - * NTP server functions - */ - // Can sync with server using CNTP as per template + /* + * Basic functions + */ + protected: + bool initImpl(const char* pin = nullptr) { + DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM7000")); /* * Battery functions @@ -371,25 +390,282 @@ class TinyGsmSim7000 : public TinyGsmSim70xx, 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 only supported using application on SIM7000!"); - } - uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; - - // when not using SSL, the TCP application toolkit is more stable - 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))); + sendAT(GF("E0")); // Echo Off + if (waitResponse() != 1) { return false; } + +#ifdef TINY_GSM_DEBUG + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes +#endif + waitResponse(); + + DBG(GF("### Modem:"), getModemName()); + + // Enable Local Time Stamp for getting network time + sendAT(GF("+CLTS=1")); + if (waitResponse(10000L) != 1) { return false; } + + // Enable battery checks + sendAT(GF("+CBATCHK=1")); + if (waitResponse() != 1) { return false; } + + SimStatus ret = getSimStatus(); + // if the sim isn't ready and a pin has been provided, try to unlock the sim + if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) { + simUnlock(pin); + return (getSimStatus() == SIM_READY); + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true + return (ret == SIM_READY || ret == SIM_LOCKED); + } + } + + /* + * Power functions + */ + // Follows functions as inherited from TinyGsmClientSIM70xx.h + + /* + * Generic network functions + */ + protected: + String getLocalIPImpl() { + sendAT(GF("+CIFSR;E0")); + String res; + if (waitResponse(10000L, res) != 1) { return ""; } + cleanResponseString(res); + return res; + } + + /* + * Secure socket layer (SSL) functions + */ + // NOTE: Use modem TinyGsmSim7000SSL for a secure client! + + /* + * WiFi functions + */ + // No functions of this type supported + + /* + * GPRS functions + */ + protected: + bool gprsConnectImpl(const char* apn, const char* user = nullptr, + const char* pwd = nullptr) { + gprsDisconnect(); + + // Bearer settings for applications based on IP + // Set the connection type to GPRS + sendAT(GF("+SAPBR=3,1,\"Contype\",\"GPRS\"")); + waitResponse(); + + // Set the APN + sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"'); + waitResponse(); + + // Set the user name + if (user && strlen(user) > 0) { + sendAT(GF("+SAPBR=3,1,\"USER\",\""), user, '"'); + waitResponse(); + } + // Set the password + if (pwd && strlen(pwd) > 0) { + sendAT(GF("+SAPBR=3,1,\"PWD\",\""), pwd, '"'); + waitResponse(); + } + + // Define the PDP context + sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"'); + waitResponse(); + + // Attach to GPRS + sendAT(GF("+CGATT=1")); + if (waitResponse(60000L) != 1) { return false; } + + // Activate the PDP context + sendAT(GF("+CGACT=1,1")); + waitResponse(60000L); + + // Open the definied GPRS bearer context + sendAT(GF("+SAPBR=1,1")); + waitResponse(85000L); + // Query the GPRS bearer context status + sendAT(GF("+SAPBR=2,1")); + if (waitResponse(30000L) != 1) { return false; } + + // Set the TCP application toolkit to multi-IP + sendAT(GF("+CIPMUX=1")); + if (waitResponse() != 1) { return false; } + + // Put the TCP application toolkit in "quick send" mode + // (thus no extra "Send OK") + sendAT(GF("+CIPQSEND=1")); + if (waitResponse() != 1) { return false; } + + // Set the TCP application toolkit to get data manually + sendAT(GF("+CIPRXGET=1")); + if (waitResponse() != 1) { return false; } + + // Start the TCP application toolkit task and set APN, USER NAME, PASSWORD + sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\"")); + if (waitResponse(60000L) != 1) { return false; } + + // Bring up the TCP application toolkit wireless connection with GPRS or CSD + sendAT(GF("+CIICR")); + if (waitResponse(60000L) != 1) { return false; } + + // Get local IP address for the TCP application toolkit + // only assigned after connection + sendAT(GF("+CIFSR;E0")); + if (waitResponse(10000L) != 1) { return false; } + + return true; + } + + bool gprsDisconnectImpl() { + // Shut the TCP application toolkit connection + // CIPSHUT will close *all* open TCP application toolkit connections + sendAT(GF("+CIPSHUT")); + if (waitResponse(60000L) != 1) { return false; } + + sendAT(GF("+CGATT=0")); // Deactivate the bearer context + if (waitResponse(60000L) != 1) { return false; } + + return true; + } + + /* + * SIM card functions + */ + // Follows functions as inherited from TinyGsmClientSIM70xx.h + + /* + * Phone Call functions + */ + // No functions of this type supported + + /* + * Audio functions + */ + // No functions of this type supported + + /* + * Text messaging (SMS) functions + */ + // Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp + + /* + * GSM Location functions + */ + // Follows all GSM-based location functions as inherited from + // TinyGsmGSMLocation.tpp + + /* + * GPS/GNSS/GLONASS location functions + */ + // Follows functions as inherited from TinyGsmClientSIM70xx.h + + /* + * Time functions + */ + // Follows all clock functions as inherited from TinyGsmTime.tpp + + /* + * NTP server functions + */ + // Follows all NTP server functions as inherited from TinyGsmNTP.tpp + + /* + * BLE functions + */ + // No functions of this type supported + + /* + * Battery functions + */ + // Follows all battery functions as inherited from TinyGsmBattery.tpp + + /* + * Temperature functions + */ + // No functions of this type supported + + /* + * 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 only supported using application on SIM7000!"); } + uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000; + + // when not using SSL, the TCP application toolkit is more stable + sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, + GF("\","), port); + return (1 == + waitResponse(timeout_ms, GF("CONNECT OK" AT_NL), + GF("CONNECT FAIL" AT_NL), GF("ALREADY CONNECT" AT_NL), + GF("ERROR" AT_NL), GF("CLOSE OK" AT_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(reinterpret_cast(buff), len); + stream.flush(); + + if (waitResponse(GF(AT_NL "DATA ACCEPT:"), GF("SEND FAIL")) != 1) { + return 0; + } + streamSkipUntil(','); // Skip mux + return streamGetIntBefore('\n'); + } + + size_t modemRead(size_t size, uint8_t mux) { + if (!sockets[mux]) return 0; + +#ifdef TINY_GSM_USE_HEX + sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size); + if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } +#else + sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size); + if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; } +#endif + streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX + streamSkipUntil(','); // Skip mux + int16_t len_requested = streamGetIntBefore(','); + // ^^ Requested number of data bytes (1-1460 bytes)to be read + int16_t len_confirmed = streamGetIntBefore('\n'); + // ^^ Confirmed number of data bytes to be read, which may be less than + // requested. 0 indicates that no data can be read. + // SRGD NOTE: Contrary to above (which is copied from AT command manual) + // this is actually be the number of bytes that will be remaining in the + // buffer after the read. + for (int i = 0; i < len_requested; i++) { + uint32_t startMillis = millis(); +#ifdef TINY_GSM_USE_HEX + 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, nullptr, 16); +#else + while (!stream.available() && + (millis() - startMillis < sockets[mux]->_timeout)) { + TINY_GSM_YIELD(); + } + char c = stream.read(); +#endif + sockets[mux]->rx.put(c); } int16_t modemSend(const void* buff, size_t len, uint8_t mux) { @@ -413,262 +689,114 @@ class TinyGsmSim7000 : public TinyGsmSim70xx, return 0; } - #ifdef TINY_GSM_USE_HEX - sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CIPRXGET:")) != 1) { - return 0; - } - #else - sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size); - if (waitResponse(GF("+CIPRXGET:")) != 1) { - return 0; - } - #endif - streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX - streamSkipUntil(','); // Skip mux - int16_t len_requested = streamGetIntBefore(','); - // ^^ Requested number of data bytes (1-1460 bytes)to be read - int16_t len_confirmed = streamGetIntBefore('\n'); - // ^^ Confirmed number of data bytes to be read, which may be less than - // requested. 0 indicates that no data can be read. - // SRGD NOTE: Contrary to above (which is copied from AT command manual) - // this is actually be the number of bytes that will be remaining in the - // buffer after the read. - for (int i = 0; i < len_requested; i++) { - uint32_t startMillis = millis(); - #ifdef TINY_GSM_USE_HEX - 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(); - } - char c = stream.read(); - #endif - sockets[mux]->rx.put(c); - } - // DBG("### READ:", len_requested, "from", mux); - // sockets[mux]->sock_available = modemGetAvailable(mux); - sockets[mux]->sock_available = len_confirmed; - waitResponse(); - return len_requested; - } + /* + * Utilities + */ + public: - size_t modemGetAvailable(uint8_t mux) { - if (!sockets[mux]) { - return 0; - } - - sendAT(GF("+CIPRXGET=4,"), mux); - size_t result = 0; - if (waitResponse(GF("+CIPRXGET:")) == 1) { - streamSkipUntil(','); // Skip mode 4 - streamSkipUntil(','); // Skip mux - result = streamGetIntBefore('\n'); - waitResponse(); - } - // 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")); - int8_t res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), - GF(",\"CLOSING\""), GF(",\"REMOTE CLOSING\""), - GF(",\"INITIAL\"")); - waitResponse(); - return 1 == res; + uint8_t getNetworkSystemMode() { + sendAT(GF("+CNSMOD?")); + if (waitResponse(GF(GSM_NL "+CNSMOD:")) != 1) { + return 0; } - - /* - * Utilities - */ - public: - - bool enableGpio(uint8_t gpio, bool enable) { - //AT+SGPIO=,,, - // 0 Set the GPIO function including the GPIO output. 1 Read the GPIO level. - // Please note that only when the gpioisset asinput, user can use parameter 1 to read the GPIO level, otherwisethemodule will return "ERROR". - // The GPIO you want to be set. (It has relations with the hardware, please refer to the hardware manual) - // Only when is set to 0, this option takes effect. 0 Set the GPIO to input. 1 Set the GPIO to output - // 0 GPIO low level 1 GPIO high level - if (gpio > 7) { - log_w("invalid gpio"); - return false; - } - char msg[20] = {}; - sprintf(msg, "+SGPIO=0,%d,1,%d", gpio, enable); - sendAT(GF(msg)); - - return waitResponse(10000L, GF("OK")) == 1; + String res = stream.readStringUntil('\n'); + waitResponse(); + res = res.substring(3); + return atoi(res.c_str()); + } + + String getGnssSystemMode() { + sendAT(GF("+CGNSMOD?")); + if (waitResponse(GF(GSM_NL "+CGNSMOD:")) != 1) { + return "Gnss system mode not available"; } - - uint8_t getNetworkSystemMode() { - sendAT(GF("+CNSMOD?")); - if (waitResponse(GF(GSM_NL "+CNSMOD:")) != 1) { - return 0; - } - String res = stream.readStringUntil('\n'); - waitResponse(); - res = res.substring(3); - return atoi(res.c_str()); + String res = stream.readStringUntil('\n'); + waitResponse(); + + return res; + } + + bool enableGpio(uint8_t gpio, bool enable){ + //AT+SGPIO=,,, + // 0 Set the GPIO function including the GPIO output. 1 Read the GPIO level. + // Please note that only when the gpioisset asinput, user can use parameter 1 to read the GPIO level, otherwisethemodule will return "ERROR". + // The GPIO you want to be set. (It has relations with the hardware, please refer to the hardware manual) + // Only when is set to 0, this option takes effect. 0 Set the GPIO to input. 1 Set the GPIO to output + // 0 GPIO low level 1 GPIO high level + if(gpio > 7){ + log_w("invalid gpio"); + return false; } - - // TODO(vshymanskyy): Optimize this! - int8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), - #if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), - #else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, - #endif - GsmConstStr r5 = NULL) { - /*String r1s(r1); r1s.trim(); - String r2s(r2); r2s.trim(); - String r3s(r3); r3s.trim(); - String r4s(r4); r4s.trim(); - String r5s(r5); r5s.trim(); - DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ - data.reserve(64); - uint8_t index = 0; - uint32_t startMillis = millis(); - do { - TINY_GSM_YIELD(); - while (stream.available() > 0) { - TINY_GSM_YIELD(); - int8_t a = stream.read(); - if (a <= 0) { - continue; // Skip 0x00 bytes, just in case - } - data += static_cast(a); - if (r1 && data.endsWith(r1)) { - index = 1; - goto finish; - } else if (r2 && data.endsWith(r2)) { - index = 2; - goto finish; - } else if (r3 && data.endsWith(r3)) { - #if defined TINY_GSM_DEBUG - if (r3 == GFP(GSM_CME_ERROR)) { - streamSkipUntil('\n'); // Read out the error - } - #endif - index = 3; - goto finish; - } else if (r4 && data.endsWith(r4)) { - index = 4; - goto finish; - } else if (r5 && data.endsWith(r5)) { - index = 5; - goto finish; - } else if (data.endsWith(GF(GSM_NL "+CIPRXGET:"))) { - 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); - } else { - data += mode; - } - } else if (data.endsWith(GF(GSM_NL "+RECEIVE:"))) { - int8_t mux = streamGetIntBefore(','); - int16_t len = streamGetIntBefore('\n'); - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; - if (len >= 0 && len <= 1024) { - sockets[mux]->sock_available = len; - } - } - data = ""; - // DBG("### Got Data:", len, "on", mux); - } else if (data.endsWith(GF("CLOSED" GSM_NL))) { - 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."); - } else if (data.endsWith(GF(GSM_NL "SMS Ready" GSM_NL))) { - data = ""; - DBG("### Unexpected module reset!"); - init(); - } - } - } while (millis() - startMillis < timeout_ms); -finish: - if (!index) { - data.trim(); - if (data.length()) { - DBG("### Unhandled:", data); + char msg[20] = {}; + sprintf(msg, "+SGPIO=0,%d,1,%d", gpio, enable); + sendAT(GF(msg)); + + return waitResponse(10000L, GF("OK")) == 1; + } + + bool handleURCs(String& data) { + if (data.endsWith(GF(AT_NL "+CIPRXGET:"))) { + 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); + return true; + } else { + data += mode; + return false; } - // data.replace(GSM_NL, "/"); - // DBG('<', index, '>', data); - return index; - } - - int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), - #if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), - #else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, - #endif - GsmConstStr r5 = NULL) { - String data; - return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); - } - - int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), - #if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), - #else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, - #endif - GsmConstStr r5 = NULL) { - return waitResponse(1000, r1, r2, r3, r4, r5); + } else if (data.endsWith(GF(AT_NL "+RECEIVE:"))) { + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore('\n'); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->got_data = true; + if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } + } + data = ""; + // DBG("### Got Data:", len, "on", mux); + return true; + } else if (data.endsWith(GF("CLOSED" AT_NL))) { + int8_t nl = data.lastIndexOf(AT_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); + return true; + } else if (data.endsWith(GF("*PSNWID:"))) { + streamSkipUntil('\n'); // Refresh network name by network + data = ""; + DBG("### Network name updated."); + return true; + } else if (data.endsWith(GF("*PSUTTZ:"))) { + streamSkipUntil('\n'); // Refresh time and time zone by network + data = ""; + DBG("### Network time and time zone updated."); + return true; + } else if (data.endsWith(GF("+CTZV:"))) { + streamSkipUntil('\n'); // Refresh network time zone by network + data = ""; + DBG("### Network time zone updated."); + return true; + } else if (data.endsWith(GF("DST: "))) { + streamSkipUntil('\n'); // Refresh Network Daylight Saving Time by network + data = ""; + DBG("### Daylight savings time state updated."); + return true; + } else if (data.endsWith(GF(AT_NL "SMS Ready" AT_NL))) { + data = ""; + DBG("### Unexpected module reset!"); + init(); + return true; } + return false; + } protected: GsmClientSim7000* sockets[TINY_GSM_MUX_COUNT]; diff --git a/src/TinyGsmClientSIM7000SSL.h b/src/TinyGsmClientSIM7000SSL.h index 2f88e893..2170e498 100644 --- a/src/TinyGsmClientSIM7000SSL.h +++ b/src/TinyGsmClientSIM7000SSL.h @@ -18,14 +18,32 @@ #include "TinyGsmClientSIM70xx.h" #include "TinyGsmTCP.tpp" #include "TinyGsmSSL.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmGSMLocation.tpp" +#include "TinyGsmTime.tpp" +#include "TinyGsmNTP.tpp" +#include "TinyGsmBattery.tpp" class TinyGsmSim7000SSL : public TinyGsmSim70xx, public TinyGsmTCP, - public TinyGsmSSL { + public TinyGsmSSL, + public TinyGsmSMS, + public TinyGsmGSMLocation, + public TinyGsmTime, + public TinyGsmNTP, + public TinyGsmBattery { friend class TinyGsmSim70xx; friend class TinyGsmTCP; - friend class TinyGsmSSL; + friend class TinyGsmSSL; + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmSMS; + friend class TinyGsmGSMLocation; + friend class TinyGsmGPS; + friend class TinyGsmNTP; + friend class TinyGsmTime; + friend class TinyGsmBattery; /* * Inner Client @@ -88,7 +106,7 @@ class TinyGsmSim7000SSL /* * Inner Secure Client */ - + public: class GsmClientSecureSIM7000SSL : public GsmClientSim7000SSL { public: GsmClientSecureSIM7000SSL() {} @@ -97,7 +115,6 @@ class TinyGsmSim7000SSL uint8_t mux = 0) : GsmClientSim7000SSL(modem, mux) {} - public: bool setCertificate(const String& certificateName) { return at->setCertificate(certificateName, mux); } @@ -118,8 +135,7 @@ class TinyGsmSim7000SSL */ public: explicit TinyGsmSim7000SSL(Stream& stream) - : TinyGsmSim70xx(stream), - certificates() { + : TinyGsmSim70xx(stream) { memset(sockets, 0, sizeof(sockets)); } @@ -127,7 +143,7 @@ class TinyGsmSim7000SSL * Basic functions */ protected: - bool initImpl(const char* pin = NULL) { + bool initImpl(const char* pin = nullptr) { DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM7000SSL")); @@ -155,7 +171,7 @@ class TinyGsmSim7000SSL 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) { + if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) { simUnlock(pin); return (getSimStatus() == SIM_READY); } else { @@ -179,14 +195,13 @@ class TinyGsmSim7000SSL // modemGetAvailable checks all socks, so we only want to do it once // modemGetAvailable calls modemGetConnected(), which also checks allf if (check_socks) { modemGetAvailable(0); } - while (stream.available()) { waitResponse(15, NULL, NULL); } + while (stream.available()) { waitResponse(15, nullptr, nullptr); } } /* * Power functions */ - protected: - // Follows the SIM70xx template + // Follows functions as inherited from TinyGsmClientSIM70xx.h /* * Generic network functions @@ -194,7 +209,7 @@ class TinyGsmSim7000SSL protected: String getLocalIPImpl() { sendAT(GF("+CNACT?")); - if (waitResponse(GF(GSM_NL "+CNACT:")) != 1) { return ""; } + if (waitResponse(GF(AT_NL "+CNACT:")) != 1) { return ""; } streamSkipUntil('\"'); String res = stream.readStringUntil('\"'); waitResponse(); @@ -202,21 +217,21 @@ class TinyGsmSim7000SSL } /* - * Secure socket layer functions + * Secure socket layer (SSL) functions */ - protected: - bool setCertificate(const String& certificateName, const uint8_t mux = 0) { - if (mux >= TINY_GSM_MUX_COUNT) return false; - certificates[mux] = certificateName; - return true; - } + // Follows functions as inherited from TinyGsmSSL.tpp + + /* + * WiFi functions + */ + // No functions of this type supported /* * GPRS functions */ protected: - bool gprsConnectImpl(const char* apn, const char* user = NULL, - const char* pwd = NULL) { + bool gprsConnectImpl(const char* apn, const char* user = nullptr, + const char* pwd = nullptr) { gprsDisconnect(); // Define the PDP context @@ -242,7 +257,7 @@ class TinyGsmSim7000SSL // 2: CHAP // 3: PAP or CHAP if (pwd && strlen(pwd) > 0 && user && strlen(user) > 0) { - sendAT(GF("+CNCFG=1,\""), apn, "\",\"", "\",\"", user, pwd, '"'); + sendAT(GF("+CNCFG=1,\""), apn, "\",\"", "\",\"", user, pwd, "\",3"); waitResponse(); } else if (user && strlen(user) > 0) { // Set the user name only @@ -265,8 +280,8 @@ class TinyGsmSim7000SSL int ntries = 0; while (!res && ntries < 5) { sendAT(GF("+CNACT=1,\""), apn, GF("\"")); - res = waitResponse(60000L, GF(GSM_NL "+APP PDP: ACTIVE"), - GF(GSM_NL "+APP PDP: DEACTIVE")) == 1; + res = waitResponse(60000L, GF(AT_NL "+APP PDP: ACTIVE"), + GF(AT_NL "+APP PDP: DEACTIVE")) == 1; waitResponse(); ntries++; } @@ -289,36 +304,57 @@ class TinyGsmSim7000SSL /* * SIM card functions */ - protected: - // Follows the SIM70xx template + // Follows functions as inherited from TinyGsmClientSIM70xx.h /* - * Messaging functions + * Phone Call functions */ - protected: - // Follows all messaging functions per template + // No functions of this type supported + + /* + * Audio functions + */ + // No functions of this type supported + + /* + * Text messaging (SMS) functions + */ + // Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp + /* + * GSM Location functions + */ + // Follows all GSM-based location functions as inherited from + // TinyGsmGSMLocation.tpp /* * GPS/GNSS/GLONASS location functions */ - protected: - // Follows the SIM70xx template + // Follows functions as inherited from TinyGsmClientSIM70xx.h /* * Time functions */ - // Can follow CCLK as per template + // Follows all clock functions as inherited from TinyGsmTime.tpp /* * NTP server functions */ - // Can sync with server using CNTP as per template + // Follows all NTP server functions as inherited from TinyGsmNTP.tpp + + /* + * BLE functions + */ + // No functions of this type supported /* * Battery functions */ - protected: - // Follows all battery functions per template + // Follows all battery functions as inherited from TinyGsmBattery.tpp + + /* + * Temperature functions + */ + // No functions of this type supported /* * Client related functions @@ -336,7 +372,9 @@ class TinyGsmSim7000SSL if (ssl) { // set the ssl version // AT+CSSLCFG="SSLVERSION",, - // PDP context identifier + // PDP context identifier - for reasons not understood by me, + // use PDP context identifier of 0 for what we defined as 1 in + // the gprsConnect function // 0: QAPI_NET_SSL_PROTOCOL_UNKNOWN // 1: QAPI_NET_SSL_PROTOCOL_TLS_1_0 // 2: QAPI_NET_SSL_PROTOCOL_TLS_1_1 @@ -359,8 +397,11 @@ class TinyGsmSim7000SSL if (ssl) { // set the PDP context to apply SSL to // AT+CSSLCFG="CTXINDEX", - // PDP context identifier - // NOTE: despite docs using caps, "ctxindex" must be in lower case + // PDP context identifier - for reasons not understood by me, + // use PDP context identifier of 0 for what we defined as 1 in + // the gprsConnect function + // NOTE: despite docs using "CRINDEX" in all caps, the module only + // accepts the command "ctxindex" and it must be in lower case sendAT(GF("+CSSLCFG=\"ctxindex\",0")); if (waitResponse(5000L, GF("+CSSLCFG:")) != 1) return false; streamSkipUntil('\n'); // read out the certificate information @@ -382,8 +423,12 @@ class TinyGsmSim7000SSL waitResponse(); // set the SSL SNI (server name indication) + // AT+CSSLCFG="SNI",, + // PDP context identifier - for reasons not understood by me, + // use PDP context identifier of 0 for what we defined as 1 in + // the gprsConnect function // NOTE: despite docs using caps, "sni" must be in lower case - sendAT(GF("+CSSLCFG=\"sni\","), mux, ',', GF("\""), host, GF("\"")); + sendAT(GF("+CSSLCFG=\"sni\",0,"), GF("\""), host, GF("\"")); waitResponse(); } @@ -393,7 +438,7 @@ class TinyGsmSim7000SSL // "TCP" or "UDP" // NOTE: the "TCP" can't be included sendAT(GF("+CAOPEN="), mux, GF(",\""), host, GF("\","), port); - if (waitResponse(timeout_ms, GF(GSM_NL "+CAOPEN:")) != 1) { return 0; } + if (waitResponse(timeout_ms, GF(AT_NL "+CAOPEN:")) != 1) { return 0; } // returns OK/r/n/r/n+CAOPEN: , // 0: Success // 1: Socket error @@ -430,7 +475,7 @@ class TinyGsmSim7000SSL // after posting data, module responds with: //+CASEND: ,, - if (waitResponse(GF(GSM_NL "+CASEND:")) != 1) { return 0; } + if (waitResponse(GF(AT_NL "+CASEND:")) != 1) { return 0; } streamSkipUntil(','); // Skip mux if (streamGetIntBefore(',') != 0) { return 0; } // If result != success return streamGetIntBefore('\n'); @@ -588,143 +633,67 @@ class TinyGsmSim7000SSL * Utilities */ public: - // TODO(vshymanskyy): Optimize this! - int8_t waitResponse(uint32_t timeout_ms, String& data, - GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - /*String r1s(r1); r1s.trim(); - String r2s(r2); r2s.trim(); - String r3s(r3); r3s.trim(); - String r4s(r4); r4s.trim(); - String r5s(r5); r5s.trim(); - DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/ - data.reserve(64); - uint8_t index = 0; - uint32_t startMillis = millis(); - do { - TINY_GSM_YIELD(); - while (stream.available() > 0) { - TINY_GSM_YIELD(); - int8_t a = stream.read(); - if (a <= 0) continue; // Skip 0x00 bytes, just in case - data += static_cast(a); - if (r1 && data.endsWith(r1)) { - index = 1; - goto finish; - } else if (r2 && data.endsWith(r2)) { - index = 2; - goto finish; - } else if (r3 && data.endsWith(r3)) { -#if defined TINY_GSM_DEBUG - if (r3 == GFP(GSM_CME_ERROR)) { - streamSkipUntil('\n'); // Read out the error - } -#endif - index = 3; - goto finish; - } else if (r4 && data.endsWith(r4)) { - index = 4; - goto finish; - } else if (r5 && data.endsWith(r5)) { - index = 5; - goto finish; - } else if (data.endsWith(GF("+CARECV:"))) { - int8_t mux = streamGetIntBefore(','); - int16_t len = streamGetIntBefore('\n'); - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; - if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } - } - data = ""; - DBG("### Got Data:", len, "on", mux); - } else if (data.endsWith(GF("+CADATAIND:"))) { - int8_t mux = streamGetIntBefore('\n'); - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - sockets[mux]->got_data = true; - } - data = ""; - DBG("### Got Data:", mux); - } else if (data.endsWith(GF("+CASTATE:"))) { - int8_t mux = streamGetIntBefore(','); - int8_t state = streamGetIntBefore('\n'); - if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { - if (state != 1) { - sockets[mux]->sock_connected = false; - DBG("### Closed: ", mux); - } - } - data = ""; - } else if (data.endsWith(GF("*PSNWID:"))) { - streamSkipUntil('\n'); // Refresh network name by network - data = ""; - DBG("### Network name updated."); - } else if (data.endsWith(GF("*PSUTTZ:"))) { - streamSkipUntil('\n'); // Refresh time and time zone by network - data = ""; - DBG("### Network time and time zone updated."); - } else if (data.endsWith(GF("+CTZV:"))) { - streamSkipUntil('\n'); // Refresh network time zone by network - data = ""; - DBG("### Network time zone updated."); - } else if (data.endsWith(GF("DST: "))) { - streamSkipUntil( - '\n'); // Refresh Network Daylight Saving Time by network - data = ""; - DBG("### Daylight savings time state updated."); - } else if (data.endsWith(GF(GSM_NL "SMS Ready" GSM_NL))) { - data = ""; - DBG("### Unexpected module reset!"); - init(); - data = ""; + bool handleURCs(String& data) { + if (data.endsWith(GF("+CARECV:"))) { + int8_t mux = streamGetIntBefore(','); + int16_t len = streamGetIntBefore('\n'); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->got_data = true; + if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; } + } + data = ""; + DBG("### Got Data:", len, "on", mux); + return true; + } else if (data.endsWith(GF("+CADATAIND:"))) { + int8_t mux = streamGetIntBefore('\n'); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + sockets[mux]->got_data = true; + } + data = ""; + DBG("### Got Data:", mux); + return true; + } else if (data.endsWith(GF("+CASTATE:"))) { + int8_t mux = streamGetIntBefore(','); + int8_t state = streamGetIntBefore('\n'); + if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) { + if (state != 1) { + sockets[mux]->sock_connected = false; + DBG("### Closed: ", mux); } } - } while (millis() - startMillis < timeout_ms); - finish: - if (!index) { - data.trim(); - if (data.length()) { DBG("### Unhandled:", data); } data = ""; + return true; + } else if (data.endsWith(GF("*PSNWID:"))) { + streamSkipUntil('\n'); // Refresh network name by network + data = ""; + DBG("### Network name updated."); + return true; + } else if (data.endsWith(GF("*PSUTTZ:"))) { + streamSkipUntil('\n'); // Refresh time and time zone by network + data = ""; + DBG("### Network time and time zone updated."); + return true; + } else if (data.endsWith(GF("+CTZV:"))) { + streamSkipUntil('\n'); // Refresh network time zone by network + data = ""; + DBG("### Network time zone updated."); + return true; + } else if (data.endsWith(GF("DST: "))) { + streamSkipUntil('\n'); // Refresh Network Daylight Saving Time by network + data = ""; + DBG("### Daylight savings time state updated."); + return true; + } else if (data.endsWith(GF(AT_NL "SMS Ready" AT_NL))) { + data = ""; + DBG("### Unexpected module reset!"); + init(); + return true; } - // data.replace(GSM_NL, "/"); - // DBG('<', index, '>', data); - return index; - } - - int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - String data; - return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5); - } - - int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK), - GsmConstStr r2 = GFP(GSM_ERROR), -#if defined TINY_GSM_DEBUG - GsmConstStr r3 = GFP(GSM_CME_ERROR), - GsmConstStr r4 = GFP(GSM_CMS_ERROR), -#else - GsmConstStr r3 = NULL, GsmConstStr r4 = NULL, -#endif - GsmConstStr r5 = NULL) { - return waitResponse(1000, r1, r2, r3, r4, r5); + return false; } protected: GsmClientSim7000SSL* sockets[TINY_GSM_MUX_COUNT]; - String certificates[TINY_GSM_MUX_COUNT]; }; #endif // SRC_TINYGSMCLIENTSIM7000SSL_H_ diff --git a/src/TinyGsmClientSIM7080.h b/src/TinyGsmClientSIM7080.h index d46cea0f..d39cb861 100644 --- a/src/TinyGsmClientSIM7080.h +++ b/src/TinyGsmClientSIM7080.h @@ -18,13 +18,31 @@ #include "TinyGsmClientSIM70xx.h" #include "TinyGsmTCP.tpp" #include "TinyGsmSSL.tpp" +#include "TinyGsmSMS.tpp" +#include "TinyGsmGSMLocation.tpp" +#include "TinyGsmTime.tpp" +#include "TinyGsmNTP.tpp" +#include "TinyGsmBattery.tpp" class TinyGsmSim7080 : public TinyGsmSim70xx, - public TinyGsmTCP, - public TinyGsmSSL { - friend class TinyGsmSim70xx; - friend class TinyGsmTCP; - friend class TinyGsmSSL; + public TinyGsmTCP, + public TinyGsmSSL, + public TinyGsmSMS, + public TinyGsmGSMLocation, + public TinyGsmTime, + public TinyGsmNTP, + public TinyGsmBattery { + friend class TinyGsmSim70xx; + friend class TinyGsmModem; + friend class TinyGsmGPRS; + friend class TinyGsmTCP; + friend class TinyGsmSSL; + friend class TinyGsmSMS; + friend class TinyGsmGSMLocation; + friend class TinyGsmGPS; + friend class TinyGsmTime; + friend class TinyGsmNTP; + friend class TinyGsmBattery; /* * Inner Client @@ -570,65 +588,42 @@ class TinyGsmSim7080 : public TinyGsmSim70xx, } size_t modemGetAvailable(uint8_t mux) { - // If the socket doesn't exist, just return - if (!sockets[mux]) { - return 0; + // Reset all sock_available values to 0 + for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) { + GsmClientSim7080* sock = sockets[muxNo]; + if (sock) { + sock->sock_available = 0; + } } - // NOTE: This gets how many characters are available on all connections that - // have data. It does not return all the connections, just those with data. + + // Request awaiting data, will return zero or more +CARECV: answers, ending with an OK sendAT(GF("+CARECV?")); - for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) { - // after the last connection, there's an ok, so we catch it right away - int res = waitResponse(3000, GF("+CARECV:"), GFP(GSM_OK), GFP(GSM_ERROR)); - // if we get the +CARECV: response, read the mux number and the number of - // characters available - if (res == 1) { + + while (int result = waitResponse(3000, GF("+CARECV:"), GFP(GSM_OK), GFP(GSM_ERROR))) { + if (result == 1) { + // if we get the +CARECV: response, read the mux number and the number of + // characters available int ret_mux = streamGetIntBefore(','); - size_t result = streamGetIntBefore('\n'); - GsmClientSim7080* sock = sockets[ret_mux]; - if (sock) { - sock->sock_available = result; - } - // if the first returned mux isn't 0 (or is higher than expected) - // we need to fill in the missing muxes - if (ret_mux > muxNo) { - for (int extra_mux = muxNo; extra_mux < ret_mux + 1; extra_mux++) { - GsmClientSim7080* isock = sockets[extra_mux]; - if (isock) { - isock->sock_available = result; - } - } - muxNo = ret_mux; - } - } else if (res == 2) { - // if we get an OK, we've reached the last socket with available data - // so we set any we haven't gotten to yet to 0 - for (int extra_mux = muxNo; extra_mux < TINY_GSM_MUX_COUNT; - extra_mux++) { - GsmClientSim7080* isock = sockets[extra_mux]; - if (isock) { - isock->sock_available = 0; + size_t charsAvailable = streamGetIntBefore('\n'); + if (0 <= ret_mux && ret_mux < TINY_GSM_MUX_COUNT) { + GsmClientSim7080* sock = sockets[ret_mux]; + if (sock) { + sock->sock_available = charsAvailable; } } - break; } else { - // if we got an error, give up + // Ok, timeout or Error -> we're done break; } - // Should be a final OK at the end. - // If every connection was returned, catch the OK here. - // If only a portion were returned, catch it above. - if (muxNo == TINY_GSM_MUX_COUNT - 1) { - waitResponse(); - } } + modemGetConnected(mux); // check the state of all connections if (!sockets[mux]) { return 0; } return sockets[mux]->sock_available; } - + bool modemGetConnected(uint8_t mux) { // NOTE: This gets the state of all connections that have been opened // since the last connection @@ -689,26 +684,511 @@ class TinyGsmSim7080 : public TinyGsmSim70xx, * Utilities */ public: + uint8_t getNetworkSystemMode() { + sendAT(GF("+CNSMOD?")); + if (waitResponse(GF(GSM_NL "+CNSMOD:")) != 1) { + return 0; + } + String res = stream.readStringUntil('\n'); + waitResponse(); + res = res.substring(3); + return atoi(res.c_str()); + } + + String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; + }; + + /* + * Inner Secure Client + */ + public: + class GsmClientSecureSIM7080 : public GsmClientSim7080 { + public: + GsmClientSecureSIM7080() {} + + explicit GsmClientSecureSIM7080(TinyGsmSim7080& modem, uint8_t mux = 0) + : GsmClientSim7080(modem, mux) {} + + bool setCertificate(const String& certificateName) { + return at->setCertificate(certificateName, mux); + } + + int connect(const char* host, uint16_t port, int timeout_s) override { + stop(); + TINY_GSM_YIELD(); + rx.clear(); + sock_connected = at->modemConnect(host, port, mux, true, timeout_s); + return sock_connected; + } + TINY_GSM_CLIENT_CONNECT_OVERRIDES + }; + + /* + * Constructor + */ + public: + explicit TinyGsmSim7080(Stream& stream) + : TinyGsmSim70xx(stream) { + memset(sockets, 0, sizeof(sockets)); + } + + /* + * Basic functions + */ + protected: + bool initImpl(const char* pin = nullptr) { + DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION); + DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM7080")); + + bool gotATOK = false; + for (uint32_t start = millis(); millis() - start < 10000L;) { + sendAT(GF("")); + int8_t resp = waitResponse(200L, GFP(GSM_OK), GFP(GSM_ERROR), GF("AT")); + if (resp == 1) { + gotATOK = true; + break; + } else if (resp == 3) { + waitResponse(200L); // get the OK + sendAT(GF("E0")); // Echo Off + DBG(GF("## Turning off echo!")); + waitResponse(2000L); + } + delay(100); + } + if (!gotATOK) { return false; } + +#ifdef TINY_GSM_DEBUG + sendAT(GF("+CMEE=2")); // turn on verbose error codes +#else + sendAT(GF("+CMEE=0")); // turn off error codes +#endif + waitResponse(); + + DBG(GF("### Modem:"), getModemName()); + + // Enable Local Time Stamp for getting network time + sendAT(GF("+CLTS=1")); + if (waitResponse(10000L) != 1) { return false; } + + // Enable battery checks + sendAT(GF("+CBATCHK=1")); + if (waitResponse() != 1) { return false; } + + SimStatus ret = getSimStatus(); + // if the sim isn't ready and a pin has been provided, try to unlock the sim + if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) { + simUnlock(pin); + return (getSimStatus() == SIM_READY); + } else { + // if the sim is ready, or it's locked but no pin has been provided, + // return true + return (ret == SIM_READY || ret == SIM_LOCKED); + } + } + + void maintainImpl() { + // Keep listening for modem URC's and proactively iterate through + // sockets asking if any data is avaiable + bool check_socks = false; + for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { + GsmClientSim7080* sock = sockets[mux]; + if (sock && sock->got_data) { + sock->got_data = false; + check_socks = true; + } + } + // modemGetAvailable checks all socks, so we only want to do it once + // modemGetAvailable calls modemGetConnected(), which also checks allf + if (check_socks) { modemGetAvailable(0); } + while (stream.available()) { waitResponse(15, nullptr, nullptr); } + } + + /* + * Power functions + */ + protected: + bool restartImpl(const char* pin = nullptr) { + bool success = true; + + bool gotATOK = false; + for (uint32_t start = millis(); millis() - start < 10000L;) { + sendAT(GF("")); + int8_t resp = waitResponse(200L, GFP(GSM_OK), GFP(GSM_ERROR), GF("AT")); + if (resp == 1) { + gotATOK = true; + break; + } else if (resp == 3) { + waitResponse(200L); // get the OK + DBG(GF("## Turning off echo!")); + sendAT(GF("E0")); // Echo Off + waitResponse(2000L); + } + delay(100); + } + if (!gotATOK) { return false; } + + sendAT(GF("+CREBOOT")); // Reboot + success &= waitResponse() == 1; + waitResponse(30000L, GF("SMS Ready")); + success &= initImpl(pin); + return success; + } + + /* + * Generic network functions + */ + protected: + String getLocalIPImpl() { + sendAT(GF("+CNACT?")); + if (waitResponse(GF(AT_NL "+CNACT:")) != 1) { return ""; } + streamSkipUntil('\"'); + String res = stream.readStringUntil('\"'); + waitResponse(); + return res; + } + + /* + * Secure socket layer (SSL) functions + */ + // Follows functions as inherited from TinyGsmSSL.tpp + + /* + * WiFi functions + */ + // No functions of this type supported + + /* + * GPRS functions + */ + protected: + bool gprsConnectImpl(const char* apn, const char* user = nullptr, + const char* pwd = nullptr) { + gprsDisconnect(); + + // Define the PDP context + sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"'); + waitResponse(); + + // Attach to GPRS + sendAT(GF("+CGATT=1")); + if (waitResponse(60000L) != 1) { return false; } + + // NOTE: **DO NOT** activate the PDP context + // For who only knows what reason, doing so screws up the rest of the + // process + + // Check the APN returned by the server + // not sure why, but the connection is more consistent with this + sendAT(GF("+CGNAPN")); + waitResponse(); + + // Bearer settings for applications based on IP + // Set the user name and password + // AT+CNCFG=,,[,[,,[]]] + // PDP Context Identifier - for reasons not understood by me, + // use PDP context identifier of 0 for what we defined as 1 above + // 0: Dual PDN Stack + // 1: Internet Protocol Version 4 + // 2: Internet Protocol Version 6 + // 0: NONE + // 1: PAP + // 2: CHAP + // 3: PAP or CHAP + if (pwd && strlen(pwd) > 0 && user && strlen(user) > 0) { + sendAT(GF("+CNCFG=0,1,\""), apn, "\",\"", user, "\",\"", pwd, "\",3"); + waitResponse(); + } else if (user && strlen(user) > 0) { + // Set the user name only + sendAT(GF("+CNCFG=0,1,\""), apn, "\",\"", user, '"'); + waitResponse(); + } else { + // Set the APN only + sendAT(GF("+CNCFG=0,1,\""), apn, '"'); + waitResponse(); + } + + // Activate application network connection + // AT+CNACT=, + // PDP Context Identifier - for reasons not understood by me, + // use PDP context identifier of 0 for what we defined as 1 above + // 0: Deactive + // 1: Active + // 2: Auto Active + bool res = false; + int ntries = 0; + while (!res && ntries < 5) { + sendAT(GF("+CNACT=0,1")); + res = waitResponse(60000L, GF(AT_NL "+APP PDP: 0,ACTIVE"), + GF(AT_NL "+APP PDP: 0,DEACTIVE")); + waitResponse(); + ntries++; + } + + return res; + } + + bool gprsDisconnectImpl() { + // Shut down the general application TCP/IP connection + // CNACT will close *all* open application connections + sendAT(GF("+CNACT=0,0")); + if (waitResponse(60000L) != 1) { return false; } + + sendAT(GF("+CGATT=0")); // Deactivate the bearer context + if (waitResponse(60000L) != 1) { return false; } + + return true; + } + + /* + * SIM card functions + */ + // Follows functions as inherited from TinyGsmClientSIM70xx.h + + /* + * Phone Call functions + */ + // No functions of this type supported + + /* + * Audio functions + */ + // No functions of this type supported + + /* + * Text messaging (SMS) functions + */ + // Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp + + /* + * GSM Location functions + */ + // Follows all GSM-based location functions as inherited from + // TinyGsmGSMLocation.tpp + + /* + * GPS/GNSS/GLONASS location functions + */ + // Follows functions as inherited from TinyGsmClientSIM70xx.h + + /* + * Time functions + */ + // Follows all clock functions as inherited from TinyGsmTime.tpp + + /* + * NTP server functions + */ + protected: + byte NTPServerSyncImpl(String server = "pool.ntp.org", int TimeZone = 0) { + // Set GPRS bearer profile to associate with NTP sync + // this may fail, it's not supported by all modules + sendAT(GF("+CNTPCID=0")); // CID must be 0. With 1 (like other modules) + // does not work! + waitResponse(10000L); + + // Set NTP server and timezone + sendAT(GF("+CNTP=\""), server, "\",", String(TimeZone)); + if (waitResponse(10000L) != 1) { return -1; } + + // Request network synchronization + sendAT(GF("+CNTP")); + if (waitResponse(10000L, GF("+CNTP:"))) { + String result = stream.readStringUntil('\n'); + // Check for ',' in case the module appends the time next to the return + // code. Eg: +CNTP: [,