Browse Source

Merge pull request #285 from EnviroDIY/master

Goodbye virtual class (hello, pre-processor macros!)
v_master v0.7.4
Sara Damiano 5 years ago
committed by GitHub
parent
commit
d678b85a9e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 3209 additions and 2287 deletions
  1. +15
    -11
      .travis.yml
  2. +21
    -20
      README.md
  3. +38
    -4
      examples/AllFunctions/AllFunctions.ino
  4. +3
    -1
      examples/BlynkClient/BlynkClient.ino
  5. +3
    -1
      examples/FileDownload/FileDownload.ino
  6. +47
    -20
      examples/HttpClient/HttpClient.ino
  7. +44
    -19
      examples/HttpsClient/HttpsClient.ino
  8. +3
    -1
      examples/MqttClient/MqttClient.ino
  9. +58
    -44
      examples/WebClient/WebClient.ino
  10. BIN
      extras/AT Command Manuals/Sequans Monarch AT Commands Manual.pdf
  11. +1
    -1
      library.json
  12. +1
    -1
      library.properties
  13. +17
    -0
      src/TinyGsmClient.h
  14. +77
    -158
      src/TinyGsmClientA6.h
  15. +88
    -203
      src/TinyGsmClientBG96.h
  16. +52
    -114
      src/TinyGsmClientESP8266.h
  17. +53
    -181
      src/TinyGsmClientM590.h
  18. +100
    -203
      src/TinyGsmClientM95.h
  19. +87
    -212
      src/TinyGsmClientMC60.h
  20. +86
    -222
      src/TinyGsmClientSIM7000.h
  21. +91
    -219
      src/TinyGsmClientSIM800.h
  22. +760
    -0
      src/TinyGsmClientSaraR4.h
  23. +770
    -0
      src/TinyGsmClientSequansMonarch.h
  24. +140
    -377
      src/TinyGsmClientUBLOX.h
  25. +257
    -163
      src/TinyGsmClientXBee.h
  26. +397
    -112
      src/TinyGsmCommon.h

+ 15
- 11
.travis.yml View File

@ -18,17 +18,19 @@ env:
- PLATFORMIO_CI_SRC=tools/FactoryReset
# Arduino test
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_A6' --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 --board=mayfly"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_BG96' --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 --board=mayfly"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_ESP8266' --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 --board=mayfly"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_M95' --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 --board=mayfly"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_M590' --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 --board=mayfly"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_MC60' --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 --board=mayfly"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM800' --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 --board=mayfly"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM808' --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 --board=mayfly"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM7000' --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 --board=mayfly"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_UBLOX' --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 --board=mayfly"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_XBEE' --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 --board=mayfly"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_A6' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_BG96' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_ESP8266' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_M95' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_M590' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_MC60' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM800' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM808' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM7000' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_UBLOX' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SARAR4' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_XBEE' --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"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SEQUANS_MONARCH' --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"
# Energia test
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_A6' --project-option='framework=energia' --board=lplm4f120h5qr"
@ -41,7 +43,9 @@ env:
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM808' --project-option='framework=energia' --board=lplm4f120h5qr"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SIM7000' --project-option='framework=energia' --board=lplm4f120h5qr"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_UBLOX' --project-option='framework=energia' --board=lplm4f120h5qr"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SARAR4' --project-option='framework=energia' --board=lplm4f120h5qr"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_XBEE' --project-option='framework=energia' --board=lplm4f120h5qr"
- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_SEQUANS_MONARCH' --project-option='framework=energia' --board=lplm4f120h5qr"
# Disabled due to a bug in Energia readBytes implementation
#- PLATFORMIO_CI_SRC=tools/test_build PLATFORMIO_CI_ARGS="--project-option='build_flags=-D TINY_GSM_MODEM_XBEE' --project-option='framework=energia' --board=lplm4f120h5qr"


+ 21
- 20
README.md View File

@ -35,31 +35,31 @@ TinyGSM also pulls data gently from the modem (whenever possible), so it can ope
## Features
Feature \ Modem | SIM8xx | u-Blox | A6/A7/A20 | Neoway M590| ESP8266 |Digi XBee|Quectel BG96|Quectel M95|Quectel MC60(E)| SIM7000 |
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
Feature \ Modem | SIM8xx | u-Blox | A6/A7/A20 | Neoway M590| ESP8266 |Digi XBee|Quectel BG96|Quectel M95|Quectel MC60(E)| SIM7000 | Monarch |
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
**Data connections**
TCP (HTTP, MQTT, Blynk, ...) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | |
UDP | ◌ | ◌ | | | | ◌ | ◌ | | | ◌ |
SSL/TLS (HTTPS) | ✔¹ | ✔ | x | x | ✔ | ✔ | ◌ | | | ◌ |
TCP (HTTP, MQTT, Blynk, ...) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | ✔ |
UDP | ◌ | ◌ | | | | ◌ | ◌ | | | ◌ | ◌ |
SSL/TLS (HTTPS) | ✔¹ | ✔ | x | x | ✔ | ✔ | ◌ | | | ◌ | ✔ |
**USSD**
Sending USSD requests | ✔ | | ✔ | ✔ | x | | | | | ✔ |
Decoding 7,8,16-bit response | ✔ | | ✔ | ✔ | x | | | | | ✔ |
Sending USSD requests | ✔ | | ✔ | ✔ | x | | | | | ✔ | |
Decoding 7,8,16-bit response | ✔ | | ✔ | ✔ | x | | | | | ✔ | |
**SMS**
Sending | ✔ | ✔ | ✔ | ✔ | x | ✔ | ✔ | ✔ | ✔ | ✔ |
Sending Unicode | ✔ | | ◌ | x | x | | ✔ | ✔ | ✔ | ✔ |
Reading | | | | | x | | | | | |
Incoming message event | | | | ? | x | | | | | |
Sending | ✔ | ✔ | ✔ | ✔ | x | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
Sending Unicode | ✔ | | ◌ | x | x | | ✔ | ✔ | ✔ | ✔ | |
Reading | | | | | x | | | | | | |
Incoming message event | | | | ? | x | | | | | | |
**Calls**
Dial, hangup | ✔ | | ✔ | x | x | x | | | | |
Receiving calls | ✔ | | ✔ | x | x | x | | | | |
Incoming event (RING) | ◌ | | ◌ | x | x | x | | | | |
DTMF sending | ✔ | | ✔ | x | x | x | | | | |
DTMF decoding | ◌ | | x | x | x | x | | | | |
Dial, hangup | ✔ | | ✔ | x | x | x | | | | | |
Receiving calls | ✔ | | ✔ | x | x | x | | | | | |
Incoming event (RING) | ◌ | | ◌ | x | x | x | | | | | |
DTMF sending | ✔ | | ✔ | x | x | x | | | | | |
DTMF decoding | ◌ | | x | x | x | x | | | | | |
**Location**
GSM location service | ✔ | ✔ | x | x | x | x | | x | ✔ | ✔ |
GPS/GNSS | ✔¹ | x | ◌¹ | x | x | x | | x | | ✔ |
GSM location service | ✔ | ✔ | x | x | x | x | | x | ✔ | ✔ | x |
GPS/GNSS | ✔¹ | x | ◌¹ | x | x | x | | x | | ✔ | x |
**Credits**
Primary Author/Contributor |[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[SRGDamia1](https://github.com/SRGDamia1/)|[vshymanskyy](https://github.com/vshymanskyy) |[replicadeltd](https://github.com/replicadeltd)|[V1pr](https://github.com/V1pr)|[captFuture](https://github.com/captFuture/)|
Primary Author/Contributor |[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[vshymanskyy](https://github.com/vshymanskyy)|[SRGDamia1](https://github.com/SRGDamia1/)|[vshymanskyy](https://github.com/vshymanskyy) |[replicadeltd](https://github.com/replicadeltd)|[V1pr](https://github.com/V1pr)|[captFuture](https://github.com/captFuture/)|[nootropicdesign](https://github.com/nootropicdesign/)|
✔ - implemented  ◌ - planned  x - not available on this modem
¹ - only some device models or firmware revisions have this feature (SIM8xx R14.18, A7, etc.)
@ -73,6 +73,7 @@ Primary Author/Contributor |[vshymanskyy](https://github.com/vshymanskyy)|[vsh
- Digi XBee WiFi and Cellular (using XBee command mode)
- Neoway M590
- u-blox Cellular Modems (many modules including LEON-G100, LISA-U2xx, SARA-G3xx, SARA-U2xx, TOBY-L2xx, LARA-R2xx, MPCI-L2xx, SARA-R4xx, SARA-N4xx, _but NOT SARA-N2xx_)
- Sequans Monarch LTE Cat M1/NB1 ***(beta)***
- Quectel BG96 ***(alpha)***
- Quectel M95 ***(alpha)***
- Quectel MC60 ***(alpha)***
@ -89,9 +90,9 @@ Primary Author/Contributor |[vshymanskyy](https://github.com/vshymanskyy)|[vsh
- ... 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:
- [ ] Sequans Monarch LTE Cat M1/NB1
- [ ] Quectel M10, UG95
- [ ] SIMCom SIM5320, SIM5360, SIM5216
- [ ] SIMCom SIM7020, SIM7100
- [ ] Telit GL865
- [ ] ZTE MG2639
- [ ] Hi-Link HLK-RM04


+ 38
- 4
examples/AllFunctions/AllFunctions.ino View File

@ -10,11 +10,13 @@
**************************************************************/
// Select your modem:
//#define TINY_GSM_MODEM_SIM800
#define TINY_GSM_MODEM_SIM800
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM868
// #define TINY_GSM_MODEM_SIM900
#define TINY_GSM_MODEM_SIM7000
// #define TINY_GSM_MODEM_SIM7000
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
@ -24,8 +26,9 @@
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// Set serial for debug console (to the Serial Monitor, speed 115200)
// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial
// Set serial for AT commands (to the module)
@ -36,10 +39,13 @@
//#include <SoftwareSerial.h>
//SoftwareSerial SerialAT(2, 3); // RX, TX
// See all AT commands, if wanted
//#define DUMP_AT_COMMANDS
// See the debugging, if wanted
#define TINY_GSM_DEBUG SerialMon
// Range to attempt to autobaud
#define GSM_AUTOBAUD_MIN 9600
#define GSM_AUTOBAUD_MAX 38400
@ -47,6 +53,7 @@
* Test enabled
*/
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#define TINY_GSM_USE_CALL true
#define TINY_GSM_USE_SMS true
#define TINY_GSM_USE_USSD true
@ -65,6 +72,8 @@
const char apn[] = "YourAPN";
const char user[] = "";
const char pass[] = "";
const char wifiSSID[] = "YourSSID";
const char wifiPass[] = "SSIDpw";
#include <TinyGsmClient.h>
@ -82,11 +91,18 @@ void setup() {
delay(10);
// Set your reset, enable, power pins here
pinMode(20, OUTPUT);
digitalWrite(20, HIGH);
pinMode(23, OUTPUT);
digitalWrite(23, HIGH);
DBG("Wait...");
delay(3000);
// Set GSM module baud rate
TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX);
// SerialAT.begin(9600);
}
void loop() {
@ -95,6 +111,7 @@ void loop() {
// To skip it, call init() instead of restart()
DBG("Initializing modem...");
if (!modem.restart()) {
// if (!modem.init()) {
DBG("Failed to restart modem, delaying 10s and retrying");
delay(3000);
// restart autobaud in case GSM just rebooted
@ -106,10 +123,22 @@ void loop() {
String modemInfo = modem.getModemInfo();
DBG("Modem:", modemInfo);
#if TINY_GSM_USE_GPRS
// Unlock your SIM card with a PIN if needed
if ( GSM_PIN && modem.getSimStatus() != 3 ) {
modem.simUnlock(GSM_PIN);
}
#endif
#if TINY_GSM_USE_WIFI
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" OK");
#endif
DBG("Waiting for network...");
if (!modem.waitForNetwork()) {
@ -221,6 +250,11 @@ void loop() {
}
#endif
#if TINY_GSM_USE_WIFI
modem.networkDisconnect();
DBG("WiFi disconnected");
#endif
#if TINY_GSM_POWERDOWN
// Try to power-off (modem may decide to restart automatically)
// To turn off modem completely, please use Reset/Enable pins


+ 3
- 1
examples/BlynkClient/BlynkClient.ino View File

@ -30,11 +30,12 @@
// Select your modem:
#define TINY_GSM_MODEM_SIM800
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM868
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM7000
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
@ -44,6 +45,7 @@
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
#include <TinyGsmClient.h>
#include <BlynkSimpleSIM800.h>


+ 3
- 1
examples/FileDownload/FileDownload.ino View File

@ -15,11 +15,12 @@
// Select your modem:
#define TINY_GSM_MODEM_SIM800
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM868
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM7000
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
@ -29,6 +30,7 @@
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// Increase RX buffer if needed
#define TINY_GSM_RX_BUFFER 1024


+ 47
- 20
examples/HttpClient/HttpClient.ino View File

@ -19,11 +19,12 @@
// Select your modem:
#define TINY_GSM_MODEM_SIM800
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM868
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM7000
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
@ -32,6 +33,8 @@
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// Increase RX buffer to capture the entire response
// Chips without internal buffering (A6/A7, ESP8266, M590)
@ -39,6 +42,9 @@
// else data will be lost (and the http library will fail).
#define TINY_GSM_RX_BUFFER 650
// See all AT commands, if wanted
//#define DUMP_AT_COMMANDS
// See the debugging, if wanted
//#define TINY_GSM_DEBUG Serial
//#define LOGGING
@ -49,12 +55,10 @@
#include <TinyGsmClient.h>
#include <ArduinoHttpClient.h>
// Uncomment this if you want to see all AT commands
//#define DUMP_AT_COMMANDS
// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial
// Set serial for AT commands (to the module)
// Use Hardware Serial on Mega, Leonardo, Micro
#define SerialAT Serial1
@ -62,6 +66,11 @@
//#include <SoftwareSerial.h>
//SoftwareSerial SerialAT(2, 3); // RX, TX
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
// set GSM PIN, if any
#define GSM_PIN ""
// Your GPRS credentials
// Leave empty, if missing user or pass
@ -91,7 +100,15 @@ void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
SerialMon.println(F("Wait..."));
// Set your reset, enable, power pins here
pinMode(20, OUTPUT);
digitalWrite(20, HIGH);
pinMode(23, OUTPUT);
digitalWrite(23, HIGH);
SerialMon.println("Wait...");
// Set GSM module baud rate
SerialAT.begin(115200);
@ -99,11 +116,11 @@ void setup() {
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.println(F("Initializing modem..."));
SerialMon.println("Initializing modem...");
modem.restart();
String modemInfo = modem.getModemInfo();
SerialMon.print(F("Modem: "));
SerialMon.print("Modem: ");
SerialMon.println(modemInfo);
// Unlock your SIM card with a PIN
@ -112,17 +129,17 @@ void setup() {
void loop() {
if (modem.hasWifi()) {
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" OK");
#if TINY_GSM_USE_WIFI
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" OK");
#endif
SerialMon.print(F("Waiting for network..."));
SerialMon.print("Waiting for network...");
if (!modem.waitForNetwork()) {
SerialMon.println(" fail");
delay(10000);
@ -130,7 +147,11 @@ void loop() {
}
SerialMon.println(" OK");
if (modem.hasGPRS()) {
if (modem.isNetworkConnected()) {
SerialMon.print("Network connected");
}
#if TINY_GSM_USE_GPRS
SerialMon.print(F("Connecting to "));
SerialMon.print(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
@ -139,7 +160,7 @@ void loop() {
return;
}
SerialMon.println(" OK");
}
#endif
SerialMon.print(F("Performing HTTP GET request... "));
int err = http.get(resource);
@ -185,8 +206,14 @@ void loop() {
http.stop();
SerialMon.println(F("Server disconnected"));
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
#if TINY_GSM_USE_WIFI
modem.networkDisconnect();
SerialMon.println(F("WiFi disconnected"));
#endif
#if TINY_GSM_USE_GPRS
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
#endif
// Do nothing forevermore
while (true) {


+ 44
- 19
examples/HttpsClient/HttpsClient.ino View File

@ -21,6 +21,7 @@
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM868
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_ESP8266
// Increase RX buffer to capture the entire response
@ -29,6 +30,9 @@
// else data will be lost (and the http library will fail).
#define TINY_GSM_RX_BUFFER 650
// See all AT commands, if wanted
//#define DUMP_AT_COMMANDS
// See the debugging, if wanted
//#define TINY_GSM_DEBUG Serial
//#define LOGGING
@ -39,12 +43,10 @@
#include <TinyGsmClient.h>
#include <ArduinoHttpClient.h>
// Uncomment this if you want to see all AT commands
//#define DUMP_AT_COMMANDS
// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial
// Set serial for AT commands (to the module)
// Use Hardware Serial on Mega, Leonardo, Micro
#define SerialAT Serial1
@ -52,6 +54,11 @@
//#include <SoftwareSerial.h>
//SoftwareSerial SerialAT(2, 3); // RX, TX
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
// set GSM PIN, if any
#define GSM_PIN ""
// Your GPRS credentials
// Leave empty, if missing user or pass
@ -81,7 +88,15 @@ void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
SerialMon.println(F("Wait..."));
// Set your reset, enable, power pins here
pinMode(20, OUTPUT);
digitalWrite(20, HIGH);
pinMode(23, OUTPUT);
digitalWrite(23, HIGH);
SerialMon.println("Wait...");
// Set GSM module baud rate
SerialAT.begin(115200);
@ -89,11 +104,11 @@ void setup() {
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.println(F("Initializing modem..."));
SerialMon.println("Initializing modem...");
modem.restart();
String modemInfo = modem.getModemInfo();
SerialMon.print(F("Modem: "));
SerialMon.print("Modem: ");
SerialMon.println(modemInfo);
// Unlock your SIM card with a PIN
@ -107,17 +122,17 @@ void setup() {
void loop() {
if (modem.hasWifi()) {
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" OK");
#if TINY_GSM_USE_WIFI
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" OK");
#endif
SerialMon.print(F("Waiting for network..."));
SerialMon.print("Waiting for network...");
if (!modem.waitForNetwork()) {
SerialMon.println(" fail");
delay(10000);
@ -125,7 +140,11 @@ void loop() {
}
SerialMon.println(" OK");
if (modem.hasGPRS()) {
if (modem.isNetworkConnected()) {
SerialMon.print("Network connected");
}
#if TINY_GSM_USE_GPRS
SerialMon.print(F("Connecting to "));
SerialMon.print(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
@ -134,7 +153,7 @@ void loop() {
return;
}
SerialMon.println(" OK");
}
#endif
SerialMon.print(F("Performing HTTPS GET request... "));
http.connectionKeepAlive(); // Currently, this is needed for HTTPS
@ -181,8 +200,14 @@ void loop() {
http.stop();
SerialMon.println(F("Server disconnected"));
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
#if TINY_GSM_USE_WIFI
modem.networkDisconnect();
SerialMon.println(F("WiFi disconnected"));
#endif
#if TINY_GSM_USE_GPRS
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
#endif
// Do nothing forevermore
while (true) {


+ 3
- 1
examples/MqttClient/MqttClient.ino View File

@ -29,11 +29,12 @@
// Select your modem:
#define TINY_GSM_MODEM_SIM800
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM868
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM7000
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
@ -43,6 +44,7 @@
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
#include <TinyGsmClient.h>
#include <PubSubClient.h>


+ 58
- 44
examples/WebClient/WebClient.ino View File

@ -10,11 +10,12 @@
// Select your modem:
#define TINY_GSM_MODEM_SIM800
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM868
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM7000
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
@ -24,21 +25,13 @@
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// Increase RX buffer if needed
//#define TINY_GSM_RX_BUFFER 512
// See the debugging, if wanted
//#define TINY_GSM_DEBUG Serial
//#define LOGGING
// #define TINY_GSM_RX_BUFFER 512
// Add a reception delay, if needed
//#define TINY_GSM_YIELD() { delay(1); }
#include <TinyGsmClient.h>
// Uncomment this if you want to see all AT commands
//#define DUMP_AT_COMMANDS
// #define TINY_GSM_YIELD() { delay(1); }
// Uncomment this if you want to use SSL
//#define USE_SSL
@ -46,6 +39,7 @@
// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial
// Set serial for AT commands (to the module)
// Use Hardware Serial on Mega, Leonardo, Micro
#define SerialAT Serial1
@ -53,6 +47,21 @@
//#include <SoftwareSerial.h>
//SoftwareSerial SerialAT(2, 3); // RX, TX
// See all AT commands, if wanted
//#define DUMP_AT_COMMANDS
// See the debugging, if wanted
// #define TINY_GSM_DEBUG SerialMon
// Range to attempt to autobaud
#define GSM_AUTOBAUD_MIN 9600
#define GSM_AUTOBAUD_MAX 38400
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
// set GSM PIN, if any
#define GSM_PIN ""
// Your GPRS credentials
// Leave empty, if missing user or pass
@ -66,6 +75,8 @@ const char wifiPass[] = "SSIDpw";
const char server[] = "vsh.pp.ua";
const char resource[] = "/TinyGSM/logo.txt";
#include <TinyGsmClient.h>
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
@ -86,22 +97,29 @@ void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
SerialMon.println(F("Wait..."));
// Set your reset, enable, power pins here
pinMode(20, OUTPUT);
digitalWrite(20, HIGH);
pinMode(23, OUTPUT);
digitalWrite(23, LOW);
digitalWrite(23, HIGH);
SerialMon.println("Wait...");
// Set GSM module baud rate
// TinyGsmAutoBaud(SerialAT,GSM_AUTOBAUD_MIN,GSM_AUTOBAUD_MAX);
SerialAT.begin(9600);
delay(3000);
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.println(F("Initializing modem..."));
SerialMon.println("Initializing modem...");
modem.restart();
// modem.init();
String modemInfo = modem.getModemInfo();
SerialMon.print(F("Modem: "));
SerialMon.print("Modem: ");
SerialMon.println(modemInfo);
// Unlock your SIM card with a PIN
@ -110,46 +128,41 @@ void setup() {
void loop() {
if (modem.hasWifi()) {
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" OK");
}
else if (modem.getModemName().indexOf("XBee") >= 0) {
SerialMon.print(F("Setting APN"));
if (!modem.gprsConnect(apn)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" OK");
#if TINY_GSM_USE_WIFI
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" OK");
#endif
SerialMon.print(F("Waiting for network..."));
if (!modem.waitForNetwork()) {
SerialMon.print("Waiting for network...");
if (!modem.waitForNetwork(240000L)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" OK");
if (modem.hasGPRS()) {
if (modem.isNetworkConnected()) {
SerialMon.println("Network connected");
}
#if TINY_GSM_USE_GPRS
SerialMon.print(F("Connecting to "));
SerialMon.print(apn);
SerialMon.println(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" OK");
}
#endif
SerialMon.print(F("Connecting to "));
SerialMon.print(server);
SerialMon.print("Connecting to ");
SerialMon.println(server);
if (!client.connect(server, port)) {
SerialMon.println(" fail");
delay(10000);
@ -158,6 +171,7 @@ void loop() {
SerialMon.println(" OK");
// Make a HTTP GET request:
SerialMon.println("Performing HTTP GET request...");
client.print(String("GET ") + resource + " HTTP/1.0\r\n");
client.print(String("Host: ") + server + "\r\n");
client.print("Connection: close\r\n\r\n");
@ -178,14 +192,14 @@ void loop() {
client.stop();
SerialMon.println(F("Server disconnected"));
if (modem.hasWifi()) {
#if TINY_GSM_USE_WIFI
modem.networkDisconnect();
SerialMon.println(F("WiFi disconnected"));
}
else {
#endif
#if TINY_GSM_USE_GPRS
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
}
#endif
// Do nothing forevermore
while (true) {


BIN
extras/AT Command Manuals/Sequans Monarch AT Commands Manual.pdf View File


+ 1
- 1
library.json View File

@ -1,6 +1,6 @@
{
"name": "TinyGSM",
"version": "0.6.2",
"version": "0.7.4",
"description": "A small Arduino library for GPRS modules, that just works. Includes examples for Blynk, MQTT, File Download, and Web Client. Supports many GSM, LTE, and WiFi modules with AT command interfaces.",
"keywords": "GSM, AT commands, AT, SIM800, SIM900, A6, A7, M590, ESP8266, SIM7000, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900A, SIM900D, SIM908, SIM968, M95, MC60, MC60E, BG96, ublox, Quectel, SIMCOM, AI Thinker, LTE, LTE-M",
"authors":


+ 1
- 1
library.properties View File

@ -1,5 +1,5 @@
name=TinyGSM
version=0.6.2
version=0.7.4
author=Volodymyr Shymanskyy
maintainer=Volodymyr Shymanskyy
sentence=A small Arduino library for GPRS modules, that just works.


+ 17
- 0
src/TinyGsmClient.h View File

@ -48,6 +48,14 @@
typedef TinyGsmUBLOX::GsmClient TinyGsmClient;
typedef TinyGsmUBLOX::GsmClientSecure TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_SARAR4)
#define TINY_GSM_MODEM_HAS_GPRS
#define TINY_GSM_MODEM_HAS_SSL
#include <TinyGsmClientSaraR4.h>
typedef TinyGsmSaraR4 TinyGsm;
typedef TinyGsmSaraR4::GsmClient TinyGsmClient;
typedef TinyGsmSaraR4::GsmClientSecure TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_M95)
#define TINY_GSM_MODEM_HAS_GPRS
#include <TinyGsmClientM95.h>
@ -96,6 +104,15 @@
typedef TinyGsmXBee::GsmClient TinyGsmClient;
typedef TinyGsmXBee::GsmClientSecure TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_SEQUANS_MONARCH)
#define TINY_GSM_MODEM_HAS_GPRS
#define TINY_GSM_MODEM_HAS_SSL
#include <TinyGsmClientSequansMonarch.h>
typedef TinyGsmSequansMonarch TinyGsm;
typedef TinyGsmSequansMonarch::GsmClient TinyGsmClient;
typedef TinyGsmSequansMonarch::GsmClientSecure TinyGsmClientSecure;
#else
#error "Please define GSM modem model"
#endif


+ 77
- 158
src/TinyGsmClientA6.h View File

@ -12,6 +12,10 @@
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 256
#endif
#define TINY_GSM_MUX_COUNT 8
#include <TinyGsmCommon.h>
@ -36,7 +40,7 @@ enum RegStatus {
};
class TinyGsmA6 : public TinyGsmModem
class TinyGsmA6
{
public:
@ -58,40 +62,24 @@ public:
this->mux = -1;
sock_connected = false;
// at->sockets[mux] = this;
// ^^ TODO: attach the socket here at init? Or later at connect?
// Currently done inconsistently between modems
return true;
}
public:
virtual int connect(const char *host, uint16_t port) {
virtual int connect(const char *host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
uint8_t newMux = -1;
sock_connected = at->modemConnect(host, port, &newMux);
sock_connected = at->modemConnect(host, port, &newMux, timeout_s);
if (sock_connected) {
mux = newMux;
at->sockets[mux] = this;
// ^^ TODO: attach the socet after attempting connection or above at init?
// Currently done inconsistently between modems
}
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
String host; host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
TINY_GSM_CLIENT_CONNECT_OVERLOADS()
virtual void stop() {
TINY_GSM_YIELD();
@ -101,67 +89,13 @@ public:
rx.clear();
}
virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
//at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c) {
return write(&c, 1);
}
virtual size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
at->maintain();
}
return rx.size();
}
TINY_GSM_CLIENT_WRITE()
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
size_t cnt = 0;
uint32_t _startMillis = millis();
while (cnt < size && millis() - _startMillis < _timeout) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
if (!rx.size() && sock_connected) {
at->maintain();
}
}
return cnt;
}
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO()
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
TINY_GSM_CLIENT_READ_NO_MODEM_FIFO()
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
/*
* Extended API
@ -180,7 +114,7 @@ private:
public:
TinyGsmA6(Stream& stream)
: TinyGsmModem(stream), stream(stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@ -189,6 +123,10 @@ public:
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
@ -216,22 +154,11 @@ public:
return "AI-Thinker A6";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
TINY_GSM_MODEM_SET_BAUD_IPR()
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
}
TINY_GSM_MODEM_TEST_AT()
void maintain() {
waitResponse(10, NULL, NULL);
}
TINY_GSM_MODEM_MAINTAIN_LISTEN()
bool factoryDefault() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
@ -240,17 +167,7 @@ public:
return waitResponse() == 1;
}
String getModemInfo() {
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
TINY_GSM_MODEM_GET_INFO_ATI()
bool hasSSL() {
return false;
@ -290,10 +207,7 @@ public:
* SIM card functions
*/
bool simUnlock(const char *pin) {
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
String getSimCCID() {
sendAT(GF("+CCID"));
@ -306,19 +220,10 @@ public:
return res;
}
String getIMEI() {
sendAT(GF("+GSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_IMEI_GSN()
SimStatus getSimStatus(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
@ -336,16 +241,7 @@ public:
return SIM_ERROR;
}
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG)
String getOperator() {
sendAT(GF("+COPS=3,0")); // Set format
@ -365,24 +261,19 @@ public:
* Generic network functions
*/
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_CSQ()
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
TINY_GSM_MODEM_WAIT_FOR_NETWORK()
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
@ -454,6 +345,10 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
@ -587,7 +482,7 @@ public:
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery functions
* Battery & temperature functions
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
@ -597,27 +492,58 @@ public:
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
stream.readStringUntil(',');
streamSkipUntil(','); // Skip battery charge status
// Read battery charge level
int res = stream.readStringUntil('\n').toInt();
// Wait for final OK
waitResponse();
return res;
}
uint8_t getBattChargeState() {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
// Read battery charge status
int res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
chargeState = stream.readStringUntil(',').toInt();
percent = stream.readStringUntil('\n').toInt();
// Wait for final OK
waitResponse();
return true;
}
float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t* mux) {
sendAT(GF("+CIPSTART="), GF("\"TCP"), GF("\",\""), host, GF("\","), port);
bool modemConnect(const char* host, uint16_t port, uint8_t* mux, int timeout_s = 75) {
unsigned long startMillis = millis();
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
if (waitResponse(75000L, GF(GSM_NL "+CIPNUM:")) != 1) {
sendAT(GF("+CIPSTART="), GF("\"TCP"), GF("\",\""), host, GF("\","), port);
if (waitResponse(timeout_ms, GF(GSM_NL "+CIPNUM:")) != 1) {
return false;
}
int newMux = stream.readStringUntil('\n').toInt();
int rsp = waitResponse(75000L,
int rsp = waitResponse((timeout_ms- (millis() - startMillis)),
GF("CONNECT OK" GSM_NL),
GF("CONNECT FAIL" GSM_NL),
GF("ALREADY CONNECT" GSM_NL));
@ -655,16 +581,10 @@ public:
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
@ -708,8 +628,7 @@ public:
DBG("### Got: ", len, "->", sockets[mux]->rx.free());
}
while (len--) {
while (!stream.available()) { TINY_GSM_YIELD(); }
sockets[mux]->rx.put(stream.read());
TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT
}
if (len_orig > sockets[mux]->available()) { // TODO
DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
@ -724,7 +643,7 @@ public:
DBG("### Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
@ -737,12 +656,12 @@ finish:
return index;
}
uint8_t waitResponse(uint32_t timeout,
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),


+ 88
- 203
src/TinyGsmClientBG96.h View File

@ -13,6 +13,10 @@
//#define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 12
#include <TinyGsmCommon.h>
@ -37,7 +41,7 @@ enum RegStatus {
};
class TinyGsmBG96 : public TinyGsmModem
class TinyGsmBG96
{
public:
@ -62,36 +66,20 @@ public:
got_data = false;
at->sockets[mux] = this;
// ^^ TODO: attach the socket here at init? Or later at connect?
// Currently done inconsistently between modems
return true;
}
public:
virtual int connect(const char *host, uint16_t port) {
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);
// sock_connected = at->modemConnect(host, port, &mux);
// at->sockets[mux] = this;
// ^^ TODO: attach the socet after attempting connection or above at init?
// Currently done inconsistently between modems
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
String host; host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
TINY_GSM_CLIENT_CONNECT_OVERLOADS()
virtual void stop() {
TINY_GSM_YIELD();
@ -112,70 +100,13 @@ public:
at->waitResponse();
}
virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
return at->modemSend(buf, size, mux);
}
TINY_GSM_CLIENT_WRITE()
virtual size_t write(uint8_t c) {
return write(&c, 1);
}
TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK()
virtual size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size()) {
at->maintain();
}
return rx.size() + sock_available;
}
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
} else {
break;
}
}
return cnt;
}
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK()
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
/*
* Extended API
@ -203,11 +134,11 @@ private:
// {}
//
// public:
// virtual int connect(const char *host, uint16_t port) {
// virtual int connect(const char *host, uint16_t port, int timeout_s) {
// stop();
// TINY_GSM_YIELD();
// rx.clear();
// sock_connected = at->modemConnect(host, port, mux, true);
// sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
// return sock_connected;
// }
// };
@ -216,7 +147,7 @@ private:
public:
TinyGsmBG96(Stream& stream)
: TinyGsmModem(stream), stream(stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@ -225,6 +156,10 @@ public:
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
@ -243,31 +178,11 @@ public:
return "Quectel BG96";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
TINY_GSM_MODEM_SET_BAUD_IPR()
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
}
TINY_GSM_MODEM_TEST_AT()
void maintain() {
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClient* sock = sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
sock->sock_available = modemGetAvailable(mux);
}
}
while (stream.available()) {
waitResponse(10, NULL, NULL);
}
}
TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS()
bool factoryDefault() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
@ -278,17 +193,7 @@ public:
return waitResponse() == 1;
}
String getModemInfo() {
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
TINY_GSM_MODEM_GET_INFO_ATI()
bool hasSSL() {
return false; // TODO: For now
@ -337,14 +242,11 @@ public:
* SIM card functions
*/
bool simUnlock(const char *pin) {
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
String getSimCCID() {
sendAT(GF("+ICCID"));
if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) {
sendAT(GF("+QCCID"));
if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
@ -353,19 +255,10 @@ public:
return res;
}
String getIMEI() {
sendAT(GF("+GSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_IMEI_GSN()
SimStatus getSimStatus(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
@ -383,50 +276,27 @@ public:
return SIM_ERROR;
}
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG)
String getOperator() {
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_OPERATOR_COPS()
/*
* Generic network functions
*/
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_CSQ()
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
TINY_GSM_MODEM_WAIT_FOR_NETWORK()
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
@ -459,18 +329,7 @@ public:
return true;
}
bool isGprsConnected() {
sendAT(GF("+CGATT?"));
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
return false;
}
int res = stream.readStringUntil('\n').toInt();
waitResponse();
if (res != 1)
return false;
return localIP() != IPAddress(0,0,0,0);
}
TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED()
/*
* IP Address functions
@ -486,6 +345,10 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
@ -573,7 +436,7 @@ public:
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery functions
* Battery & temperature functions
*/
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
@ -582,10 +445,11 @@ public:
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip battery charge status
streamSkipUntil(','); // Skip battery charge level
// return voltage in mV
uint16_t res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
@ -595,26 +459,59 @@ public:
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
stream.readStringUntil(',');
streamSkipUntil(','); // Skip battery charge status
// Read battery charge level
int res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
uint8_t getBattChargeState() {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
// Read battery charge status
int res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
chargeState = stream.readStringUntil(',').toInt();
percent = stream.readStringUntil(',').toInt();
milliVolts = stream.readStringUntil('\n').toInt();
// Wait for final OK
waitResponse();
return true;
}
float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 20)
{
int rsp;
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
// <PDPcontextID>(1-16), <connectID>(0-11),"TCP/UDP/TCP LISTENER/UDP SERVICE",
// "<IP_address>/<domain_name>",<remote_port>,<local_port>,<access_mode>(0-2 0=buffer)
sendAT(GF("+QIOPEN=1,"), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(",0,0"));
rsp = waitResponse();
if (waitResponse(20000L, GF(GSM_NL "+QIOPEN:")) != 1) {
if (waitResponse(timeout_ms, GF(GSM_NL "+QIOPEN:")) != 1) {
return false;
}
@ -638,7 +535,6 @@ protected:
return 0;
}
// TODO: Wait for ACK? AT+QISEND=id,0
maintain(); // look for a very quick response from the remote
return len;
}
@ -651,13 +547,10 @@ protected:
sockets[mux]->sock_available = len;
for (size_t i=0; i<len; i++) {
while (!stream.available()) { TINY_GSM_YIELD(); }
char c = stream.read();
sockets[mux]->rx.put(c);
TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT
}
waitResponse();
DBG("### READ:", len, "from", mux);
maintain(); // Listen for a close or other URC
return len;
}
@ -668,13 +561,12 @@ protected:
streamSkipUntil(','); // Skip total received
streamSkipUntil(','); // Skip have read
result = stream.readStringUntil('\n').toInt();
DBG("### DATA AVAILABLE:", result, "on", mux);
if (result) DBG("### DATA AVAILABLE:", result, "on", mux);
waitResponse();
}
if (!result) {
sockets[mux]->sock_connected = modemGetConnected(mux);
}
maintain(); // Listen for a close or other URC
return result;
}
@ -693,7 +585,6 @@ protected:
int res = stream.readStringUntil(',').toInt(); // socket state
waitResponse();
maintain(); // Listen for a close or other URC
// 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
return 2 == res;
@ -705,16 +596,10 @@ public:
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
@ -770,7 +655,7 @@ public:
data = "";
}
}
} while (millis() - startMillis < timeout);
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
@ -783,12 +668,12 @@ finish:
return index;
}
uint8_t waitResponse(uint32_t timeout,
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),


+ 52
- 114
src/TinyGsmClientESP8266.h View File

@ -12,6 +12,10 @@
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 512
#endif
#define TINY_GSM_MUX_COUNT 5
#include <TinyGsmCommon.h>
@ -36,7 +40,7 @@ enum RegStatus {
class TinyGsmESP8266 : public TinyGsmModem
class TinyGsmESP8266
{
public:
@ -59,36 +63,20 @@ public:
sock_connected = false;
at->sockets[mux] = this;
// ^^ TODO: attach the socket here at init? Or later at connect?
// Currently done inconsistently between modems
return true;
}
public:
virtual int connect(const char *host, uint16_t port) {
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);
// sock_connected = at->modemConnect(host, port, &mux);
// at->sockets[mux] = this;
// ^^ TODO: attach the socet after attempting connection or above at init?
// Currently done inconsistently between modems
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
String host; host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
TINY_GSM_CLIENT_CONNECT_OVERLOADS()
virtual void stop() {
TINY_GSM_YIELD();
@ -98,67 +86,13 @@ public:
rx.clear();
}
virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
//at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c) {
return write(&c, 1);
}
virtual size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
at->maintain();
}
return rx.size();
}
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
size_t cnt = 0;
uint32_t _startMillis = millis();
while (cnt < size && millis() - _startMillis < _timeout) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
if (!rx.size() && sock_connected) {
at->maintain();
}
}
return cnt;
}
TINY_GSM_CLIENT_WRITE()
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO()
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
TINY_GSM_CLIENT_READ_NO_MODEM_FIFO()
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
/*
* Extended API
@ -184,15 +118,11 @@ public:
{}
public:
virtual int connect(const char *host, uint16_t port) {
virtual int connect(const char *host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, true);
// sock_connected = at->modemConnect(host, port, &mux);
// at->sockets[mux] = this;
// ^^ TODO: attach the socet after attempting connection or above at init?
// Currently done inconsistently between modems
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
return sock_connected;
}
};
@ -201,7 +131,7 @@ public:
public:
TinyGsmESP8266(Stream& stream)
: TinyGsmModem(stream), stream(stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@ -210,6 +140,10 @@ public:
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
@ -239,18 +173,9 @@ public:
sendAT(GF("+UART_CUR="), baud, "8,1,0,0");
}
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
}
TINY_GSM_MODEM_TEST_AT()
void maintain() {
waitResponse(10, NULL, NULL);
}
TINY_GSM_MODEM_MAINTAIN_LISTEN()
bool factoryDefault() {
sendAT(GF("+RESTORE"));
@ -345,8 +270,8 @@ public:
return (s == REG_OK_IP || s == REG_OK_TCP);
}
bool waitForNetwork(unsigned long timeout = 60000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
bool waitForNetwork(unsigned long timeout_ms = 60000L) {
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
sendAT(GF("+CIPSTATUS"));
int res1 = waitResponse(3000, GF("busy p..."), GF("STATUS:"));
if (res1 == 2) {
@ -364,6 +289,7 @@ public:
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd) {
sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\""));
if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) {
@ -395,20 +321,39 @@ public:
return res2;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Battery & temperature functions
*/
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE;
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE;
float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75)
{
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
if (ssl) {
sendAT(GF("+CIPSSLSIZE=4096"));
waitResponse();
}
sendAT(GF("+CIPSTART="), mux, ',', ssl ? GF("\"SSL") : GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(","), TINY_GSM_TCP_KEEP_ALIVE);
sendAT(GF("+CIPSTART="), mux, ',', ssl ? GF("\"SSL") : GF("\"TCP"),
GF("\",\""), host, GF("\","), port, GF(","), TINY_GSM_TCP_KEEP_ALIVE);
// TODO: Check mux
int rsp = waitResponse(75000L,
int rsp = waitResponse(timeout_ms,
GFP(GSM_OK),
GFP(GSM_ERROR),
GF("ALREADY CONNECT"));
@ -440,16 +385,10 @@ public:
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
@ -493,8 +432,7 @@ public:
DBG("### Got: ", len, "->", sockets[mux]->rx.free());
}
while (len--) {
while (!stream.available()) { TINY_GSM_YIELD(); }
sockets[mux]->rx.put(stream.read());
TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT
}
if (len_orig > sockets[mux]->available()) { // TODO
DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
@ -511,7 +449,7 @@ public:
DBG("### Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
@ -524,12 +462,12 @@ finish:
return index;
}
uint8_t waitResponse(uint32_t timeout,
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),


+ 53
- 181
src/TinyGsmClientM590.h View File

@ -12,6 +12,10 @@
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 256
#endif
#define TINY_GSM_MUX_COUNT 2
#include <TinyGsmCommon.h>
@ -36,7 +40,7 @@ enum RegStatus {
};
class TinyGsmM590 : public TinyGsmModem
class TinyGsmM590
{
public:
@ -59,36 +63,21 @@ public:
sock_connected = false;
at->sockets[mux] = this;
// ^^ TODO: attach the socket here at init? Or later at connect?
// Currently done inconsistently between modems
return true;
}
public:
virtual int connect(const char *host, uint16_t port) {
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);
// sock_connected = at->modemConnect(host, port, &mux);
// at->sockets[mux] = this;
// ^^ TODO: attach the socet after attempting connection or above at init?
// Currently done inconsistently between modems
sock_connected = at->modemConnect(host, port, mux, timeout_s);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
String host; host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
TINY_GSM_CLIENT_CONNECT_OVERLOADS()
virtual void stop() {
TINY_GSM_YIELD();
@ -98,67 +87,13 @@ public:
rx.clear();
}
virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
//at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c) {
return write(&c, 1);
}
virtual size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
at->maintain();
}
return rx.size();
}
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
size_t cnt = 0;
uint32_t _startMillis = millis();
while (cnt < size && millis() - _startMillis < _timeout) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
if (!rx.size() && sock_connected) {
at->maintain();
}
}
return cnt;
}
TINY_GSM_CLIENT_WRITE()
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO()
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
TINY_GSM_CLIENT_READ_NO_MODEM_FIFO()
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
/*
* Extended API
@ -167,7 +102,7 @@ public:
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
TinyGsmM590* at;
TinyGsmM590* at;
uint8_t mux;
bool sock_connected;
RxFifo rx;
@ -177,7 +112,7 @@ private:
public:
TinyGsmM590(Stream& stream)
: TinyGsmModem(stream), stream(stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@ -186,6 +121,10 @@ public:
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
@ -208,24 +147,11 @@ public:
return "Neoway M590";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
TINY_GSM_MODEM_SET_BAUD_IPR()
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
}
TINY_GSM_MODEM_TEST_AT()
void maintain() {
//while (stream.available()) {
waitResponse(10, NULL, NULL);
//}
}
TINY_GSM_MODEM_MAINTAIN_LISTEN()
bool factoryDefault() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
@ -240,17 +166,7 @@ public:
return waitResponse() == 1;
}
String getModemInfo() {
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
TINY_GSM_MODEM_GET_INFO_ATI()
bool hasSSL() {
return false;
@ -297,35 +213,14 @@ public:
* SIM card functions
*/
bool simUnlock(const char *pin) {
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
String getSimCCID() {
sendAT(GF("+CCID"));
if (waitResponse(GF(GSM_NL "+CCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_SIMCCID_CCID()
String getIMEI() {
sendAT(GF("+GSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_IMEI_GSN()
SimStatus getSimStatus(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
@ -343,50 +238,27 @@ public:
return SIM_ERROR;
}
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG)
String getOperator() {
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_OPERATOR_COPS()
/*
* Generic network functions
*/
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_CSQ()
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
TINY_GSM_MODEM_WAIT_FOR_NETWORK()
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
@ -404,8 +276,8 @@ public:
sendAT(GF("+XIIC=1"));
waitResponse();
const unsigned long timeout = 60000L;
for (unsigned long start = millis(); millis() - start < timeout; ) {
const unsigned long timeout_ms = 60000L;
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
if (isGprsConnected()) {
//goto set_dns; // TODO
return true;
@ -456,6 +328,10 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
@ -524,12 +400,14 @@ public:
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery functions
* Battery & temperature functions
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE;
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE;
float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Client related functions
@ -537,12 +415,13 @@ public:
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux) {
bool modemConnect(const char* host, uint16_t port, uint8_t mux, int timeout_s = 75) {
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
for (int i=0; i<3; i++) { // TODO: no need for loop?
String ip = dnsIpQuery(host);
sendAT(GF("+TCPSETUP="), mux, GF(","), ip, GF(","), port);
int rsp = waitResponse(75000L,
int rsp = waitResponse(timeout_ms,
GF(",OK" GSM_NL),
GF(",FAIL" GSM_NL),
GF("+TCPSETUP:Error" GSM_NL));
@ -596,16 +475,10 @@ public:
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
@ -649,8 +522,7 @@ public:
DBG("### Got: ", len, "->", sockets[mux]->rx.free());
}
while (len--) {
while (!stream.available()) { TINY_GSM_YIELD(); }
sockets[mux]->rx.put(stream.read());
TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT
}
if (len_orig > sockets[mux]->available()) { // TODO
DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
@ -666,7 +538,7 @@ public:
DBG("### Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
@ -679,12 +551,12 @@ finish:
return index;
}
uint8_t waitResponse(uint32_t timeout,
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),


+ 100
- 203
src/TinyGsmClientM95.h View File

@ -13,6 +13,10 @@
//#define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 6
#include <TinyGsmCommon.h>
@ -37,7 +41,7 @@ enum RegStatus {
};
class TinyGsmM95 : public TinyGsmModem
class TinyGsmM95
{
public:
@ -62,36 +66,20 @@ public:
got_data = false;
at->sockets[mux] = this;
// ^^ TODO: attach the socket here at init? Or later at connect?
// Currently done inconsistently between modems
return true;
}
public:
virtual int connect(const char *host, uint16_t port) {
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);
// sock_connected = at->modemConnect(host, port, &mux);
// at->sockets[mux] = this;
// ^^ TODO: attach the socet after attempting connection or above at init?
// Currently done inconsistently between modems
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
String host; host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
TINY_GSM_CLIENT_CONNECT_OVERLOADS()
virtual void stop() {
TINY_GSM_YIELD();
@ -112,70 +100,13 @@ public:
at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR"));
}
virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c) {
return write(&c, 1);
}
virtual size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size()) {
at->maintain();
}
return rx.size() + sock_available;
}
TINY_GSM_CLIENT_WRITE()
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
} else {
break;
}
}
return cnt;
}
TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK()
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK()
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
/*
* Extended API
@ -203,11 +134,11 @@ private:
// {}
//
// public:
// virtual int connect(const char *host, uint16_t port) {
// virtual int connect(const char *host, uint16_t port, int timeout_s) {
// stop();
// TINY_GSM_YIELD();
// rx.clear();
// sock_connected = at->modemConnect(host, port, mux, true);
// sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
// return sock_connected;
// }
// };
@ -216,7 +147,7 @@ private:
public:
TinyGsmM95(Stream& stream)
: TinyGsmModem(stream), stream(stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@ -225,6 +156,10 @@ public:
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
@ -247,31 +182,11 @@ public:
return "Quectel M95";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
TINY_GSM_MODEM_SET_BAUD_IPR()
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
}
TINY_GSM_MODEM_TEST_AT()
void maintain() {
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClient* sock = sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
sock->sock_available = modemGetAvailable(mux);
}
}
while (stream.available()) {
waitResponse(10, NULL, NULL);
}
}
TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS()
bool factoryDefault() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
@ -282,17 +197,7 @@ public:
return waitResponse() == 1;
}
String getModemInfo() {
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
TINY_GSM_MODEM_GET_INFO_ATI()
bool hasSSL() {
return false; // TODO: For now
@ -345,14 +250,11 @@ public:
* SIM card functions
*/
bool simUnlock(const char *pin) {
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
String getSimCCID() {
sendAT(GF("+ICCID"));
if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) {
sendAT(GF("+QCCID"));
if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
@ -361,19 +263,10 @@ public:
return res;
}
String getIMEI() {
sendAT(GF("+GSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_IMEI_GSN()
SimStatus getSimStatus(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
@ -391,41 +284,15 @@ public:
return SIM_ERROR;
}
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG)
String getOperator() {
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_OPERATOR_COPS()
/*
* Generic network functions
*/
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_CSQ()
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
@ -441,9 +308,12 @@ public:
waitResponse();
}
TINY_GSM_MODEM_WAIT_FOR_NETWORK()
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
@ -497,18 +367,7 @@ public:
return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1;
}
bool isGprsConnected() {
sendAT(GF("+CGATT?"));
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
return false;
}
int res = stream.readStringUntil('\n').toInt();
waitResponse();
if (res != 1)
return false;
return localIP() != IPAddress(0,0,0,0);
}
TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED()
/*
* IP Address functions
@ -522,6 +381,10 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Messaging functions
*/
@ -621,7 +484,7 @@ public:
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery functions
* Battery & temperature functions
*/
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
@ -630,10 +493,11 @@ public:
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip battery charge status
streamSkipUntil(','); // Skip battery charge level
// return voltage in mV
uint16_t res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
@ -643,21 +507,66 @@ public:
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
stream.readStringUntil(',');
streamSkipUntil(','); // Skip battery charge status
// Read battery charge level
int res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
uint8_t getBattChargeState() {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
// Read battery charge status
int res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
chargeState = stream.readStringUntil(',').toInt();
percent = stream.readStringUntil(',').toInt();
milliVolts = stream.readStringUntil('\n').toInt();
// Wait for final OK
waitResponse();
return true;
}
float getTemperature() {
sendAT(GF("+QTEMP"));
if (waitResponse(GF(GSM_NL "+QTEMP:")) != 1) {
return (float)-9999;
}
streamSkipUntil(','); // Skip mode
// Read charge of thermistor
// milliVolts = stream.readStringUntil(',').toInt();
streamSkipUntil(','); // Skip thermistor charge
float temp = stream.readStringUntil('\n').toFloat();
// Wait for final OK
waitResponse();
return temp;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75)
{
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
sendAT(GF("+QIOPEN="), mux, GF("\"TCP"), GF("\",\""), host, GF("\","), port);
int rsp = waitResponse(75000L,
int rsp = waitResponse(timeout_ms,
GF("CONNECT OK" GSM_NL),
GF("CONNECT FAIL" GSM_NL),
GF("ALREADY CONNECT" GSM_NL));
@ -690,7 +599,6 @@ protected:
}
}
waitResponse(5000L);
maintain(); // look for a very quick response from the remote
// streamSkipUntil(','); // Skip mux
// return stream.readStringUntil('\n').toInt();
@ -706,13 +614,10 @@ protected:
sockets[mux]->sock_available = len;
for (size_t i=0; i<len; i++) {
while (!stream.available()) { TINY_GSM_YIELD(); }
char c = stream.read();
sockets[mux]->rx.put(c);
TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT
}
waitResponse();
DBG("### READ:", len, "from", mux);
maintain(); // Listen for a close or other URC
return len;
}
@ -723,13 +628,12 @@ protected:
streamSkipUntil(','); // Skip total received
streamSkipUntil(','); // Skip have read
result = stream.readStringUntil('\n').toInt();
DBG("### DATA AVAILABLE:", result, "on", mux);
if (result) DBG("### DATA AVAILABLE:", result, "on", mux);
waitResponse();
}
if (!result) {
sockets[mux]->sock_connected = modemGetConnected(mux);
}
maintain(); // Listen for a close or other URC
return result;
}
@ -748,7 +652,6 @@ protected:
int res = stream.readStringUntil(',').toInt(); // socket state
waitResponse();
maintain(); // Listen for a close or other URC
// 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
return 2 == res;
@ -760,16 +663,10 @@ public:
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
@ -822,7 +719,7 @@ public:
DBG("### Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
@ -835,12 +732,12 @@ finish:
return index;
}
uint8_t waitResponse(uint32_t timeout,
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),


+ 87
- 212
src/TinyGsmClientMC60.h View File

@ -16,6 +16,10 @@
//#define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 6
#include <TinyGsmCommon.h>
@ -41,7 +45,7 @@ enum RegStatus {
};
class TinyGsmMC60 : public TinyGsmModem
class TinyGsmMC60
{
public:
@ -66,36 +70,20 @@ public:
got_data = false;
at->sockets[mux] = this;
// ^^ TODO: attach the socket here at init? Or later at connect?
// Currently done inconsistently between modems
return true;
}
public:
virtual int connect(const char *host, uint16_t port) {
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);
// sock_connected = at->modemConnect(host, port, &mux);
// at->sockets[mux] = this;
// ^^ TODO: attach the socet after attempting connection or above at init?
// Currently done inconsistently between modems
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
String host; host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
TINY_GSM_CLIENT_CONNECT_OVERLOADS()
virtual void stop() {
TINY_GSM_YIELD();
@ -116,70 +104,13 @@ public:
at->waitResponse(60000L, GF("CLOSED"), GF("CLOSE OK"), GF("ERROR"));
}
virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
return at->modemSend(buf, size, mux);
}
TINY_GSM_CLIENT_WRITE()
virtual size_t write(uint8_t c) {
return write(&c, 1);
}
TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK()
virtual size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK()
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size()) {
at->maintain();
}
return rx.size() + sock_available;
}
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
} else {
break;
}
}
return cnt;
}
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
/*
* Extended API
@ -207,11 +138,11 @@ private:
// {}
//
// public:
// virtual int connect(const char *host, uint16_t port) {
// virtual int connect(const char *host, uint16_t port, int timeout_s) {
// stop();
// TINY_GSM_YIELD();
// rx.clear();
// sock_connected = at->modemConnect(host, port, mux, true);
// sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
// return sock_connected;
// }
// };
@ -220,7 +151,7 @@ private:
public:
TinyGsmMC60(Stream& stream)
: TinyGsmModem(stream), stream(stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@ -229,6 +160,10 @@ public:
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
@ -254,31 +189,11 @@ public:
return "Quectel MC60";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
TINY_GSM_MODEM_SET_BAUD_IPR()
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
}
TINY_GSM_MODEM_TEST_AT()
void maintain() {
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClient* sock = sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
sock->sock_available = modemGetAvailable(mux);
}
}
while (stream.available()) {
waitResponse(10, NULL, NULL);
}
}
TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS()
bool factoryDefault() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
@ -289,17 +204,7 @@ public:
return waitResponse() == 1;
}
String getModemInfo() {
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
TINY_GSM_MODEM_GET_INFO_ATI()
/*
* under development
@ -367,35 +272,14 @@ public:
* SIM card functions
*/
bool simUnlock(const char *pin) {
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
String getSimCCID() {
sendAT(GF("+ICCID"));
if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_SIMCCID_CCID()
String getIMEI() {
sendAT(GF("+GSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_IMEI_GSN()
SimStatus getSimStatus(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
@ -415,50 +299,27 @@ public:
return SIM_ERROR;
}
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG)
String getOperator() {
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_OPERATOR_COPS()
/*
* Generic network functions
*/
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_CSQ()
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
TINY_GSM_MODEM_WAIT_FOR_NETWORK()
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
@ -513,7 +374,7 @@ public:
}
// Check that we have a local IP address
if (localIP() != 0) {
if (localIP() != IPAddress(0,0,0,0)) {
return true;
}
@ -525,18 +386,7 @@ public:
return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1;
}
bool isGprsConnected() {
sendAT(GF("+CGATT?"));
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
return false;
}
int res = stream.readStringUntil('\n').toInt();
waitResponse();
if (res != 1)
return false;
return localIP() != IPAddress(0,0,0,0);
}
TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED()
/*
* IP Address functions
@ -550,6 +400,10 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Messaging functions
*/
@ -649,7 +503,7 @@ public:
}
/*
* Battery functions
* Battery & temperature functions
*/
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
@ -658,10 +512,11 @@ public:
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip battery charge status
streamSkipUntil(','); // Skip battery charge level
// return voltage in mV
uint16_t res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
@ -671,21 +526,53 @@ public:
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
stream.readStringUntil(',');
streamSkipUntil(','); // Skip battery charge status
// Read battery charge level
int res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
uint8_t getBattChargeState() {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
// Read battery charge status
int res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
chargeState = stream.readStringUntil(',').toInt();
percent = stream.readStringUntil(',').toInt();
milliVolts = stream.readStringUntil('\n').toInt();
// Wait for final OK
waitResponse();
return true;
}
float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75)
{
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
sendAT(GF("+QIOPEN="), mux, GF("\"TCP"), GF("\",\""), host, GF("\","), port);
int rsp = waitResponse(75000L,
int rsp = waitResponse(timeout_ms,
GF("CONNECT OK" GSM_NL),
GF("CONNECT FAIL" GSM_NL),
GF("ALREADY CONNECT" GSM_NL));
@ -722,7 +609,6 @@ protected:
// streamSkipUntil(','); // Skip mux
// return stream.readStringUntil('\n').toInt();
maintain(); // look for a very quick response from the remote
return len; // TODO
}
@ -735,13 +621,10 @@ protected:
sockets[mux]->sock_available = len;
for (size_t i=0; i<len; i++) {
while (!stream.available()) { TINY_GSM_YIELD(); }
char c = stream.read();
sockets[mux]->rx.put(c);
TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT
}
waitResponse();
DBG("### READ:", len, "from", mux);
maintain(); // Listen for a close or other URC
return len;
}
@ -752,13 +635,12 @@ protected:
streamSkipUntil(','); // Skip total received
streamSkipUntil(','); // Skip have read
result = stream.readStringUntil('\n').toInt();
DBG("### DATA AVAILABLE:", result, "on", mux);
if (result) DBG("### DATA AVAILABLE:", result, "on", mux);
waitResponse();
}
if (!result) {
sockets[mux]->sock_connected = modemGetConnected(mux);
}
maintain(); // Listen for a close or other URC
return result;
}
@ -777,7 +659,6 @@ protected:
int res = stream.readStringUntil(',').toInt(); // socket state
waitResponse();
maintain(); // Listen for a close or other URC
// 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
return 2 == res;
@ -789,16 +670,10 @@ public:
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL)
{
@ -855,7 +730,7 @@ public:
DBG("### Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
@ -868,12 +743,12 @@ finish:
return index;
}
uint8_t waitResponse(uint32_t timeout,
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL, GsmConstStr r6=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5, r6);
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5, r6);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),


+ 86
- 222
src/TinyGsmClientSIM7000.h View File

@ -9,7 +9,7 @@
#ifndef TinyGsmClientSIM7000_h
#define TinyGsmClientSIM7000_h
#define TINY_GSM_DEBUG Serial
// #define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX
#if !defined(TINY_GSM_RX_BUFFER)
@ -45,7 +45,7 @@ enum TinyGSMDateTimeFormat {
DATE_DATE = 2
};
class TinyGsmSim7000 : public TinyGsmModem
class TinyGsmSim7000
{
public:
@ -71,36 +71,20 @@ public:
got_data = false;
at->sockets[mux] = this;
// ^^ TODO: attach the socket here at init? Or later at connect?
// Currently done inconsistently between modems
return true;
}
public:
virtual int connect(const char *host, uint16_t port) {
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);
// sock_connected = at->modemConnect(host, port, &mux);
// at->sockets[mux] = this;
// ^^ TODO: attach the socet after attempting connection or above at init?
// Currently done inconsistently between modems
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
String host; host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
TINY_GSM_CLIENT_CONNECT_OVERLOADS()
virtual void stop() {
TINY_GSM_YIELD();
@ -121,86 +105,13 @@ public:
at->waitResponse();
}
virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c) {
return write(&c, 1);
}
virtual size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size()) {
// TODO: Is this needed for SIM7000?
// Workaround: sometimes SIM7000 forgets to notify about data arrival.
// TODO: Currently we ping the module periodically,
// but maybe there's a better indicator that we need to poll
if (millis() - prev_check > 250) {
got_data = true;
prev_check = millis();
}
at->maintain();
}
return rx.size() + sock_available;
}
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Is this needed for SIM7000?
// Workaround: sometimes SIM7000 forgets to notify about data arrival.
// TODO: Currently we ping the module periodically,
// but maybe there's a better indicator that we need to poll
if (millis() - prev_check > 250) {
got_data = true;
prev_check = millis();
}
// TODO: Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
} else {
break;
}
}
return cnt;
}
TINY_GSM_CLIENT_WRITE()
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK()
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK()
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
/*
* Extended API
@ -229,14 +140,11 @@ public:
{}
public:
virtual int connect(const char *host, uint16_t port) {
virtual int connect(const char *host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, true);
// sock_connected = at->modemConnect(host, port, &mux, true);
// at->sockets[mux] = this;
// TODO: When is the socket attached?
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
return sock_connected;
}
};
@ -245,7 +153,7 @@ public:
public:
TinyGsmSim7000(Stream& stream)
: TinyGsmModem(stream), stream(stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@ -254,6 +162,10 @@ public:
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
@ -272,48 +184,17 @@ public:
return "SIMCom SIM7000";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
TINY_GSM_MODEM_SET_BAUD_IPR()
bool testAT(unsigned long timeout = 10000L) {
//streamWrite(GF("AAAAA" GSM_NL)); // TODO: extra A's to help detect the baud rate
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
}
TINY_GSM_MODEM_TEST_AT()
void maintain() {
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClient* sock = sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
sock->sock_available = modemGetAvailable(mux);
}
}
while (stream.available()) {
waitResponse(10, NULL, NULL);
}
}
TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS()
bool factoryDefault() { // these commands aren't supported
return false;
}
String getModemInfo() {
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
TINY_GSM_MODEM_GET_INFO_ATI()
bool hasSSL() {
return false; // TODO: Module supports SSL, but not yet implemented
@ -382,35 +263,14 @@ public:
* SIM card functions
*/
bool simUnlock(const char *pin) {
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
String getSimCCID() {
sendAT(GF("+ICCID"));
if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_SIMCCID_CCID()
String getIMEI() {
sendAT(GF("+GSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_IMEI_GSN()
SimStatus getSimStatus(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
@ -428,42 +288,15 @@ public:
return SIM_ERROR;
}
RegStatus getRegistrationStatus() {
sendAT(GF("+CGREG?"));
// TODO: Shouldn't this be CEREG for the EPS status of this 4G module?
if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG)
String getOperator() {
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_OPERATOR_COPS()
/*
* Generic network functions
*/
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_CSQ()
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
@ -480,6 +313,8 @@ public:
return res;
}
TINY_GSM_MODEM_WAIT_FOR_NETWORK()
String setNetworkMode(uint8_t mode) {
sendAT(GF("+CNMP="), mode);
if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) {
@ -514,6 +349,7 @@ public:
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
@ -641,6 +477,10 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
@ -814,6 +654,7 @@ public:
/*
* Time functions
*/
String getGSMDateTime(TinyGSMDateTimeFormat format) {
sendAT(GF("+CCLK?"));
if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) {
@ -887,15 +728,16 @@ public:
/*
* Battery functions
*/
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
uint16_t getBattVoltage() {
sendAT(GF("+CBC"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip battery charge status
streamSkipUntil(','); // Skip battery charge level
// return voltage in mV
uint16_t res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
@ -906,22 +748,53 @@ public:
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
stream.readStringUntil(',');
streamSkipUntil(','); // Skip battery charge status
// Read battery charge level
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
uint8_t getBattChargeState() {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
// Read battery charge status
int res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
chargeState = stream.readStringUntil(',').toInt();
percent = stream.readStringUntil(',').toInt();
milliVolts = stream.readStringUntil('\n').toInt();
// Wait for final OK
waitResponse();
return true;
}
float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75)
{
int rsp;
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port);
rsp = waitResponse(75000L,
rsp = waitResponse(timeout_ms,
GF("CONNECT OK" GSM_NL),
GF("CONNECT FAIL" GSM_NL),
GF("ALREADY CONNECT" GSM_NL),
@ -942,7 +815,6 @@ protected:
return 0;
}
streamSkipUntil(','); // Skip mux
maintain(); // look for a very quick response from the remote
return stream.readStringUntil('\n').toInt();
}
@ -963,30 +835,30 @@ protected:
size_t len_requested = stream.readStringUntil(',').toInt();
// ^^ Requested number of data bytes (1-1460 bytes)to be read
size_t len_confirmed = stream.readStringUntil('\n').toInt();
if (len_confirmed > len_requested) {
if (len_confirmed < len_requested) {
DBG(len_requested - len_confirmed, "fewer bytes confirmed than requested!");
}
sockets[mux]->sock_available = len_confirmed;
// ^^ Confirmed number of data bytes to be read, which may be less than requested.
// 0 indicates that no data can be read.
for (size_t i=0; i<len_confirmed; i++) {
for (size_t i=0; i<TinyGsmMin(len_confirmed, len_requested) ; i++) {
uint32_t startMillis = millis();
#ifdef TINY_GSM_USE_HEX
while (stream.available() < 2) { TINY_GSM_YIELD(); }
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()) { TINY_GSM_YIELD(); }
while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); }
char c = stream.read();
#endif
sockets[mux]->rx.put(c);
}
waitResponse();
DBG("### READ:", len_confirmed, "from", mux);
maintain(); // Listen for a close or other URC
return len_confirmed;
DBG("### READ:", TinyGsmMin(len_confirmed, len_requested), "from", mux);
return TinyGsmMin(len_confirmed, len_requested);
}
size_t modemGetAvailable(uint8_t mux) {
@ -1001,7 +873,6 @@ protected:
if (!result) {
sockets[mux]->sock_connected = modemGetConnected(mux);
}
maintain(); // Listen for a close or other URC
return result;
}
@ -1009,7 +880,6 @@ protected:
sendAT(GF("+CIPSTATUS="), mux);
int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\""));
waitResponse();
maintain(); // Listen for a close or other URC
return 1 == res;
}
@ -1019,16 +889,10 @@ public:
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
@ -1094,7 +958,7 @@ public:
DBG("### Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
@ -1107,12 +971,12 @@ finish:
return index;
}
uint8_t waitResponse(uint32_t timeout,
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),


+ 91
- 219
src/TinyGsmClientSIM800.h View File

@ -13,6 +13,10 @@
//#define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 5
#include <TinyGsmCommon.h>
@ -42,7 +46,7 @@ enum TinyGSMDateTimeFormat {
DATE_DATE = 2
};
class TinyGsmSim800 : public TinyGsmModem
class TinyGsmSim800
{
public:
@ -68,36 +72,20 @@ public:
got_data = false;
at->sockets[mux] = this;
// ^^ TODO: attach the socket here at init? Or later at connect?
// Currently done inconsistently between modems
return true;
}
public:
virtual int connect(const char *host, uint16_t port) {
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);
// sock_connected = at->modemConnect(host, port, &mux);
// at->sockets[mux] = this;
// ^^ TODO: attach the socet after attempting connection or above at init?
// Currently done inconsistently between modems
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
String host; host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
TINY_GSM_CLIENT_CONNECT_OVERLOADS()
virtual void stop() {
TINY_GSM_YIELD();
@ -118,84 +106,13 @@ public:
at->waitResponse();
}
virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c) {
return write(&c, 1);
}
virtual size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size()) {
// Workaround: sometimes SIM800 forgets to notify about data arrival.
// TODO: Currently we ping the module periodically,
// but maybe there's a better indicator that we need to poll
if (millis() - prev_check > 250) {
got_data = true;
prev_check = millis();
}
at->maintain();
}
return rx.size() + sock_available;
}
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// Workaround: sometimes SIM800 forgets to notify about data arrival.
// TODO: Currently we ping the module periodically,
// but maybe there's a better indicator that we need to poll
if (millis() - prev_check > 250) {
got_data = true;
prev_check = millis();
}
// TODO: Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
} else {
break;
}
}
return cnt;
}
TINY_GSM_CLIENT_WRITE()
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK()
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK()
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
/*
* Extended API
@ -224,14 +141,11 @@ public:
{}
public:
virtual int connect(const char *host, uint16_t port) {
virtual int connect(const char *host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, true);
// sock_connected = at->modemConnect(host, port, &mux, true);
// at->sockets[mux] = this;
// TODO: When is the socket attached?
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
return sock_connected;
}
};
@ -240,7 +154,7 @@ public:
public:
TinyGsmSim800(Stream& stream)
: TinyGsmModem(stream), stream(stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
@ -249,6 +163,10 @@ public:
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
@ -278,32 +196,11 @@ public:
return "SIMCom SIM800";
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
}
TINY_GSM_MODEM_SET_BAUD_IPR()
bool testAT(unsigned long timeout = 10000L) {
//streamWrite(GF("AAAAA" GSM_NL)); // TODO: extra A's to help detect the baud rate
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
}
TINY_GSM_MODEM_TEST_AT()
void maintain() {
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClient* sock = sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
sock->sock_available = modemGetAvailable(mux);
}
}
while (stream.available()) {
waitResponse(10, NULL, NULL);
}
}
TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS()
bool factoryDefault() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
@ -320,17 +217,7 @@ public:
return waitResponse() == 1;
}
String getModemInfo() {
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
TINY_GSM_MODEM_GET_INFO_ATI()
bool hasSSL() {
#if defined(TINY_GSM_MODEM_SIM900)
@ -408,35 +295,14 @@ public:
* SIM card functions
*/
bool simUnlock(const char *pin) {
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
String getSimCCID() {
sendAT(GF("+ICCID"));
if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_SIMCCID_CCID()
String getIMEI() {
sendAT(GF("+GSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_IMEI_GSN()
SimStatus getSimStatus(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
@ -454,50 +320,27 @@ public:
return SIM_ERROR;
}
RegStatus getRegistrationStatus() {
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG)
String getOperator() {
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_OPERATOR_COPS()
/*
* Generic network functions
*/
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_CSQ()
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
TINY_GSM_MODEM_WAIT_FOR_NETWORK()
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
@ -616,6 +459,7 @@ public:
return true;
}
/*
* IP Address functions
*/
@ -632,6 +476,9 @@ public:
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
@ -775,6 +622,7 @@ public:
/*
* Time functions
*/
String getGSMDateTime(TinyGSMDateTimeFormat format) {
sendAT(GF("+CCLK?"));
if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) {
@ -799,7 +647,7 @@ public:
}
/*
* Battery functions
* Battery & temperature functions
*/
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
@ -808,10 +656,11 @@ public:
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip
streamSkipUntil(','); // Skip battery charge status
streamSkipUntil(','); // Skip battery charge level
// return voltage in mV
uint16_t res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
@ -821,20 +670,52 @@ public:
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
stream.readStringUntil(',');
streamSkipUntil(','); // Skip battery charge status
// Read battery charge level
int res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
uint8_t getBattChargeState() {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
// Read battery charge status
int res = stream.readStringUntil(',').toInt();
// Wait for final OK
waitResponse();
return res;
}
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
chargeState = stream.readStringUntil(',').toInt();
percent = stream.readStringUntil(',').toInt();
milliVolts = stream.readStringUntil('\n').toInt();
// Wait for final OK
waitResponse();
return true;
}
float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75)
{
int rsp;
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
#if !defined(TINY_GSM_MODEM_SIM900)
sendAT(GF("+CIPSSL="), ssl);
rsp = waitResponse();
@ -843,7 +724,7 @@ protected:
}
#endif
sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port);
rsp = waitResponse(75000L,
rsp = waitResponse(timeout_ms,
GF("CONNECT OK" GSM_NL),
GF("CONNECT FAIL" GSM_NL),
GF("ALREADY CONNECT" GSM_NL),
@ -864,7 +745,6 @@ protected:
return 0;
}
streamSkipUntil(','); // Skip mux
maintain(); // look for a very quick response from the remote
return stream.readStringUntil('\n').toInt();
}
@ -885,30 +765,30 @@ protected:
size_t len_requested = stream.readStringUntil(',').toInt();
// ^^ Requested number of data bytes (1-1460 bytes)to be read
size_t len_confirmed = stream.readStringUntil('\n').toInt();
if (len_confirmed > len_requested) {
if (len_confirmed < len_requested) {
DBG(len_requested - len_confirmed, "fewer bytes confirmed than requested!");
}
sockets[mux]->sock_available = len_confirmed;
// ^^ Confirmed number of data bytes to be read, which may be less than requested.
// 0 indicates that no data can be read.
for (size_t i=0; i<len_confirmed; i++) {
for (size_t i=0; i<TinyGsmMin(len_confirmed, len_requested) ; i++) {
uint32_t startMillis = millis();
#ifdef TINY_GSM_USE_HEX
while (stream.available() < 2) { TINY_GSM_YIELD(); }
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()) { TINY_GSM_YIELD(); }
while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); }
char c = stream.read();
#endif
sockets[mux]->rx.put(c);
}
waitResponse();
DBG("### READ:", len_confirmed, "from", mux);
maintain(); // Listen for a close or other URC
return len_confirmed;
DBG("### READ:", TinyGsmMin(len_confirmed, len_requested), "from", mux);
return TinyGsmMin(len_confirmed, len_requested);
}
size_t modemGetAvailable(uint8_t mux) {
@ -923,7 +803,6 @@ protected:
if (!result) {
sockets[mux]->sock_connected = modemGetConnected(mux);
}
maintain(); // Listen for a close or other URC
return result;
}
@ -931,7 +810,6 @@ protected:
sendAT(GF("+CIPSTATUS="), mux);
int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\""));
waitResponse();
maintain(); // Listen for a close or other URC
return 1 == res;
}
@ -941,16 +819,10 @@ public:
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
@ -1016,7 +888,7 @@ public:
DBG("### Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
@ -1029,12 +901,12 @@ finish:
return index;
}
uint8_t waitResponse(uint32_t timeout,
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),


+ 760
- 0
src/TinyGsmClientSaraR4.h View File

@ -0,0 +1,760 @@
/**
* @file TinyGsmClientSaraR4.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef TinyGsmClientSaraR4_h
#define TinyGsmClientSaraR4_h
//#pragma message("TinyGSM: TinyGsmClientSaraR4")
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 7
#include <TinyGsmCommon.h>
#define GSM_NL "\r\n"
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:";
enum SimStatus {
SIM_ERROR = 0,
SIM_READY = 1,
SIM_LOCKED = 2,
};
enum RegStatus {
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmSaraR4
{
public:
class GsmClient : public Client
{
friend class TinyGsmSaraR4;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public:
GsmClient() {}
GsmClient(TinyGsmSaraR4& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmSaraR4* modem, uint8_t mux = 0) {
this->at = modem;
this->mux = mux;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
at->sockets[mux] = this;
return true;
}
public:
virtual int connect(const char *host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
uint8_t oldMux = mux;
sock_connected = at->modemConnect(host, port, &mux, false, timeout_s);
if (mux != oldMux) {
DBG("WARNING: Mux number changed from", oldMux, "to", mux);
at->sockets[oldMux] = NULL;
}
at->sockets[mux] = this;
at->maintain();
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERLOADS()
virtual void stop() {
TINY_GSM_YIELD();
// Read and dump anything remaining in the modem's internal buffer.
// The socket will appear open in response to connected() even after it
// closes until all data is read from the buffer.
// Doing it this way allows the external mcu to find and get all of the data
// that it wants from the socket even if it was closed externally.
rx.clear();
at->maintain();
while (sock_available > 0) {
at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
rx.clear();
at->maintain();
}
at->modemDisconnect(mux);
}
TINY_GSM_CLIENT_WRITE()
TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK()
TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK()
TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
TinyGsmSaraR4* at;
uint8_t mux;
uint16_t sock_available;
uint32_t prev_check;
bool sock_connected;
bool got_data;
RxFifo rx;
};
class GsmClientSecure : public GsmClient
{
public:
GsmClientSecure() {}
GsmClientSecure(TinyGsmSaraR4& modem, uint8_t mux = 1)
: GsmClient(modem, mux)
{}
public:
virtual int connect(const char *host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
uint8_t oldMux = mux;
sock_connected = at->modemConnect(host, port, &mux, true, timeout_s);
if (mux != oldMux) {
DBG("WARNING: Mux number changed from", oldMux, "to", mux);
at->sockets[oldMux] = NULL;
}
at->sockets[mux] = this;
at->maintain();
return sock_connected;
}
};
public:
TinyGsmSaraR4(Stream& stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
return false;
}
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) {
return false;
}
#ifdef TINY_GSM_DEBUG
sendAT(GF("+CMEE=2")); // turn on verbose error codes
#else
sendAT(GF("+CMEE=0")); // turn off error codes
#endif
waitResponse();
getModemName();
int ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
}
// if the sim is ready, or it's locked but no pin has been provided, return true
else {
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
String getModemName() {
sendAT(GF("+CGMI"));
String res1;
if (waitResponse(1000L, res1) != 1) {
return "u-blox Cellular Modem";
}
res1.replace(GSM_NL "OK" GSM_NL, "");
res1.trim();
sendAT(GF("+GMM"));
String res2;
if (waitResponse(1000L, res2) != 1) {
return "u-blox Cellular Modem";
}
res2.replace(GSM_NL "OK" GSM_NL, "");
res2.trim();
String name = res1 + String(' ') + res2;
DBG("### Modem:", name);
if (!name.startsWith("u-blox SARA-R4") && !name.startsWith("u-blox SARA-N4")) {
DBG("### WARNING: You are using the wrong TinyGSM modem!");
}
return name;
}
TINY_GSM_MODEM_SET_BAUD_IPR()
TINY_GSM_MODEM_TEST_AT()
TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS()
bool factoryDefault() {
sendAT(GF("&F")); // Resets the current profile, other NVM not affected
return waitResponse() == 1;
}
TINY_GSM_MODEM_GET_INFO_ATI()
bool hasSSL() {
return true;
}
bool hasWifi() {
return false;
}
bool hasGPRS() {
return true;
}
/*
* Power functions
*/
bool restart() {
if (!testAT()) {
return false;
}
sendAT(GF("+CFUN=15"));
if (waitResponse(10000L) != 1) {
return false;
}
delay(3000); // TODO: Verify delay timing here
return init();
}
bool poweroff() {
sendAT(GF("+CPWROFF"));
return waitResponse(40000L) == 1;
}
bool radioOff() {
sendAT(GF("+CFUN=0"));
if (waitResponse(10000L) != 1) {
return false;
}
delay(3000);
return true;
}
bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* SIM card functions
*/
TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
TINY_GSM_MODEM_GET_SIMCCID_CCID()
String getIMEI() {
sendAT(GF("+CGSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
continue;
}
int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED"));
waitResponse();
switch (status) {
case 2:
case 3: return SIM_LOCKED;
case 1: return SIM_READY;
default: return SIM_ERROR;
}
}
return SIM_ERROR;
}
TINY_GSM_MODEM_GET_REGISTRATION_XREG(CEREG)
TINY_GSM_MODEM_GET_OPERATOR_COPS()
/*
* Generic network functions
*/
TINY_GSM_MODEM_GET_CSQ()
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
if (s == REG_OK_HOME || s == REG_OK_ROAMING)
return true;
else if (s == REG_UNKNOWN) // for some reason, it can hang at unknown..
return isGprsConnected();
else return false;
}
TINY_GSM_MODEM_WAIT_FOR_NETWORK()
bool setURAT( uint8_t urat ) {
// AT+URAT=<SelectedAcT>[,<PreferredAct>[,<2ndPreferredAct>]]
sendAT(GF("+COPS=2")); // Deregister from network
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+URAT="), urat); // Radio Access Technology (RAT) selection
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+COPS=0")); // Auto-register to the network
if (waitResponse() != 1) {
return false;
}
return restart();
}
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
sendAT(GF("+CGATT=1")); // attach to GPRS
if (waitResponse(360000L) != 1) {
return false;
}
// Using CGDCONT sets up an "external" PCP context, i.e. a data connection
// using the external IP stack (e.g. Windows dial up) and PPP link over the
// serial interface. This is the only command set supported by the LTE-M
// and LTE NB-IoT modules (SARA-R4xx, SARA-N4xx)
if (user && strlen(user) > 0) {
sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"'); // Set the authentication
waitResponse();
}
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"'); // Define PDP context 1
waitResponse();
sendAT(GF("+CGACT=1,1")); // activate PDP profile/context 1
if (waitResponse(150000L) != 1) {
return false;
}
return true;
}
bool gprsDisconnect() {
sendAT(GF("+CGACT=1,0")); // Deactivate PDP context 1
if (waitResponse(40000L) != 1) {
return false;
}
sendAT(GF("+CGATT=0")); // detach from GPRS
if (waitResponse(360000L) != 1) {
return false;
}
return true;
}
TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED()
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+CGPADDR"));
if (waitResponse(GF(GSM_NL "+CGPADDR:")) != 1) {
return "";
}
streamSkipUntil(','); // Skip context id
String res = stream.readStringUntil('\r');
if (waitResponse() != 1) {
return "";
}
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Messaging functions
*/
String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sendSMS(const String& number, const String& text) {
sendAT(GF("+CSCS=\"GSM\"")); // Set GSM default alphabet
waitResponse();
sendAT(GF("+CMGF=1")); // Set preferred message format to text mode
waitResponse();
sendAT(GF("+CMGS=\""), number, GF("\"")); // set the phone number
if (waitResponse(GF(">")) != 1) {
return false;
}
stream.print(text); // Actually send the message
stream.write((char)0x1A);
stream.flush();
return waitResponse(60000L) == 1;
}
bool sendSMS_UTF16(const String& number, const void* text, size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Location functions
*/
String getGsmLocation() {
sendAT(GF("+ULOC=2,3,0,120,1"));
if (waitResponse(30000L, GF(GSM_NL "+UULOC:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Battery & temperature functions
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int8_t getBattPercent() {
sendAT(GF("+CIND?"));
if (waitResponse(GF(GSM_NL "+CIND:")) != 1) {
return 0;
}
int8_t res = stream.readStringUntil(',').toInt();
int8_t percent = res*20; // return is 0-5
// Wait for final OK
waitResponse();
return percent;
}
uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE;
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
percent = getBattPercent();
return true;
}
float getTemperature() {
// First make sure the temperature is set to be in celsius
sendAT(GF("+UTEMP=0")); // Would use 1 for Fahrenheit
if (waitResponse() != 1) {
return (float)-9999;
}
sendAT(GF("+UTEMP?"));
if (waitResponse(GF(GSM_NL "+UTEMP:")) != 1) {
return (float)-9999;
}
streamSkipUntil(','); // Skip units (C/F)
int16_t res = stream.readStringUntil('\n').toInt();
float temp = -9999;
if (res != 655355) {
temp = ((float)res)/10;
}
return temp;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t* mux,
bool ssl = false, int timeout_s = 120)
{
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
sendAT(GF("+USOCR=6")); // create a socket
if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) { // reply is +USOCR: ## of socket created
return false;
}
*mux = stream.readStringUntil('\n').toInt();
waitResponse();
if (ssl) {
sendAT(GF("+USOSEC="), *mux, ",1");
waitResponse();
}
// Enable NODELAY
sendAT(GF("+USOSO="), *mux, GF(",6,1,1"));
waitResponse();
// Enable KEEPALIVE, 30 sec
//sendAT(GF("+USOSO="), *mux, GF(",6,2,30000"));
//waitResponse();
// connect on the allocated socket
sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port);
int rsp = waitResponse(timeout_ms);
return (1 == rsp);
}
bool modemDisconnect(uint8_t mux) {
TINY_GSM_YIELD();
if (!modemGetConnected(mux)) {
sockets[mux]->sock_connected = false;
return true;
}
sendAT(GF("+USOCL="), mux);
return 1 == waitResponse(120000L); // can take up to 120s to get a response
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+USOWR="), mux, ',', len);
if (waitResponse(GF("@")) != 1) {
return 0;
}
// 50ms delay, see AT manual section 25.10.4
delay(50);
stream.write((uint8_t*)buff, len);
stream.flush();
if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip mux
int sent = stream.readStringUntil('\n').toInt();
waitResponse(); // sends back OK after the confirmation of number sent
return sent;
}
size_t modemRead(size_t size, uint8_t mux) {
sendAT(GF("+USORD="), mux, ',', size);
if (waitResponse(GF(GSM_NL "+USORD:")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip mux
size_t len = stream.readStringUntil(',').toInt();
sockets[mux]->sock_available = len;
streamSkipUntil('\"');
for (size_t i=0; i<len; i++) {
TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT
}
streamSkipUntil('\"');
waitResponse();
DBG("### READ:", len, "from", mux);
return len;
}
size_t modemGetAvailable(uint8_t mux) {
// NOTE: Querying a closed socket gives an error "operation not allowed"
sendAT(GF("+USORD="), mux, ",0");
size_t result = 0;
uint8_t res = waitResponse(GF(GSM_NL "+USORD:"));
// Will give error "operation not allowed" when attempting to read a socket
// that you have already told to close
if (res == 1) {
streamSkipUntil(','); // Skip mux
result = stream.readStringUntil('\n').toInt();
// if (result) DBG("### DATA AVAILABLE:", result, "on", mux);
waitResponse();
}
if (!result && res != 2 && res != 3) { // Don't check modemGetConnected after an error
sockets[mux]->sock_connected = modemGetConnected(mux);
}
return result;
}
bool modemGetConnected(uint8_t mux) {
// NOTE: Querying a closed socket gives an error "operation not allowed"
sendAT(GF("+USOCTL="), mux, ",10");
uint8_t res = waitResponse(GF(GSM_NL "+USOCTL:"));
if (res != 1)
return false;
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip type
int result = stream.readStringUntil('\n').toInt();
// 0: the socket is in INACTIVE status (it corresponds to CLOSED status
// defined in RFC793 "TCP Protocol Specification" [112])
// 1: the socket is in LISTEN status
// 2: the socket is in SYN_SENT status
// 3: the socket is in SYN_RCVD status
// 4: the socket is in ESTABILISHED status
// 5: the socket is in FIN_WAIT_1 status
// 6: the socket is in FIN_WAIT_2 status
// 7: the sokcet is in CLOSE_WAIT status
// 8: the socket is in CLOSING status
// 9: the socket is in LAST_ACK status
// 10: the socket is in TIME_WAIT status
waitResponse();
return (result != 0);
}
public:
/*
Utilities
*/
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
/*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);
int index = 0;
unsigned long startMillis = millis();
do {
TINY_GSM_YIELD();
while (stream.available() > 0) {
int a = stream.read();
if (a <= 0) continue; // Skip 0x00 bytes, just in case
data += (char)a;
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(GSM_NL "+UUSORD:"))) {
int mux = stream.readStringUntil(',').toInt();
int len = stream.readStringUntil('\n').toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
sockets[mux]->sock_available = len;
}
data = "";
DBG("### URC Data Received:", len, "on", mux);
} else if (data.endsWith(GF(GSM_NL "+UUSOCL:"))) {
int mux = stream.readStringUntil('\n').toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### URC Sock Closed:", mux);
}
}
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
if (data.length()) {
DBG("### Unhandled:", data);
}
data = "";
}
//DBG('<', index, '>');
return index;
}
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
return waitResponse(1000, r1, r2, r3, r4, r5);
}
public:
Stream& stream;
protected:
GsmClient* sockets[TINY_GSM_MUX_COUNT];
};
#endif

+ 770
- 0
src/TinyGsmClientSequansMonarch.h View File

@ -0,0 +1,770 @@
/**
* @file TinyGsmClientSequansMonarch.h
* @author Michael Krumpus
* @license LGPL-3.0
* @copyright Copyright (c) 2019 Michael Krumpus
* @date Jan 2019
*/
#ifndef TinyGsmClientSequansMonarch_h
#define TinyGsmClientSequansMonarch_h
//#define TINY_GSM_DEBUG Serial
//#define TINY_GSM_USE_HEX
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 6
#include <TinyGsmCommon.h>
#define GSM_NL "\r\n"
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
enum SimStatus {
SIM_ERROR = 0,
SIM_READY = 1,
SIM_LOCKED = 2,
};
enum RegStatus {
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
enum SocketStatus {
SOCK_CLOSED = 0,
SOCK_ACTIVE_DATA = 1,
SOCK_SUSPENDED = 2,
SOCK_SUSPENDED_PENDING_DATA = 3,
SOCK_LISTENING = 4,
SOCK_INCOMING = 5,
SOCK_OPENING = 6,
};
class TinyGsmSequansMonarch
{
public:
class GsmClient : public Client
{
friend class TinyGsmSequansMonarch;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public:
GsmClient() {}
GsmClient(TinyGsmSequansMonarch& modem, uint8_t mux = 1) {
init(&modem, mux);
}
bool init(TinyGsmSequansMonarch* modem, uint8_t mux = 1) {
this->at = modem;
this->mux = mux;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
// adjust for zero indexed socket array vs Sequans' 1 indexed mux numbers
// using modulus will force 6 back to 0
at->sockets[mux % TINY_GSM_MUX_COUNT] = this;
return true;
}
public:
virtual int connect(const char *host, uint16_t port, int timeout_s) {
if (sock_connected) stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERLOADS()
virtual void stop() {
TINY_GSM_YIELD();
// Read and dump anything remaining in the modem's internal buffer.
// The socket will appear open in response to connected() even after it
// closes until all data is read from the buffer.
// Doing it this way allows the external mcu to find and get all of the data
// that it wants from the socket even if it was closed externally.
rx.clear();
at->maintain();
while (sock_available > 0) {
at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
rx.clear();
at->maintain();
}
at->sendAT(GF("+SQNSH="), mux);
sock_connected = false;
at->waitResponse();
}
TINY_GSM_CLIENT_WRITE()
TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK()
TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK()
TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
TinyGsmSequansMonarch* at;
uint8_t mux;
uint16_t sock_available;
uint32_t prev_check;
bool sock_connected;
bool got_data;
RxFifo rx;
};
class GsmClientSecure : public GsmClient
{
public:
GsmClientSecure() {}
GsmClientSecure(TinyGsmSequansMonarch& modem, uint8_t mux = 1)
: GsmClient(modem, mux)
{}
protected:
bool strictSSL = false;
public:
virtual int connect(const char *host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
// configure security profile 1 with parameters:
if (strictSSL) {
// require minimum of TLS 1.2 (3)
// only support cipher suite 0x3D: TLS_RSA_WITH_AES_256_CBC_SHA256
// verify server certificate against imported CA certs 0 and enforce validity period (3)
at->sendAT(GF("+SQNSPCFG=1,3,\"0x3D\",3,0,,,\"\",\"\""));
} else {
// use TLS 1.0 or higher (1)
// support wider variety of cipher suites
// do not verify server certificate (0)
at->sendAT(GF("+SQNSPCFG=1,1,\"0x2F;0x35;0x3C;0x3D\",0,,,,\"\",\"\""));
}
if (at->waitResponse() != 1) {
DBG("failed to configure security profile");
return false;
}
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
return sock_connected;
}
void setStrictSSL(bool strict) {
strictSSL = strict;
}
};
public:
TinyGsmSequansMonarch(Stream& stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
return false;
}
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) {
return false;
}
getSimStatus();
return true;
}
String getModemName() {
return "Sequans Monarch";
}
TINY_GSM_MODEM_SET_BAUD_IPR()
TINY_GSM_MODEM_TEST_AT()
void maintain() {
for (int mux = 1; mux <= TINY_GSM_MUX_COUNT; mux++) {
GsmClient* sock = sockets[mux % TINY_GSM_MUX_COUNT];
if (sock && sock->got_data) {
sock->got_data = false;
sock->sock_available = modemGetAvailable(mux);
// modemGetConnected() always checks the state of ALL socks
modemGetConnected();
}
}
while (stream.available()) {
waitResponse(15, NULL, NULL);
}
}
bool factoryDefault() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
waitResponse();
sendAT(GF("+IPR=0")); // Auto-baud
waitResponse();
sendAT(GF("+IFC=0,0")); // No Flow Control
waitResponse();
sendAT(GF("+ICF=3,3")); // 8 data 0 parity 1 stop
waitResponse();
sendAT(GF("+CSCLK=0")); // Disable Slow Clock
waitResponse();
sendAT(GF("&W")); // Write configuration
return waitResponse() == 1;
}
TINY_GSM_MODEM_GET_INFO_ATI()
bool hasSSL() {
return true;
}
/*
* Power functions
*/
bool restart() {
if (!testAT()) {
return false;
}
sendAT(GF("+CFUN=0"));
int res = waitResponse(20000L, GFP(GSM_OK), GFP(GSM_ERROR), GF("+SYSSTART")) ;
if (res != 1 && res != 3) {
return false;
}
sendAT(GF("+CFUN=1,1"));
res = waitResponse(20000L, GF("+SYSSTART"), GFP(GSM_ERROR)) ;
if (res != 1 && res != 3) {
return false;
}
delay(1000);
return init();
}
bool poweroff() {
sendAT(GF("+SQNSSHDN"));
return waitResponse();
}
bool radioOff() {
sendAT(GF("+CFUN=0"));
if (waitResponse(10000L) != 1) {
return false;
}
delay(3000);
return true;
}
/*
When power saving is enabled, UART0 interface is activated with sleep mode support.
Module power state is controlled by RTS0 line. When no activity on UART, CTS line
will be set to OFF state (driven high level) <timeout> milliseconds (100ms to 10s,
default 5s) after the last sent character, then module will go to sleep mode as soon
as DTE set RTS line to OFF state (driver high level).
*/
bool sleepEnable(bool enable = true) {
sendAT(GF("+SQNIPSCFG="), enable);
return waitResponse() == 1;
}
/*
* SIM card functions
*/
TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
String getSimCCID() {
sendAT(GF("+SQNCCID"));
if (waitResponse(GF(GSM_NL "+SQNCCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_IMEI_GSN()
SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
continue;
}
int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED"));
waitResponse();
switch (status) {
case 2:
case 3: return SIM_LOCKED;
case 1: return SIM_READY;
default: return SIM_ERROR;
}
}
return SIM_ERROR;
}
TINY_GSM_MODEM_GET_REGISTRATION_XREG(CEREG)
TINY_GSM_MODEM_GET_OPERATOR_COPS()
/*
* Generic network functions
*/
TINY_GSM_MODEM_GET_CSQ()
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
if (s == REG_OK_HOME || s == REG_OK_ROAMING) {
// DBG(F("connected with status:"), s);
return true;
} else {
return false;
}
}
TINY_GSM_MODEM_WAIT_FOR_NETWORK()
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
// Define the PDP context (This uses context #3!)
sendAT(GF("+CGDCONT=3,\"IPV4V6\",\""), apn, '"');
waitResponse();
// Set authentication
if (user && strlen(user) > 0) {
sendAT(GF("+CGAUTH=3,1,\""), user, GF("\",\""), pwd, GF("\""));
waitResponse();
}
// Activate the PDP context
sendAT(GF("+CGACT=1,3"));
waitResponse(60000L);
// Attach to GPRS
sendAT(GF("+CGATT=1"));
if (waitResponse(60000L) != 1)
return false;
return true;
}
bool gprsDisconnect() {
sendAT(GF("+CGATT=0"));
if (waitResponse(60000L) != 1)
return false;
return true;
}
bool isGprsConnected() {
sendAT(GF("+CGATT?"));
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
return false;
}
int res = stream.readStringUntil('\n').toInt();
waitResponse();
if (res != 1)
return false;
return true;
}
/*
* IP Address functions
*/
String getLocalIP() {
sendAT(GF("+CGPADDR=3"));
if (waitResponse(10000L, GF("+CGPADDR: 3,\"")) != 1) {
return "";
}
String res = stream.readStringUntil('\"');
waitResponse();
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED;
String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sendSMS(const String& number, const String& text) {
sendAT(GF("+CMGF=1"));
waitResponse();
//Set GSM 7 bit default alphabet (3GPP TS 23.038)
sendAT(GF("+CSCS=\"GSM\""));
waitResponse();
sendAT(GF("+CMGS=\""), number, GF("\""));
if (waitResponse(GF(">")) != 1) {
return false;
}
stream.print(text);
stream.write((char)0x1A);
stream.flush();
return waitResponse(60000L) == 1;
}
bool sendSMS_UTF16(const String& number, const void* text, size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Location functions
*/
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery & temperature functions
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE;
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE;
float getTemperature() {
sendAT(GF("+SMDTH"));
if (waitResponse(10000L, GF("+SMDTH: ")) != 1) {
return (float)-9999;
}
String res;
if (waitResponse(1000L, res) != 1) {
return (float)-9999;
}
if (res.indexOf("ERROR") >=0) {
return (float)-9999;
}
return res.toFloat();
}
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75)
{
int rsp;
unsigned long startMillis = millis();
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
if (ssl) {
// enable SSl and use security profile 1
//AT+SQNSSCFG=<connId>,<enable>,<spId>
sendAT(GF("+SQNSSCFG="), mux, GF(",1,1"));
if (waitResponse() != 1) {
DBG("### WARNING: failed to configure secure socket");
return false;
}
}
// Socket configuration
//AT+SQNSCFG:<connId1>, <cid1>, <pktSz1>, <maxTo1>, <connTo1>, <txTo1>
// <connId1> = Connection ID = mux
// <cid1> = PDP context ID = 3 - this is number set up above in the GprsConnect function
// <pktSz1> = Packet Size, used for online data mode only = 300 (default)
// <maxTo1> = Max timeout in seconds = 90 (default)
// <connTo1> = Connection timeout in hundreds of milliseconds = 600 (default)
// <txTo1> = Data sending timeout in hundreds of milliseconds, used for online data mode only = 50 (default)
sendAT(GF("+SQNSCFG="), mux, GF(",3,300,90,600,50"));
waitResponse(5000L);
// Socket configuration extended
//AT+SQNSCFGEXT:<connId1>, <srMode1>, <recvDataMode1>, <keepalive1>, <listenAutoRsp1>, <sendDataMode1>
// <connId1> = Connection ID = mux
// <srMode1> = Send/Receive URC model = 1 - data amount mode
// <recvDataMode1> = Receive data mode = 0 - data as text (1 would be as hex)
// <keepalive1> = unused = 0
// <listenAutoRsp1> = Listen auto-response mode = 0 - deactivated
// <sendDataMode1> = Send data mode = 0 - data as text (1 would be as hex)
sendAT(GF("+SQNSCFGEXT="), mux, GF(",1,0,0,0,0"));
waitResponse(5000L);
// Socket dial
//AT+SQNSD=<connId>,<txProt>,<rPort>,<IPaddr>[,<closureType>[,<lPort>[,<connMode>[,acceptAnyRemote]]]]
// <connId> = Connection ID = mux
// <txProt> = Transmission protocol = 0 - TCP (1 for UDP)
// <rPort> = Remote host port to contact
// <IPaddr> = Any valid IP address in the format xxx.xxx.xxx.xxx or any host name solved with a DNS query
// <closureType> = Socket closure behaviour for TCP, has no effect for UDP = 0 - local port closes when remote does (default)
// <lPort> = UDP connection local port, has no effect for TCP connections.
// <connMode> = Connection mode = 1 - command mode connection
// <acceptAnyRemote> = Applies to UDP only
sendAT(GF("+SQNSD="), mux, ",0,", port, ',', GF("\""), host, GF("\""), ",0,0,1");
rsp = waitResponse((timeout_ms - (millis() - startMillis)),
GFP(GSM_OK),
GFP(GSM_ERROR),
GF("NO CARRIER" GSM_NL)
);
// creation of socket failed immediately.
if (rsp != 1) return false;
// wait until we get a good status
bool connected = false;
while (!connected && ((millis() - startMillis) < timeout_ms)) {
connected = modemGetConnected(mux);
delay(100); // socket may be in opening state
}
return connected;
}
int modemSend(const void* buff, size_t len, uint8_t mux) {
if (sockets[mux % TINY_GSM_MUX_COUNT]->sock_connected == false) {
DBG("### Sock closed, cannot send data!");
return 0;
}
sendAT(GF("+SQNSSENDEXT="), mux, ',', len);
waitResponse(10000L, GF(GSM_NL "> "));
stream.write((uint8_t*)buff, len);
stream.flush();
if (waitResponse() != 1) {
DBG("### no OK after send");
return 0;
}
return len;
// uint8_t nAttempts = 5;
// bool gotPrompt = false;
// while (nAttempts > 0 && !gotPrompt) {
// sendAT(GF("+SQNSSEND="), mux);
// if (waitResponse(5000, GF(GSM_NL "> ")) == 1) {
// gotPrompt = true;
// }
// nAttempts--;
// delay(50);
// }
// if (gotPrompt) {
// stream.write((uint8_t*)buff, len);
// stream.write((char)0x1A);
// stream.flush();
// if (waitResponse() != 1) {
// DBG("### no OK after send");
// return 0;
// }
// return len;
// }
// return 0;
}
size_t modemRead(size_t size, uint8_t mux) {
sendAT(GF("+SQNSRECV="), mux, ',', size);
if (waitResponse(GF("+SQNSRECV: ")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip mux
size_t len = stream.readStringUntil('\n').toInt();
for (size_t i=0; i<len; i++) {
uint32_t startMillis = millis(); \
while (!stream.available() && ((millis() - startMillis) < sockets[mux % TINY_GSM_MUX_COUNT]->_timeout)) { TINY_GSM_YIELD(); } \
char c = stream.read(); \
sockets[mux % TINY_GSM_MUX_COUNT]->rx.put(c);
}
// DBG("### Read:", len, "from", mux);
waitResponse();
sockets[mux % TINY_GSM_MUX_COUNT]->sock_available = modemGetAvailable(mux);
return len;
}
size_t modemGetAvailable(uint8_t mux) {
sendAT(GF("+SQNSI="), mux);
size_t result = 0;
if (waitResponse(GF("+SQNSI:")) == 1) {
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip total sent
streamSkipUntil(','); // Skip total received
result = stream.readStringUntil(',').toInt(); // keep data not yet read
waitResponse();
}
// DBG("### Available:", result, "on", mux);
return result;
}
bool modemGetConnected(uint8_t mux = 1) {
// This single command always returns the connection status of all
// six possible sockets.
sendAT(GF("+SQNSS"));
for (int muxNo = 1; muxNo <= TINY_GSM_MUX_COUNT; muxNo++) {
if (waitResponse(GFP(GSM_OK), GF(GSM_NL "+SQNSS: ")) != 2) {
break;
};
uint8_t status = 0;
// if (stream.readStringUntil(',').toInt() != muxNo) { // check the mux no
// DBG("### Warning: misaligned mux numbers!");
// }
streamSkipUntil(','); // skip mux [use muxNo]
status = stream.parseInt(); // Read the status
// if mux is in use, will have comma then other info after the status
// if not, there will be new line immediately after status
// streamSkipUntil('\n'); // Skip port and IP info
// SOCK_CLOSED = 0,
// SOCK_ACTIVE_DATA = 1,
// SOCK_SUSPENDED = 2,
// SOCK_SUSPENDED_PENDING_DATA = 3,
// SOCK_LISTENING = 4,
// SOCK_INCOMING = 5,
// SOCK_OPENING = 6,
sockets[muxNo % TINY_GSM_MUX_COUNT]->sock_connected = \
((status != SOCK_CLOSED) && (status != SOCK_INCOMING) && (status != SOCK_OPENING));
}
waitResponse(); // Should be an OK at the end
return sockets[mux % TINY_GSM_MUX_COUNT]->sock_connected;
}
public:
/*
Utilities
*/
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
/*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);
int index = 0;
unsigned long startMillis = millis();
do {
TINY_GSM_YIELD();
while (stream.available() > 0) {
int a = stream.read();
if (a <= 0) continue; // Skip 0x00 bytes, just in case
data += (char)a;
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(GSM_NL "+SQNSRING:"))) {
int mux = stream.readStringUntil(',').toInt();
int len = stream.readStringUntil('\n').toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux % TINY_GSM_MUX_COUNT]) {
sockets[mux % TINY_GSM_MUX_COUNT]->got_data = true;
sockets[mux % TINY_GSM_MUX_COUNT]->sock_available = len;
}
data = "";
DBG("### URC Data Received:", len, "on", mux);
} else if (data.endsWith(GF("SQNSH: "))) {
int mux = stream.readStringUntil('\n').toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux % TINY_GSM_MUX_COUNT]) {
sockets[mux % TINY_GSM_MUX_COUNT]->sock_connected = false;
}
data = "";
DBG("### URC Sock Closed: ", mux);
}
}
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
if (data.length()) {
DBG("### Unhandled:", data);
}
data = "";
}
return index;
}
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
return waitResponse(1000, r1, r2, r3, r4, r5);
}
public:
Stream& stream;
protected:
GsmClient* sockets[TINY_GSM_MUX_COUNT];
};
#endif

+ 140
- 377
src/TinyGsmClientUBLOX.h View File

@ -12,6 +12,10 @@
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 7
#include <TinyGsmCommon.h>
@ -37,7 +41,7 @@ enum RegStatus {
};
class TinyGsmUBLOX : public TinyGsmModem
class TinyGsmUBLOX
{
public:
@ -62,50 +66,30 @@ public:
sock_connected = false;
got_data = false;
// at->sockets[mux] = this;
// ^^ TODO: attach the socket here at init? Or later at connect?
// Currently done inconsistently between modems
at->sockets[mux] = this;
return true;
}
public:
virtual int connect(const char *host, uint16_t port) {
if (sock_connected) {
stop();
// If we're creating a new connection on the same client, we need to wait
// until the async close has finished on Cat-M modems.
// After close has completed, the +UUSOCL should appear.
if (at->isCatM) {
DBG("Waiting for +UUSOCL URC on", mux);
for (unsigned long start = millis(); millis() - start < 120000L; ) {
at->maintain();
if (!sock_connected) break;
}
}
}
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);
sock_connected = at->modemConnect(host, port, &mux);
uint8_t oldMux = mux;
sock_connected = at->modemConnect(host, port, &mux, false, timeout_s);
if (mux != oldMux) {
DBG("WARNING: Mux number changed from", oldMux, "to", mux);
at->sockets[oldMux] = NULL;
}
at->sockets[mux] = this;
// ^^ TODO: attach the socet after attempting connection or above at init?
// Currently done inconsistently between modems
at->maintain();
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
String host; host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
TINY_GSM_CLIENT_CONNECT_OVERLOADS()
virtual void stop() {
TINY_GSM_YIELD();
@ -122,89 +106,15 @@ public:
at->maintain();
}
at->modemDisconnect(mux);
// We don't actually know if the CatM modem has finished closing because
// we're using an "asynchronous" close
if (!at->isCatM) sock_connected = false;
}
virtual size_t write(const uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c) {
return write(&c, 1);
}
virtual size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual int available() {
TINY_GSM_YIELD();
if (!rx.size()) {
// Workaround: sometimes SARA R410 forgets to notify about data arrival.
// TODO: Currently we ping the module periodically,
// but maybe there's a better indicator that we need to poll
if (millis() - prev_check > 250) {
got_data = true;
prev_check = millis();
}
at->maintain();
}
return rx.size() + sock_available;
}
virtual int read(uint8_t *buf, size_t size) {
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// Workaround: sometimes SARA R410 forgets to notify about data arrival.
// TODO: Currently we ping the module periodically,
// but maybe there's a better indicator that we need to poll
if (millis() - prev_check > 250) {
got_data = true;
prev_check = millis();
}
// TODO: Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
} else {
break;
}
}
return cnt;
}
TINY_GSM_CLIENT_WRITE() ;
virtual int read() {
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() ;
virtual int peek() { return -1; } //TODO
virtual void flush() { at->stream.flush(); }
TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() ;
virtual uint8_t connected() {
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool() { return connected(); }
TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
/*
* Extended API
@ -233,14 +143,17 @@ public:
{}
public:
virtual int connect(const char *host, uint16_t port) {
virtual int connect(const char *host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
// sock_connected = at->modemConnect(host, port, mux, true);
sock_connected = at->modemConnect(host, port, &mux, true);
uint8_t oldMux = mux;
sock_connected = at->modemConnect(host, port, &mux, true, timeout_s);
if (mux != oldMux) {
DBG("WARNING: Mux number changed from", oldMux, "to", mux);
at->sockets[oldMux] = NULL;
}
at->sockets[mux] = this;
// TODO: When is the socket attached?
at->maintain();
return sock_connected;
}
@ -250,16 +163,19 @@ public:
public:
TinyGsmUBLOX(Stream& stream)
: TinyGsmModem(stream), stream(stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
isCatM = false; // For SARA R4 and N4 series
}
/*
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
if (!testAT()) {
@ -277,14 +193,8 @@ public:
#endif
waitResponse();
String name = getModemName();
DBG(GF("### Modem:"), name);
if (name.startsWith("u-blox SARA-R4") or name.startsWith("u-blox SARA-N4")) {
isCatM = true;
}
else if (name.startsWith("u-blox SARA-N2")) {
DBG(GF("### SARA N2 NB-IoT modems not supported!"), name);
}
getModemName();
int ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
@ -314,59 +224,32 @@ public:
res2.replace(GSM_NL "OK" GSM_NL, "");
res2.trim();
return res1 + String(' ') + res2;
}
String name = res1 + String(' ') + res2;
DBG("### Modem:", name);
if (name.startsWith("u-blox SARA-R4") || name.startsWith("u-blox SARA-N4")) {
DBG("### WARNING: You are using the wrong TinyGSM modem!");
}
else if (name.startsWith("u-blox SARA-N2")) {
DBG("### SARA N2 NB-IoT modems not supported!");
}
void setBaud(unsigned long baud) {
sendAT(GF("+IPR="), baud);
return name;
}
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) return true;
delay(100);
}
return false;
}
TINY_GSM_MODEM_SET_BAUD_IPR()
void maintain() {
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClient* sock = sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
sock->sock_available = modemGetAvailable(mux);
}
}
while (stream.available()) {
waitResponse(15, NULL, NULL);
}
}
TINY_GSM_MODEM_TEST_AT()
TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS()
bool factoryDefault() {
if (!isCatM) {
sendAT(GF("+UFACTORY=0,1")); // No factory restore, erase NVM
waitResponse();
sendAT(GF("+CFUN=16")); // Reset
return waitResponse() == 1;
}
else {
sendAT(GF("&F")); // Resets the current profile, other NVM not affected
return waitResponse() == 1;
}
sendAT(GF("+UFACTORY=0,1")); // No factory restore, erase NVM
waitResponse();
sendAT(GF("+CFUN=16")); // Reset
return waitResponse() == 1;
}
String getModemInfo() {
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
TINY_GSM_MODEM_GET_INFO_ATI()
bool hasSSL() {
return true;
@ -388,14 +271,7 @@ public:
if (!testAT()) {
return false;
}
if (!isCatM)
{
sendAT(GF("+CFUN=16"));
}
else
{
sendAT(GF("+CFUN=15"));
}
sendAT(GF("+CFUN=16"));
if (waitResponse(10000L) != 1) {
return false;
}
@ -423,21 +299,9 @@ public:
* SIM card functions
*/
bool simUnlock(const char *pin) {
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
String getSimCCID() {
sendAT(GF("+CCID"));
if (waitResponse(GF(GSM_NL "+CCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
TINY_GSM_MODEM_GET_SIMCCID_CCID()
String getIMEI() {
sendAT(GF("+CGSN"));
@ -450,8 +314,8 @@ public:
return res;
}
SimStatus getSimStatus(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
@ -469,49 +333,15 @@ public:
return SIM_ERROR;
}
RegStatus getRegistrationStatus() {
if (isCatM) { // Check EPS registration for LTE modules
sendAT(GF("+CEREG?"));
if (waitResponse(GF(GSM_NL "+CEREG:")) != 1) {
return REG_UNKNOWN;
}
}
else {
sendAT(GF("+CGREG?")); // Check GPRS registration for others
if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) {
return REG_UNKNOWN;
}
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG)
String getOperator() {
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_OPERATOR_COPS()
/*
* Generic network functions
*/
int16_t getSignalQuality() {
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
TINY_GSM_MODEM_GET_CSQ()
bool isNetworkConnected() {
RegStatus s = getRegistrationStatus();
@ -522,27 +352,12 @@ public:
else return false;
}
bool setURAT( uint8_t urat ) {
// AT+URAT=<SelectedAcT>[,<PreferredAct>[,<2ndPreferredAct>]]
sendAT(GF("+COPS=2")); // Deregister from network
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+URAT="), urat); // Radio Access Technology (RAT) selection
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+COPS=0")); // Auto-register to the network
if (waitResponse() != 1) {
return false;
}
return restart();
}
TINY_GSM_MODEM_WAIT_FOR_NETWORK()
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
gprsDisconnect();
@ -551,131 +366,75 @@ public:
return false;
}
// Using CGDCONT sets up an "external" PCP context, i.e. a data connection
// using the external IP stack (e.g. Windows dial up) and PPP link over the
// serial interface. This is the only command set supported by the LTE-M
// and LTE NB-IoT modules (SARA-R4xx, SARA-N4xx)
// Setting up the PSD profile/PDP context with the UPSD commands sets up an
// "internal" PDP context, i.e. a data connection using the internal IP
// stack and related AT commands for sockets. This is what we're using for
// all of the other modules.
// stack and related AT commands for sockets.
if (isCatM) {
if (user && strlen(user) > 0) {
sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"'); // Set the authentication
waitResponse();
}
sendAT(GF("+UPSD=0,1,\""), apn, '"'); // Set APN for PSD profile 0
waitResponse();
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"'); // Define PDP context 1
if (user && strlen(user) > 0) {
sendAT(GF("+UPSD=0,2,\""), user, '"'); // Set user for PSD profile 0
waitResponse();
sendAT(GF("+CGACT=1,1")); // activate PDP profile/context 1
if (waitResponse(150000L) != 1) {
return false;
}
return true;
}
else {
sendAT(GF("+UPSD=0,1,\""), apn, '"'); // Set APN for PSD profile 0
waitResponse();
if (user && strlen(user) > 0) {
sendAT(GF("+UPSD=0,2,\""), user, '"'); // Set user for PSD profile 0
waitResponse();
}
if (pwd && strlen(pwd) > 0) {
sendAT(GF("+UPSD=0,3,\""), pwd, '"'); // Set password for PSD profile 0
waitResponse();
}
sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP on PSD profile 0
if (pwd && strlen(pwd) > 0) {
sendAT(GF("+UPSD=0,3,\""), pwd, '"'); // Set password for PSD profile 0
waitResponse();
sendAT(GF("+UPSDA=0,3")); // Activate the PDP context associated with profile 0
if (waitResponse(360000L) != 1) {
return false;
}
sendAT(GF("+UPSND=0,8")); // Activate PSD profile 0
if (waitResponse(GF(",8,1")) != 1) {
return false;
}
waitResponse();
return true;
}
}
bool gprsDisconnect() {
// LTE-M and NB-IoT modules do not support UPSx commands
if (isCatM) {
sendAT(GF("+CGACT=1,0")); // Deactivate PDP context 1
if (waitResponse(40000L) != 1) {
return false;
}
}
sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP on PSD profile 0
waitResponse();
else {
sendAT(GF("+UPSDA=0,4")); // Deactivate the PDP context associated with profile 0
if (waitResponse(360000L) != 1)
return false;
sendAT(GF("+UPSDA=0,3")); // Activate the PDP context associated with profile 0
if (waitResponse(360000L) != 1) {
return false;
}
sendAT(GF("+CGATT=0")); // detach from GPRS
if (waitResponse(360000L) != 1)
sendAT(GF("+UPSND=0,8")); // Activate PSD profile 0
if (waitResponse(GF(",8,1")) != 1) {
return false;
}
waitResponse();
return true;
}
bool isGprsConnected() {
sendAT(GF("+CGATT?"));
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
bool gprsDisconnect() {
sendAT(GF("+UPSDA=0,4")); // Deactivate the PDP context associated with profile 0
if (waitResponse(360000L) != 1) {
return false;
}
int res = stream.readStringUntil('\n').toInt();
waitResponse();
if (res != 1)
sendAT(GF("+CGATT=0")); // detach from GPRS
if (waitResponse(360000L) != 1) {
return false;
}
return localIP() != IPAddress(0,0,0,0);
return true;
}
TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED()
/*
* IP Address functions
*/
String getLocalIP() {
// LTE-M and NB-IoT modules do not support UPSx commands
if (isCatM) {
sendAT(GF("+CGPADDR"));
if (waitResponse(GF(GSM_NL "+CGPADDR:")) != 1) {
return "";
}
streamSkipUntil(','); // Skip context id
String res = stream.readStringUntil('\r');
if (waitResponse() != 1) {
return "";
}
return res;
sendAT(GF("+UPSND=0,0"));
if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) {
return "";
}
else {
sendAT(GF("+UPSND=0,0"));
if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) {
return "";
}
streamSkipUntil(','); // Skip PSD profile
streamSkipUntil('\"'); // Skip request type
String res = stream.readStringUntil('\"');
if (waitResponse() != 1) {
return "";
}
return res;
streamSkipUntil(','); // Skip PSD profile
streamSkipUntil('\"'); // Skip request type
String res = stream.readStringUntil('\"');
if (waitResponse() != 1) {
return "";
}
return res;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
@ -730,8 +489,9 @@ public:
}
/*
* Battery functions
* Battery & temperature functions
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int8_t getBattPercent() {
@ -745,13 +505,26 @@ public:
return res;
}
uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE;
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
percent = getBattPercent();
return true;
}
// This would only available for a small number of modules in this group (TOBY-L)
float getTemperature() TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t* mux, bool ssl = false) {
bool modemConnect(const char* host, uint16_t port, uint8_t* mux,
bool ssl = false, int timeout_s = 120)
{
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
sendAT(GF("+USOCR=6")); // create a socket
if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) { // reply is +USOCR: ## of socket created
return false;
@ -776,22 +549,23 @@ protected:
// TODO: Use faster "asynchronous" connection?
// We would have to wait for the +UUSOCO URC to verify connection
sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port);
int rsp = waitResponse(120000L);
int rsp = waitResponse(timeout_ms);
return (1 == rsp);
}
bool modemDisconnect(uint8_t mux) {
TINY_GSM_YIELD();
if (!modemGetConnected(mux)) return true;
if (isCatM) { // These modems allow a faster "asynchronous" close
sendAT(GF("+USOCL="), mux, GF(",1"));
int rsp = waitResponse(120000L);
return (1 == rsp); // but it still can take up to 120s to get a response
if (!modemGetConnected(mux)) {
sockets[mux]->sock_connected = false;
return true;
}
else { // no async close
sendAT(GF("+USOCL="), mux);
return (1 == waitResponse());
bool success;
sendAT(GF("+USOCL="), mux);
success = 1 == waitResponse(); // should return within 1s
if (success) {
sockets[mux]->sock_connected = false;
}
return success;
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
@ -808,8 +582,7 @@ protected:
}
streamSkipUntil(','); // Skip mux
int sent = stream.readStringUntil('\n').toInt();
waitResponse();
maintain(); // look for a very quick response from the remote
waitResponse(); // sends back OK after the confirmation of number sent
return sent;
}
@ -824,18 +597,16 @@ protected:
streamSkipUntil('\"');
for (size_t i=0; i<len; i++) {
while (!stream.available()) { TINY_GSM_YIELD(); }
char c = stream.read();
sockets[mux]->rx.put(c);
TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT
}
streamSkipUntil('\"');
waitResponse();
DBG("### READ:", len, "from", mux);
maintain(); // Listen for a close or other URC
return len;
}
size_t modemGetAvailable(uint8_t mux) {
// NOTE: Querying a closed socket gives an error "operation not allowed"
sendAT(GF("+USORD="), mux, ",0");
size_t result = 0;
uint8_t res = waitResponse(GF(GSM_NL "+USORD:"));
@ -844,17 +615,17 @@ protected:
if (res == 1) {
streamSkipUntil(','); // Skip mux
result = stream.readStringUntil('\n').toInt();
DBG("### DATA AVAILABLE:", result, "on", mux);
// if (result) DBG("### DATA AVAILABLE:", result, "on", mux);
waitResponse();
}
if (!result && res != 2 && res != 3) { // Don't check modemGetConnected after an error
sockets[mux]->sock_connected = modemGetConnected(mux);
}
maintain(); // Listen for a close or other URC
return result;
}
bool modemGetConnected(uint8_t mux) {
// NOTE: Querying a closed socket gives an error "operation not allowed"
sendAT(GF("+USOCTL="), mux, ",10");
uint8_t res = waitResponse(GF(GSM_NL "+USOCTL:"));
if (res != 1)
@ -876,7 +647,6 @@ protected:
// 9: the socket is in LAST_ACK status
// 10: the socket is in TIME_WAIT status
waitResponse();
maintain(); // Listen for a close or other URC
return (result != 0);
}
@ -886,16 +656,10 @@ public:
Utilities
*/
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
@ -937,17 +701,17 @@ public:
sockets[mux]->sock_available = len;
}
data = "";
DBG("### Got Data:", len, "on", mux);
DBG("### URC Data Received:", len, "on", mux);
} else if (data.endsWith(GF(GSM_NL "+UUSOCL:"))) {
int mux = stream.readStringUntil('\n').toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
DBG("### URC Sock Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
@ -960,12 +724,12 @@ finish:
return index;
}
uint8_t waitResponse(uint32_t timeout,
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=GFP(GSM_CME_ERROR), GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
@ -979,7 +743,6 @@ public:
protected:
GsmClient* sockets[TINY_GSM_MUX_COUNT];
bool isCatM;
};
#endif

+ 257
- 163
src/TinyGsmClientXBee.h View File

@ -24,6 +24,20 @@
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
// Use this to avoid too many entrances and exits from command mode.
// The cellular Bee's often freeze up and won't respond when attempting
// to enter command mode too many times.
#define XBEE_COMMAND_START_DECORATOR(nAttempts, failureReturn) \
bool wasInCommandMode = inCommandMode; \
if (!wasInCommandMode) { /* don't re-enter command mode if already in it */ \
if (!commandMode(nAttempts)) return failureReturn; /* Return immediately if fails */ \
}
#define XBEE_COMMAND_END_DECORATOR \
if (!wasInCommandMode) { /* only exit if we weren't in command mode */ \
exitCommand(); \
}
enum SimStatus {
SIM_ERROR = 0,
SIM_READY = 1,
@ -49,7 +63,7 @@ enum XBeeType {
};
class TinyGsmXBee : public TinyGsmModem
class TinyGsmXBee
{
public:
@ -77,31 +91,30 @@ public:
}
public:
// NOTE: The XBee saves all paramter information in flash. When you turn it
// on it immediately prepares to re-connect to whatever was last connected to.
// All the modemConnect() function does is tell it the paramters to put into
// flash. The connection itself is not opened until you attempt to send data.
// NOTE: The XBee saves all connection information (ssid/pwd or apn AND last used IP address)
// in flash (NVM). When you turn it on it immediately prepares to re-connect to whatever was
// last set. The TCP connection itself is not opened until you attempt to send data.
// Because all settings are saved to flash, it is possible (or likely) that
// you could send out data even if you haven't "made" any connection.
virtual int connect(const char *host, uint16_t port) {
// you could send data even if you haven't "made" any connection.
virtual int connect(const char *host, uint16_t port, int timeout_s) {
// NOTE: Not caling stop() or yeild() here
at->streamClear(); // Empty anything in the buffer before starting
if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
sock_connected = at->modemConnect(host, port, mux, false);
at->writeChanges();
at->exitCommand();
}
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
virtual int connect(const char *host, uint16_t port) {
return connect(host, port, 75);
}
virtual int connect(IPAddress ip, uint16_t port) {
virtual int connect(IPAddress ip, uint16_t port, int timeout_s) {
// NOTE: Not caling stop() or yeild() here
at->streamClear(); // Empty anything in the buffer before starting
if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
sock_connected = at->modemConnect(ip, port, mux, false);
at->writeChanges();
at->exitCommand();
}
sock_connected = at->modemConnect(ip, port, mux, timeout_s);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
return connect(ip, port, 75);
}
virtual void stop() {
at->streamClear(); // Empty anything in the buffer
@ -196,12 +209,6 @@ public:
if (available()) {
return true;
}
// Double check that we don't know it's closed
// NOTE: modemGetConnected() is likely to return a "false" true because
// it will return unknown until after data is sent over the connection.
// If the socket is definitely closed, modemGetConnected() will set
// sock_connected to false;
at->modemGetConnected();
return sock_connected;
}
virtual operator bool() { return connected(); }
@ -230,23 +237,17 @@ public:
{}
public:
virtual int connect(const char *host, uint16_t port) {
virtual int connect(const char *host, uint16_t port, int timeout_s) {
// NOTE: Not caling stop() or yeild() here
at->streamClear(); // Empty anything in the buffer before starting
if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
sock_connected = at->modemConnect(host, port, mux, true);
at->writeChanges();
at->exitCommand();
}
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port) {
virtual int connect(IPAddress ip, uint16_t port, int timeout_s) {
// NOTE: Not caling stop() or yeild() here
at->streamClear(); // Empty anything in the buffer before starting
if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
sock_connected = at->modemConnect(ip, port, mux, false);
at->writeChanges();
at->exitCommand();
}
sock_connected = at->modemConnect(ip, port, mux, timeout_s);
return sock_connected;
}
};
@ -255,24 +256,26 @@ public:
public:
TinyGsmXBee(Stream& stream)
: TinyGsmModem(stream), stream(stream)
: stream(stream)
{
beeType = XBEE_UNKNOWN; // Start not knowing what kind of bee it is
guardTime = TINY_GSM_XBEE_GUARD_TIME; // Start with the default guard time of 1 second
resetPin = -1;
savedIP = IPAddress(0,0,0,0);
savedHost = "";
inCommandMode = false;
memset(sockets, 0, sizeof(sockets));
}
TinyGsmXBee(Stream& stream, int8_t resetPin)
: TinyGsmModem(stream), stream(stream)
: stream(stream)
{
beeType = XBEE_UNKNOWN; // Start not knowing what kind of bee it is
guardTime = TINY_GSM_XBEE_GUARD_TIME; // Start with the default guard time of 1 second
this->resetPin = resetPin;
savedIP = IPAddress(0,0,0,0);
savedHost = "";
inCommandMode = false;
memset(sockets, 0, sizeof(sockets));
}
@ -280,6 +283,10 @@ public:
* Basic functions
*/
bool begin(const char* pin = NULL) {
return init(pin);
}
bool init(const char* pin = NULL) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
@ -288,20 +295,26 @@ public:
digitalWrite(resetPin, HIGH);
}
if (!commandMode(10)) return false; // Try up to 10 times for the init
XBEE_COMMAND_START_DECORATOR(10, false)
sendAT(GF("AP0")); // Put in transparent mode
bool ret_val = waitResponse() == 1;
ret_val &= writeChanges();
sendAT(GF("GT64")); // shorten the guard time to 100ms
ret_val &= waitResponse();
ret_val &= writeChanges();
ret_val &= waitResponse() == 1;
if (ret_val) guardTime = 110;
// Make sure the command mode drop-out time is long enough that we won't fall
// out of command mode without intentionally leaving it. This is the default
// drop out time of 0x64 x 100ms (10 seconds)
sendAT(GF("CT64"));
ret_val &= waitResponse() == 1;
ret_val &= writeChanges();
getSeries(); // Get the "Hardware Series";
exitCommand();
XBEE_COMMAND_END_DECORATOR
return ret_val;
}
@ -310,7 +323,7 @@ public:
}
void setBaud(unsigned long baud) {
if (!commandMode()) return;
XBEE_COMMAND_START_DECORATOR(5, )
switch(baud)
{
case 2400: sendAT(GF("BD1")); break;
@ -331,40 +344,48 @@ public:
}
waitResponse();
writeChanges();
exitCommand();
XBEE_COMMAND_END_DECORATOR
}
bool testAT(unsigned long timeout = 10000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (commandMode())
{
bool testAT(unsigned long timeout_ms = 10000L) {
unsigned long start = millis();
bool success = false;
while (!success && millis() - start < timeout_ms) {
if (!inCommandMode) {
success = commandMode();
if (success) exitCommand();
}
else {
sendAT();
if (waitResponse(200) == 1) {
exitCommand();
return true;
success = true;
}
// if we didn't respond to the AT, assume we're not in command mode
else inCommandMode = false;
}
delay(100);
delay(250);
}
return false;
return success;
}
void maintain() {
// this only happens OUTSIDE command mode, so if we're getting characters
// they should be data received from the TCP connection
// TINY_GSM_YIELD();
// while (stream.available()) {
// char c = stream.read();
// if (c > 0) sockets[0]->rx.put(c);
// if (!inCommandMode) {
// while (stream.available()) {
// char c = stream.read();
// if (c > 0) sockets[0]->rx.put(c);
// }
// }
}
bool factoryDefault() {
if (!commandMode()) return false; // Return immediately
XBEE_COMMAND_START_DECORATOR(5, false)
sendAT(GF("RE"));
bool ret_val = waitResponse() == 1;
ret_val &= writeChanges();
exitCommand();
XBEE_COMMAND_END_DECORATOR
// Make sure the guard time for the modem object is set back to default
// otherwise communication would fail after the reset
guardTime = 1010;
@ -372,14 +393,7 @@ public:
}
String getModemInfo() {
String modemInf = "";
if (!commandMode()) return modemInf; // Try up to 10 times for the init
sendAT(GF("HS")); // Get the "Hardware Series"
modemInf += readResponseString();
exitCommand();
return modemInf;
return sendATGetString(GF("HS"));
}
bool hasSSL() {
@ -428,7 +442,9 @@ public:
}
bool restart() {
if (!commandMode()) return false; // Return immediately
if (beeType == XBEE_UNKNOWN) getSeries(); // how we restart depends on this
if (beeType != XBEE_S6B_WIFI) {
@ -440,9 +456,10 @@ public:
sendAT(GF("FR"));
if (waitResponse() != 1) return exitAndFail();
else inCommandMode = false; // Reset effectively exits command mode
if (beeType == XBEE_S6B_WIFI) delay(2000); // Wifi module actually resets about 2 seconds later
else delay(100); // cellular modules wait 100ms before reset happes
else delay(100); // cellular modules wait 100ms before reset happens
// Wait until reboot complete and responds to command mode call again
for (unsigned long start = millis(); millis() - start < 60000L; ) {
@ -462,7 +479,7 @@ public:
}
void setupPinSleep(bool maintainAssociation = false) {
if (!commandMode()) return; // Return immediately
XBEE_COMMAND_START_DECORATOR(5, )
if (beeType == XBEE_UNKNOWN) getSeries(); // Command depends on series
@ -481,7 +498,7 @@ public:
}
writeChanges();
exitCommand();
XBEE_COMMAND_END_DECORATOR
}
bool poweroff() { // Not supported
@ -501,32 +518,27 @@ public:
}
String getSimCCID() {
if (!commandMode()) return ""; // Return immediately
sendAT(GF("S#"));
String res = readResponseString();
exitCommand();
return res;
return sendATGetString(GF("S#"));
}
String getIMEI() {
if (!commandMode()) return ""; // Return immediately
sendAT(GF("IM"));
String res = readResponseString();
exitCommand();
return res;
return sendATGetString(GF("IM"));
}
SimStatus getSimStatus(unsigned long timeout = 10000L) {
SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
return SIM_READY; // unsupported
}
RegStatus getRegistrationStatus() {
if (!commandMode()) return REG_UNKNOWN; // Return immediately
XBEE_COMMAND_START_DECORATOR(5, REG_UNKNOWN)
if (!inCommandMode) return REG_UNKNOWN; // Return immediately
if (beeType == XBEE_UNKNOWN) getSeries(); // Need to know the bee type to interpret response
sendAT(GF("AI"));
int16_t intRes = readResponseInt();
int16_t intRes = readResponseInt(10000L);
RegStatus stat = REG_UNKNOWN;
switch (beeType){
@ -598,16 +610,12 @@ public:
}
}
exitCommand();
XBEE_COMMAND_END_DECORATOR
return stat;
}
String getOperator() {
if (!commandMode()) return ""; // Return immediately
sendAT(GF("MN"));
String res = readResponseString();
exitCommand();
return res;
return sendATGetString(GF("MN"));
}
/*
@ -615,12 +623,17 @@ public:
*/
int16_t getSignalQuality() {
if (!commandMode()) return 0; // Return immediately
XBEE_COMMAND_START_DECORATOR(5, 0);
if (beeType == XBEE_UNKNOWN) getSeries(); // Need to know what type of bee so we know how to ask
if (beeType == XBEE_S6B_WIFI) sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity
else sendAT(GF("DB")); // ask for the cell strength in dBm
int16_t intRes = readResponseInt();
exitCommand();
XBEE_COMMAND_END_DECORATOR
if (beeType == XBEE3_LTEM_ATT && intRes == 105) intRes = 0; // tends to reply with "69" when signal is unknown
if (beeType == XBEE_S6B_WIFI) return -93 + intRes; // the maximum sensitivity is -93dBm
else return -1*intRes; // need to convert to negative number
@ -631,52 +644,60 @@ public:
return (s == REG_OK);
}
bool waitForNetwork(unsigned long timeout = 60000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
bool waitForNetwork(unsigned long timeout_ms = 60000L) {
bool retVal = false;
XBEE_COMMAND_START_DECORATOR(5, false)
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
if (isNetworkConnected()) {
return true;
retVal = true;
break;
}
delay(250); // per Neil H. - more stable with delay
}
return false;
XBEE_COMMAND_END_DECORATOR
return retVal;
}
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd) {
if (!commandMode()) return false; // return immediately
bool retVal = true;
XBEE_COMMAND_START_DECORATOR(5, false)
//nh For no pwd don't set setscurity or pwd
if (NULL == ssid ) return exitAndFail();
if (ssid == NULL) retVal = false;;
if (NULL != pwd)
if (pwd != NULL)
{
sendAT(GF("EE"), 2); // Set security to WPA2
if (waitResponse() != 1) return exitAndFail();
if (waitResponse() != 1) retVal = false;
sendAT(GF("PK"), pwd);
} else {
sendAT(GF("EE"), 0); // Set No security
}
if (waitResponse() != 1) return exitAndFail();
if (waitResponse() != 1) retVal = false;
sendAT(GF("ID"), ssid);
if (waitResponse() != 1) return exitAndFail();
if (waitResponse() != 1) retVal = false;
if (!writeChanges()) return exitAndFail();
exitCommand();
if (!writeChanges()) retVal = false;
return true;
XBEE_COMMAND_END_DECORATOR
return retVal;
}
bool networkDisconnect() {
if (!commandMode()) return false; // return immediately
XBEE_COMMAND_START_DECORATOR(5, false)
sendAT(GF("NR0")); // Do a network reset in order to disconnect
// NOTE: On wifi modules, using a network reset will not
// WARNING: On wifi modules, using a network reset will not
// allow the same ssid to re-join without rebooting the module.
int8_t res = (1 == waitResponse(5000));
writeChanges();
exitCommand();
XBEE_COMMAND_END_DECORATOR
return res;
}
@ -685,37 +706,42 @@ public:
*/
String getLocalIP() {
if (!commandMode()) return ""; // Return immediately
XBEE_COMMAND_START_DECORATOR(5, "")
sendAT(GF("MY"));
String IPaddr; IPaddr.reserve(16);
// wait for the response - this response can be very slow
IPaddr = readResponseString(30000);
exitCommand();
XBEE_COMMAND_END_DECORATOR
IPaddr.trim();
return IPaddr;
}
IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
}
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
if (!commandMode()) return false; // Return immediately
XBEE_COMMAND_START_DECORATOR(5, false)
sendAT(GF("AN"), apn); // Set the APN
waitResponse();
bool success = waitResponse() == 1;
writeChanges();
exitCommand();
return true;
XBEE_COMMAND_END_DECORATOR
return success;
}
bool gprsDisconnect() {
if (!commandMode()) return false; // return immediately
XBEE_COMMAND_START_DECORATOR(5, false)
sendAT(GF("AM1")); // Cheating and disconnecting by turning on airplane mode
int8_t res = (1 == waitResponse(5000));
writeChanges();
sendAT(GF("AM0")); // Airplane mode off
waitResponse(5000);
writeChanges();
exitCommand();
XBEE_COMMAND_END_DECORATOR
return res;
}
@ -738,11 +764,14 @@ public:
if (waitResponse() !=1) return exitAndFail();
sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriage return)
if (waitResponse() !=1) return exitAndFail();
if (!writeChanges()) return exitAndFail();
if (!writeChanges()) return exitAndFail();
// Get out of command mode to actually send the text
exitCommand();
streamWrite(text);
stream.write((char)0x0D); // close off with the carriage return
return true;
}
@ -753,12 +782,25 @@ public:
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery functions
* Battery & temperature functions
*/
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE;
bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) TINY_GSM_ATTR_NOT_AVAILABLE;
float getTemperature() {
String res = sendATGetString(GF("TP"));
if (res == "") {
return (float)-9999;
}
char buf[5] = {0,};
res.toCharArray(buf, 5);
int8_t intRes = (int8_t)strtol(buf, 0, 16); // degrees Celsius displayed in 8-bit two's complement format.
return (float)intRes;
}
/*
* Client related functions
@ -766,51 +808,66 @@ public:
protected:
IPAddress getHostIP(const char* host) {
IPAddress getHostIP(const char* host, int timeout_s = 45) {
String strIP; strIP.reserve(16);
unsigned long startMillis = millis();
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
bool gotIP = false;
XBEE_COMMAND_START_DECORATOR(5, IPAddress(0,0,0,0))
// XBee's require a numeric IP address for connection, but do provide the
// functionality to look up the IP address from a fully qualified domain name
while (millis() - startMillis < 45000L) // the lookup can take a while
while ((millis() - startMillis) < timeout_ms) // the lookup can take a while
{
sendAT(GF("LA"), host);
while (stream.available() < 4 && (millis() - startMillis < 45000L)) {}; // wait for any response
while (stream.available() < 4 && (millis() - startMillis < timeout_ms)) {TINY_GSM_YIELD()};
strIP = stream.readStringUntil('\r'); // read result
strIP.trim();
if (!strIP.endsWith(GF("ERROR"))) {
if (strIP != "" && strIP != GF("ERROR")) {
gotIP = true;
break;
}
delay(2500); // wait a bit before trying again
}
if (gotIP) { // No reason to continue if we don't know the IP address
XBEE_COMMAND_END_DECORATOR
if (gotIP) {
return TinyGsmIpFromString(strIP);
}
else return IPAddress(0,0,0,0);
}
bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false) {
// If requested host is the same as the previous one and we already
// have a valid IP address, we don't have to do anything.
if (this->savedHost == String(host) && savedIP != IPAddress(0,0,0,0)) {
return true;
bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0,
bool ssl = false, int timeout_s = 75)
{
unsigned long startMillis = millis();
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
bool retVal = false;
XBEE_COMMAND_START_DECORATOR(5, false)
// If it's a new host or we dont' have a good IP, we need to do a DNS
// search for the IP to connect to
if (this->savedHost != String(host) || savedIP == IPAddress(0,0,0,0)) {
this->savedHost = String(host);
savedIP = getHostIP(host, timeout_s); // This will return 0.0.0.0 if lookup fails
}
// Otherwise, set the new host and mark the IP as invalid
this->savedHost = String(host);
savedIP = getHostIP(host); // This will return 0.0.0.0 if lookup fails
// If we now have a valid IP address, use it to connect
if (savedIP != IPAddress(0,0,0,0)) { // Only re-set connection information if we have an IP address
return modemConnect(savedIP, port, mux, ssl);
retVal = modemConnect(savedIP, port, mux, ssl, timeout_ms - (millis() - startMillis));
}
else return false;
XBEE_COMMAND_END_DECORATOR
return retVal;
}
bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false) {
bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false, int timeout_s = 75) {
savedIP = ip; // Set the newly requested IP address
bool success = true;
uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
XBEE_COMMAND_START_DECORATOR(5, false)
String host; host.reserve(16);
host += ip[0];
host += ".";
@ -819,6 +876,7 @@ protected:
host += ip[2];
host += ".";
host += ip[3];
if (ssl) {
sendAT(GF("IP"), 4); // Put in SSL over TCP communication mode
success &= (1 == waitResponse());
@ -826,10 +884,21 @@ protected:
sendAT(GF("IP"), 1); // Put in TCP mode
success &= (1 == waitResponse());
}
sendAT(GF("DL"), host); // Set the "Destination Address Low"
success &= (1 == waitResponse());
sendAT(GF("DE"), String(port, HEX)); // Set the destination port
success &= (1 == waitResponse());
for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
if (modemGetConnected()) {
sockets[mux]->sock_connected = true;
break;
}
}
XBEE_COMMAND_END_DECORATOR
return success;
}
@ -844,36 +913,37 @@ protected:
// really be open, but no data has yet been sent. We return this unknown value
// as true so there's a possibility it's wrong.
bool modemGetConnected() {
if (!commandMode()) return false; // Return immediately
// If the IP address is 0, it's not valid so we can't be connected
if (savedIP == IPAddress(0,0,0,0)) return false;
XBEE_COMMAND_START_DECORATOR(5, false)
// Verify that we're connected to the *right* IP address
// We might be connected - but to the wrong thing
// NOTE: In transparent mode, there is only one connection possible - no multiplex
String strIP; strIP.reserve(16);
sendAT(GF("DL"));
strIP = stream.readStringUntil('\r'); // read result
if (TinyGsmIpFromString(strIP) != savedIP) return exitAndFail();
// String strIP; strIP.reserve(16);
// sendAT(GF("DL"));
// strIP = stream.readStringUntil('\r'); // read result
// if (TinyGsmIpFromString(strIP) != savedIP) return exitAndFail();
if (beeType == XBEE_UNKNOWN) getSeries(); // Need to know the bee type to interpret response
switch (beeType){ // The wifi be can only say if it's connected to the netowrk
case XBEE_S6B_WIFI: {
RegStatus s = getRegistrationStatus();
XBEE_COMMAND_END_DECORATOR
if (s != REG_OK) {
sockets[0]->sock_connected = false;
sockets[0]->sock_connected = false; // no multiplex
}
return (s == REG_OK); // if it's connected, we hope the sockets are too
}
default: { // Cellular XBee's
sendAT(GF("CI"));
int16_t intRes = readResponseInt();
exitCommand();
XBEE_COMMAND_END_DECORATOR
switch(intRes) {
case 0x00: // 0x00 = The socket is definitely open
case 0x28: // 0x28 = "Unknown."
case 0xFF: // 0xFF = No known status - this is always returned prior to sending data
return true;
case 0x02: // 0x02 = Invalid parameters (bad IP/host)
@ -901,19 +971,13 @@ public:
}
}
template<typename... Args>
void sendAT(Args... cmd) {
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
TINY_GSM_MODEM_STREAM_UTILITIES()
// TODO: Optimize this!
// NOTE: This function is used while INSIDE command mode, so we're only
// waiting for requested responses. The XBee has no unsoliliced responses
// (URC's) when in command mode.
uint8_t waitResponse(uint32_t timeout, String& data,
uint8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
@ -949,7 +1013,7 @@ public:
goto finish;
}
}
} while (millis() - startMillis < timeout);
} while (millis() - startMillis < timeout_ms);
finish:
if (!index) {
data.trim();
@ -971,12 +1035,12 @@ finish:
return index;
}
uint8_t waitResponse(uint32_t timeout,
uint8_t waitResponse(uint32_t timeout_ms,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
@ -985,28 +1049,39 @@ finish:
return waitResponse(1000, r1, r2, r3, r4, r5);
}
bool commandMode(uint8_t retries = 3) {
bool commandMode(uint8_t retries = 5) {
// If we're already in command mode, move on
if (inCommandMode && (millis() - lastCommandModeMillis) < 10000L) return true;
uint8_t triesMade = 0;
uint8_t triesUntilReset = 2; // only reset after 2 failures
uint8_t triesUntilReset = 4; // only reset after 4 failures
bool success = false;
streamClear(); // Empty everything in the buffer before starting
while (!success and triesMade < retries) {
// Cannot send anything for 1 "guard time" before entering command mode
// Default guard time is 1s, but the init fxn decreases it to 100 ms
delay(guardTime + 50);
delay(guardTime + 10);
streamWrite(GF("+++")); // enter command mode
int res = waitResponse(guardTime*2);
success = (1 == res);
if (0 == res) {
triesUntilReset--;
if (triesUntilReset == 0) {
triesUntilReset = 2;
triesUntilReset = 4;
pinReset(); // if it's unresponsive, reset
delay(250); // a short delay to allow it to come back up TODO-optimize this
delay(250); // a short delay to allow it to come back up
// TODO-optimize this
}
}
triesMade ++;
}
if (success) {
inCommandMode = true;
lastCommandModeMillis = millis();
}
return success;
}
@ -1019,8 +1094,11 @@ finish:
}
void exitCommand(void) {
// NOTE: Here we explicitely try to exit command mode
// even if the internal flag inCommandMode was already false
sendAT(GF("CN")); // Exit command mode
waitResponse();
inCommandMode = false;
}
bool exitAndFail(void) {
@ -1035,23 +1113,37 @@ finish:
DBG(GF("### Modem: "), getModemName());
}
String readResponseString(uint32_t timeout = 1000) {
String readResponseString(uint32_t timeout_ms = 1000) {
TINY_GSM_YIELD();
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < timeout) {};
while (!stream.available() && millis() - startMillis < timeout_ms) {};
String res = stream.readStringUntil('\r'); // lines end with carriage returns
res.trim();
return res;
}
int16_t readResponseInt(uint32_t timeout = 1000) {
String res = readResponseString(timeout); // it just works better reading a string first
int16_t readResponseInt(uint32_t timeout_ms = 1000) {
String res = readResponseString(timeout_ms); // it just works better reading a string first
if (res == "") res = "FF";
char buf[5] = {0,};
res.toCharArray(buf, 5);
int16_t intRes = strtol(buf, 0, 16);
return intRes;
}
String sendATGetString(GsmConstStr cmd) {
XBEE_COMMAND_START_DECORATOR(5, "")
sendAT(cmd);
String res = readResponseString();
XBEE_COMMAND_END_DECORATOR
return res;
}
bool gotIPforSavedHost() {
if (savedHost != "" && savedIP != IPAddress(0,0,0,0)) return true;
else return false;
}
public:
Stream& stream;
@ -1061,6 +1153,8 @@ protected:
XBeeType beeType;
IPAddress savedIP;
String savedHost;
bool inCommandMode;
uint32_t lastCommandModeMillis;
GsmClient* sockets[TINY_GSM_MUX_COUNT];
};


+ 397
- 112
src/TinyGsmCommon.h View File

@ -10,7 +10,7 @@
#define TinyGsmCommon_h
// The current library version number
#define TINYGSM_VERSION "0.6.2"
#define TINYGSM_VERSION "0.7.4"
#if defined(SPARK) || defined(PARTICLE)
#include "Particle.h"
@ -34,10 +34,6 @@
#define TINY_GSM_YIELD() { delay(0); }
#endif
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 256
#endif
#define TINY_GSM_ATTR_NOT_AVAILABLE __attribute__((error("Not available on this modem type")))
#define TINY_GSM_ATTR_NOT_IMPLEMENTED __attribute__((error("Not implemented")))
@ -203,136 +199,425 @@ String TinyGsmDecodeHex16bit(String &instr) {
}
// Connect to a IP address given as an IPAddress object by
// converting said IP address to text
#define TINY_GSM_CLIENT_CONNECT_OVERLOADS() \
virtual int connect(IPAddress ip, uint16_t port, int timeout_s) { \
String host; host.reserve(16); \
host += ip[0]; \
host += "."; \
host += ip[1]; \
host += "."; \
host += ip[2]; \
host += "."; \
host += ip[3]; \
return connect(host.c_str(), port, timeout_s); \
} \
virtual int connect(const char *host, uint16_t port) { \
return connect(host, port, 75); \
} \
virtual int connect(IPAddress ip, uint16_t port) { \
return connect(ip, port, 75); \
}
class TinyGsmModem
{
// Writes data out on the client using the modem send functionality
#define TINY_GSM_CLIENT_WRITE() \
virtual size_t write(const uint8_t *buf, size_t size) { \
TINY_GSM_YIELD(); \
at->maintain(); \
return at->modemSend(buf, size, mux); \
} \
\
virtual size_t write(uint8_t c) {\
return write(&c, 1); \
}\
\
virtual size_t write(const char *str) { \
if (str == NULL) return 0; \
return write((const uint8_t *)str, strlen(str)); \
}
public:
TinyGsmModem(Stream& stream)
: stream(stream)
{}
// Returns the combined number of characters available in the TinyGSM fifo
// and the modem chips internal fifo, doing an extra check-in with the
// modem to see if anything has arrived without a UURC.
#define TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() \
virtual int available() { \
TINY_GSM_YIELD(); \
if (!rx.size()) { \
/* Workaround: sometimes module forgets to notify about data arrival.
TODO: Currently we ping the module periodically,
but maybe there's a better indicator that we need to poll */ \
if (millis() - prev_check > 250) { \
got_data = true; \
prev_check = millis(); \
} \
at->maintain(); \
} \
return rx.size() + sock_available; \
}
/*
* Basic functions
*/
// Prepare the modem for further functionality
virtual bool init(const char* pin = NULL) = 0;
// Begin is redundant with init
virtual bool begin(const char* pin = NULL) {
return init(pin);
// Returns the combined number of characters available in the TinyGSM fifo and
// the modem chips internal fifo. Use this if you don't expect to miss any URC's.
#define TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() \
virtual int available() { \
TINY_GSM_YIELD(); \
if (!rx.size()) { \
at->maintain(); \
} \
return rx.size() + sock_available; \
}
// Returns a string with the chip name
virtual String getModemName() = 0;
// Sets the serial communication baud rate
virtual void setBaud(unsigned long baud) = 0;
// Checks that the modem is responding to standard AT commands
virtual bool testAT(unsigned long timeout = 10000L) = 0;
// Holds open communication with the modem waiting for data to come in
virtual void maintain() = 0;
// Resets all modem chip settings to factor defaults
virtual bool factoryDefault() = 0;
// Returns the response to a get info request. The format varies by modem.
virtual String getModemInfo() = 0;
// Answers whether types of communication are available on this modem
virtual bool hasSSL() = 0;
virtual bool hasWifi() = 0;
virtual bool hasGPRS() = 0;
/*
* Power functions
*/
virtual bool restart() = 0;
virtual bool poweroff() = 0;
/*
* SIM card functions - only apply to cellular modems
*/
virtual bool simUnlock(const char *pin) { return false; }
virtual String getSimCCID() { return ""; }
virtual String getIMEI() { return ""; }
virtual String getOperator() { return ""; }
/*
* Generic network functions
*/
virtual int16_t getSignalQuality() = 0;
// NOTE: this returns whether the modem is registered on the cellular or WiFi
// network NOT whether GPRS or other internet connections are available
virtual bool isNetworkConnected() = 0;
virtual bool waitForNetwork(unsigned long timeout = 60000L) {
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
}
return false;
// Returns the number of characters avaialable in the TinyGSM fifo
// Assumes the modem chip has no internal fifo
#define TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() \
virtual int available() { \
TINY_GSM_YIELD(); \
if (!rx.size() && sock_connected) { \
at->maintain(); \
} \
return rx.size(); \
}
/*
* WiFi functions - only apply to WiFi modems
*/
virtual bool networkConnect(const char* ssid, const char* pwd) { return false; }
virtual bool networkDisconnect() { return false; }
#define TINY_GSM_CLIENT_READ_OVERLOAD() \
virtual int read() { \
uint8_t c; \
if (read(&c, 1) == 1) { \
return c; \
} \
return -1; \
}
/*
* GPRS functions - only apply to cellular modems
*/
// Reads characters out of the TinyGSM fifo, and from the modem chips internal
// fifo if avaiable, also double checking with the modem if data has arrived
// without issuing a UURC.
#define TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() \
virtual int read(uint8_t *buf, size_t size) { \
TINY_GSM_YIELD(); \
at->maintain(); \
size_t cnt = 0; \
while (cnt < size) { \
size_t chunk = TinyGsmMin(size-cnt, rx.size()); \
if (chunk > 0) { \
rx.get(buf, chunk); \
buf += chunk; \
cnt += chunk; \
continue; \
} \
/* Workaround: sometimes module forgets to notify about data arrival.
TODO: Currently we ping the module periodically,
but maybe there's a better indicator that we need to poll */ \
if (millis() - prev_check > 250) { \
got_data = true; \
prev_check = millis(); \
} \
/* TODO: Read directly into user buffer? */ \
at->maintain(); \
if (sock_available > 0) { \
int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); \
if (n == 0) break; \
} else { \
break; \
} \
} \
return cnt; \
} \
TINY_GSM_CLIENT_READ_OVERLOAD()
// Reads characters out of the TinyGSM fifo, and from the modem chips internal
// fifo if avaiable. Use this if you don't expect to miss any URC's.
#define TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() \
virtual int read(uint8_t *buf, size_t size) { \
TINY_GSM_YIELD(); \
at->maintain(); \
size_t cnt = 0; \
while (cnt < size) { \
size_t chunk = TinyGsmMin(size-cnt, rx.size()); \
if (chunk > 0) { \
rx.get(buf, chunk); \
buf += chunk; \
cnt += chunk; \
continue; \
} \
/* TODO: Read directly into user buffer? */ \
at->maintain(); \
if (sock_available > 0) { \
int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); \
if (n == 0) break; \
} else { \
break; \
} \
} \
return cnt; \
} \
TINY_GSM_CLIENT_READ_OVERLOAD()
// Reads characters out of the TinyGSM fifo, waiting for any URC's from the
// modem for new data if there's nothing in the fifo. This assumes the
//modem chip itself has no fifo.
#define TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() \
virtual int read(uint8_t *buf, size_t size) { \
TINY_GSM_YIELD(); \
size_t cnt = 0; \
uint32_t _startMillis = millis(); \
while (cnt < size && millis() - _startMillis < _timeout) { \
size_t chunk = TinyGsmMin(size-cnt, rx.size()); \
if (chunk > 0) { \
rx.get(buf, chunk); \
buf += chunk; \
cnt += chunk; \
continue; \
} \
/* TODO: Read directly into user buffer? */ \
if (!rx.size() && sock_connected) { \
at->maintain(); \
} \
} \
return cnt; \
} \
\
virtual int read() { \
uint8_t c; \
if (read(&c, 1) == 1) { \
return c; \
} \
return -1; \
}
virtual bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
return false;
// The peek, flush, and connected functions
#define TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() \
virtual int peek() { return -1; } /* TODO */ \
\
virtual void flush() { at->stream.flush(); } \
\
virtual uint8_t connected() { \
if (available()) { \
return true; \
} \
return sock_connected; \
} \
virtual operator bool() { return connected(); }
// Set baud rate via the V.25TER standard IPR command
#define TINY_GSM_MODEM_SET_BAUD_IPR() \
void setBaud(unsigned long baud) { \
sendAT(GF("+IPR="), baud); \
}
virtual bool gprsDisconnect() { return false; }
virtual bool isGprsConnected() { return false; }
/*
* IP Address functions
*/
virtual String getLocalIP() = 0;
virtual IPAddress localIP() {
return TinyGsmIpFromString(getLocalIP());
// Test response to AT commands
#define TINY_GSM_MODEM_TEST_AT() \
bool testAT(unsigned long timeout_ms = 10000L) { \
for (unsigned long start = millis(); millis() - start < timeout_ms; ) { \
sendAT(GF("")); \
if (waitResponse(200) == 1) return true; \
delay(100); \
} \
return false; \
}
/*
Utilities
*/
template<typename T>
void streamWrite(T last) {
stream.print(last);
}
// Keeps listening for modem URC's and iterates through sockets
// to see if any data is avaiable
#define TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() \
void maintain() { \
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { \
GsmClient* sock = sockets[mux]; \
if (sock && sock->got_data) { \
sock->got_data = false; \
sock->sock_available = modemGetAvailable(mux); \
} \
} \
while (stream.available()) { \
waitResponse(15, NULL, NULL); \
} \
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail) {
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(const char c, const unsigned long timeout = 1000L) {
unsigned long startMillis = millis();
while (millis() - startMillis < timeout) {
while (millis() - startMillis < timeout && !stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c)
return true;
}
return false;
}
// Keeps listening for modem URC's - doesn't check socks because
// modem has no internal fifo
#define TINY_GSM_MODEM_MAINTAIN_LISTEN() \
void maintain() { \
waitResponse(10, NULL, NULL); \
}
// Asks for modem information via the V.25TER standard ATI command
// NOTE: The actual value and style of the response is quite varied
#define TINY_GSM_MODEM_GET_INFO_ATI() \
String getModemInfo() { \
sendAT(GF("I")); \
String res; \
if (waitResponse(1000L, res) != 1) { \
return ""; \
} \
res.replace(GSM_NL "OK" GSM_NL, ""); \
res.replace(GSM_NL, " "); \
res.trim(); \
return res; \
}
// Unlocks a sim via the 3GPP TS command AT+CPIN
#define TINY_GSM_MODEM_SIM_UNLOCK_CPIN() \
bool simUnlock(const char *pin) { \
sendAT(GF("+CPIN=\""), pin, GF("\"")); \
return waitResponse() == 1; \
}
// Gets the CCID of a sim card via AT+CCID
#define TINY_GSM_MODEM_GET_SIMCCID_CCID() \
String getSimCCID() { \
sendAT(GF("+CCID")); \
if (waitResponse(GF(GSM_NL "+CCID:")) != 1) { \
return ""; \
} \
String res = stream.readStringUntil('\n'); \
waitResponse(); \
res.trim(); \
return res; \
}
// Asks for TA Serial Number Identification (IMEI) via the V.25TER standard AT+GSN command
#define TINY_GSM_MODEM_GET_IMEI_GSN() \
String getIMEI() { \
sendAT(GF("+GSN")); \
if (waitResponse(GF(GSM_NL)) != 1) { \
return ""; \
} \
String res = stream.readStringUntil('\n'); \
waitResponse(); \
res.trim(); \
return res; \
}
// Gets the modem's registration status via CREG/CGREG/CEREG
// CREG = Generic network registration
// CGREG = GPRS service registration
// CEREG = EPS registration for LTE modules
#define TINY_GSM_MODEM_GET_REGISTRATION_XREG(regCommand) \
RegStatus getRegistrationStatus() { \
sendAT(GF("+" #regCommand "?")); \
if (waitResponse(GF(GSM_NL "+" #regCommand ":")) != 1) { \
return REG_UNKNOWN; \
} \
streamSkipUntil(','); /* Skip format (0) */ \
int status = stream.readStringUntil('\n').toInt(); \
waitResponse(); \
return (RegStatus)status; \
}
public:
Stream& stream;
};
// Gets the current network operator via the 3GPP TS command AT+COPS
#define TINY_GSM_MODEM_GET_OPERATOR_COPS() \
String getOperator() { \
sendAT(GF("+COPS?")); \
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { \
return ""; \
} \
streamSkipUntil('"'); /* Skip mode and format */ \
String res = stream.readStringUntil('"'); \
waitResponse(); \
return res; \
}
// Waits for network attachment
#define TINY_GSM_MODEM_WAIT_FOR_NETWORK() \
bool waitForNetwork(unsigned long timeout_ms = 60000L) { \
for (unsigned long start = millis(); millis() - start < timeout_ms; ) { \
if (isNetworkConnected()) { \
return true; \
} \
delay(250); \
} \
return false; \
}
// Checks if current attached to GPRS/EPS service
#define TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() \
bool isGprsConnected() { \
sendAT(GF("+CGATT?")); \
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { \
return false; \
} \
int res = stream.readStringUntil('\n').toInt(); \
waitResponse(); \
if (res != 1) \
return false; \
\
return localIP() != IPAddress(0,0,0,0); \
}
// Gets signal quality report according to 3GPP TS command AT+CSQ
#define TINY_GSM_MODEM_GET_CSQ() \
int16_t getSignalQuality() { \
sendAT(GF("+CSQ")); \
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { \
return 99; \
} \
int res = stream.readStringUntil(',').toInt(); \
waitResponse(); \
return res; \
}
// Yields up to a time-out period and then reads a character from the stream into the mux FIFO
// TODO: Do we need to wait two _timeout periods for no character return? Will wait once in the first
// "while !stream.available()" and then will wait again in the stream.read() function.
#define TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT \
uint32_t startMillis = millis(); \
while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } \
char c = stream.read(); \
sockets[mux]->rx.put(c);
// Utility templates for writing/skipping characters on a stream
#define TINY_GSM_MODEM_STREAM_UTILITIES() \
template<typename T> \
void streamWrite(T last) { \
stream.print(last); \
} \
\
template<typename T, typename... Args> \
void streamWrite(T head, Args... tail) { \
stream.print(head); \
streamWrite(tail...); \
} \
\
template<typename... Args> \
void sendAT(Args... cmd) { \
streamWrite("AT", cmd..., GSM_NL); \
stream.flush(); \
TINY_GSM_YIELD(); \
/* DBG("### AT:", cmd...); */ \
} \
\
bool streamSkipUntil(const char c, const unsigned long timeout_ms = 1000L) { \
unsigned long startMillis = millis(); \
while (millis() - startMillis < timeout_ms) { \
while (millis() - startMillis < timeout_ms && !stream.available()) { \
TINY_GSM_YIELD(); \
} \
if (stream.read() == c) { \
return true; \
} \
} \
return false; \
}
#endif

Loading…
Cancel
Save