first commit

This commit is contained in:
Alejandro Guerrero 2026-09-27 17:21:39 +02:00
commit e6a42e28e9
4355 changed files with 4626605 additions and 0 deletions

View file

@ -0,0 +1,60 @@
# ChangeLog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and its stricter, better defined, brother [Common Changelog](https://common-changelog.org/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
***
## [Unreleased]
****
### Changed
### Added
### Removed
### Fixed
***
## [v0.12.0]
### Changed
- Moved bulk of waitResponse function to modem template and gave modems handleURCs fxn
- Moved option in waitResponse for verbose outputs.
- setBaud now returns a bool
- Replace new line consts with defines and renamed to AT_NL
- Renamed all RegStatus enums to be unique
- Replaced `NULL` with `nullptr` and replaced c-style casts.
- Moved setCertificates function and the certificate name matrix to the SSL template.
- Changed inputs for (unimplemented) SSL certificate functions.
- All modems will now return the pre-defined manufacturer and model in the name if the function to get the internal name fails.
- Cleaned up code for getting modem names.
- Made battery return types signed.
### Added
- Added support for SSL for the Quentcel BG95 and BG96 from [Aurelien BOUIN](https://github.com/aurelihein) and [George O'Connor](https://github.com/georgeman93)
- Added support for UBLOX SARA-R5 from [Sebastian Bergner](https://github.com/sebastianbergner)
- Added support for SIMCOM A7672X from [Giovanni de Rosso Unruh](https://github.com/giovannirosso)
- Added SIM5320 GPS location from [Bengarman](https://github.com/Bengarman)
- Added functions `getModemSerialNumber`, `getModemModel`, and `getModemRevision`.
- Added deep debugging option
- Added documentation to the FIFO class
### Removed
- Removed non-functional factory reset from SIM70xx series
### Fixed
- Removed extra wait on SIM7000 from [Mikael Fredriksson](https://github.com/Gelemikke)
- Fix status returns on ESP8266/ESP32 AT commands
- Fix length of HEX for Sequans Monarch
- Fix SIM7600 password then user when cid is set from [github0013](https://github.com/github0013)
- Fix cardinal points in location by gps for SIM7600 from [Juxn3](https://github.com/Juxn3)
- Fix NTP server sync for SIM70xx models from [Gonzalo Brusco](https://github.com/gonzabrusco)
- Fixed SIM70xx inheritance
***

165
libraries/TinyGSM/LICENSE Normal file
View file

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View file

@ -0,0 +1,9 @@
.PHONY: travis-build
travis-build:
ifdef PLATFORMIO_CI_ARGS
platformio ci --lib="." $(PLATFORMIO_CI_ARGS)
else
platformio ci --lib="." --board=leonardo
endif

421
libraries/TinyGSM/README.md Normal file
View file

@ -0,0 +1,421 @@
[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct.svg)](https://vshymanskyy.github.io/StandWithUkraine)
![TinyGSM logo](https://cdn.rawgit.com/vshymanskyy/TinyGSM/d18e93dc51fe988a0b175aac647185457ef640b5/extras/logo.svg)
A small Arduino library for GSM modules, that just works.
<!---
[![GitHub download](https://img.shields.io/github/downloads/vshymanskyy/TinyGSM/total.svg)](https://github.com/vshymanskyy/TinyGSM/releases/latest)--->
[![GitHub version](https://img.shields.io/github/release/vshymanskyy/TinyGSM.svg)](https://github.com/vshymanskyy/TinyGSM/releases/latest)
[![Build status](https://img.shields.io/travis/vshymanskyy/TinyGSM.svg)](https://travis-ci.org/vshymanskyy/TinyGSM)
[![GitHub issues](https://img.shields.io/github/issues/vshymanskyy/TinyGSM.svg)](https://github.com/vshymanskyy/TinyGSM/issues)
[![GitHub wiki](https://img.shields.io/badge/Wiki-available-brightgreen.svg)](https://github.com/vshymanskyy/TinyGSM/wiki)
[![GitHub stars](https://img.shields.io/github/stars/vshymanskyy/TinyGSM.svg)](https://github.com/vshymanskyy/TinyGSM/stargazers)
[![License](https://img.shields.io/badge/license-LGPL3-blue.svg)](https://github.com/vshymanskyy/TinyGSM/blob/master/LICENSE)
If you like **TinyGSM** - give it a star, or fork it and contribute!
[![GitHub stars](https://img.shields.io/github/stars/vshymanskyy/TinyGSM.svg?style=social&label=Star)](https://github.com/vshymanskyy/TinyGSM/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/vshymanskyy/TinyGSM.svg?style=social&label=Fork)](https://github.com/vshymanskyy/TinyGSM/network)
You can also join our chat:
[![Gitter](https://img.shields.io/gitter/room/vshymanskyy/TinyGSM.svg)](https://app.gitter.im/#/room/#tinygsm_Lobby:gitter.im)
- [Supported modems](#supported-modems)
- [Supported boards/modules](#supported-boardsmodules)
- [Features](#features)
- [Getting Started](#getting-started)
- [First Steps](#first-steps)
- [Writing your own code](#writing-your-own-code)
- [If you have any issues](#if-you-have-any-issues)
- [How does it work?](#how-does-it-work)
- [API Reference](#api-reference)
- [Troubleshooting](#troubleshooting)
- [Ensure stable data \& power connection](#ensure-stable-data--power-connection)
- [Baud rates](#baud-rates)
- [Broken initial configuration](#broken-initial-configuration)
- [Failed connection or no data received](#failed-connection-or-no-data-received)
- [Diagnostics sketch](#diagnostics-sketch)
- [Web request formatting problems - "but it works with PostMan"](#web-request-formatting-problems---but-it-works-with-postman)
- [SoftwareSerial problems](#softwareserial-problems)
- [ESP32 Notes](#esp32-notes)
- [HardwareSerial](#hardwareserial)
- [HttpClient](#httpclient)
- [SAMD21](#samd21)
- [Goouuu Tech IOT-GA6 vs AI-Thinker A6 confusion](#goouuu-tech-iot-ga6-vs-ai-thinker-a6-confusion)
- [SIM800 and SSL](#sim800-and-ssl)
- [Which version of the SIM7000 code to use](#which-version-of-the-sim7000-code-to-use)
- [License](#license)
### Arduino Client interface support
This library is easy to integrate with lots of sketches which use Ethernet or WiFi.
**PubSubClient ([MQTT](http://mqtt.org/))**, **[Blynk](http://blynk.cc)**, **HTTP Client** and **File Download** examples are provided.
![examples](/extras/examples.png)
### TinyGSM is tiny
The complete WebClient example for Arduino Uno (via Software Serial) takes little resources:
```
Sketch uses 15022 bytes (46%) of program storage space. Maximum is 32256 bytes.
Global variables use 574 bytes (28%) of dynamic memory, leaving 1474 bytes for local variables. Maximum is 2048 bytes.
```
Arduino GSM library uses 15868 bytes (49%) of Flash and 1113 bytes (54%) of RAM in a similar scenario.
TinyGSM also pulls data gently from the modem (whenever possible), so it can operate on very little RAM.
**Now, you have more space for your experiments.**
## Supported modems
- SIMCom SIM800 series (SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868)
- SIMCom SIM900 series (SIM900A, SIM900D, SIM908, SIM968)
- SIMCom WCDMA/HSPA/HSPA+ Modules (SIM5360, SIM5320, SIM5300E, SIM5300E/A)
- SIMCom LTE Modules (SIM7100E, SIM7500E, SIM7500A, SIM7600C, SIM7600E)
- SIMCom SIM7000E/A/G CAT-M1/NB-IoT Module
- SIMCom SIM7070/SIM7080/SIM7090 CAT-M1/NB-IoT Module
- SIMCom A7672X CAT-M1 Module
- AI-Thinker A6, A6C, A7, A20
- ESP8266/ESP32 (AT commands interface, similar to GSM modems)
- Digi XBee WiFi and Cellular (using XBee command mode)
- Neoway M590
- u-blox 2G, 3G, 4G, and LTE Cat1 Cellular Modems (many modules including LEON-G100, LISA-U2xx, SARA-G3xx, SARA-U2xx, TOBY-L2xx, LARA-R2xx, MPCI-L2xx)
- u-blox LTE-M/NB-IoT Modems (SARA-R4xx, SARA-N4xx, SARA-R5xx, _but NOT SARA-N2xx_)
- Sequans Monarch LTE Cat M1/NB1 (VZM20Q)
- Quectel BG96
- Quectel BG95
- Quectel M95
- Quectel MC60 ***(alpha)***
### Supported boards/modules
- EnviroDIY LTE Bee, WiFi Bee
- Arduino MKR GSM 1400
- Sodaq GPRSbee, uBee
- Microduino GSM
- Adafruit FONA Mini Cellular GSM Breakout, 800/808 Shield, FONA 3G
- Industruino GSM
- Dragino NB-IoT Bee
- Digi XBee S6B, XBee LTE Cat 1, XBee3 LTE Cat 1, XBee3 CatM
- Nimbelink Skywire/Airgain NL-SW-LTE-QBG96, NL-SW-LTE-QBG95
- RAK WisLTE ***(alpha)***
- ... other modules, based on supported modems. Some boards require [**special configuration**](https://github.com/vshymanskyy/TinyGSM/wiki/Board-configuration).
Watch this repo for new updates! And of course, contributions are welcome ;)
## Features
**Data connections**
- TCP (HTTP, MQTT, Blynk, ...)
- ALL modules support TCP connections
- Most modules support multiple simultaneous connections:
- A6/A7 - 8
- ESP8266 - 5
- Neoway M590 - 2
- Quectel BG96 - 12
- Quectel BG95 - 12
- Quectel M95 - 6
- Quectel MC60/MC60E - 6
- Sequans Monarch - 6
- SIM 800/900 - 5
- SIM 5360/5320/5300/7100 - 10
- SIM7000 - 8 possible without SSL, only 2 with
- SIM 7070/7080/7090 - 12
- SIM 7500/7600/7800 - 10
- SIM A7672X - 10
- u-blox 2G/3G - 7
- u-blox SARA R4/N4 - 7
- Digi XBee - _only 1 connection supported!_
- UDP
- Not yet supported on any module, though it may be some day
- SSL/TLS (HTTPS)
- Supported on:
- SIM800, SIM7000, A7672X, u-Blox, XBee _cellular_, ESP8266, Sequans Monarch and Quectel BG95 and BG96
- Note: **only some device models or firmware revisions have this feature** (SIM8xx R14.18, A7, etc.)
- Not yet supported on:
- SIM 5360/5320/7100, SIM 7500/7600/7800
- Not possible on:
- SIM900, A6/A7, Neoway M590, XBee _WiFi_
- Like TCP, most modules support simultaneous connections
- TCP and SSL connections can usually be mixed up to the total number of possible connections
**USSD**
- Sending USSD requests and decoding 7,8,16-bit responses
- Supported on:
- All SIMCom modems, Quectel modems, most u-blox
- Not possible on:
- XBee, u-blox SARA R4/N4, ESP8266 (obviously)
**SMS**
- Only _sending_ SMS is supported, not receiving
- Supported on all cellular modules
**Voice Calls**
- Supported on:
- SIM800/SIM900, SIM7600, A6/A7, Quectel modems, u-blox
- Not yet supported on:
- SIM7000, SIM5360/5320/7100, SIM7500/7800, VZM20Q (Monarch)
- Not possible on:
- XBee (any type), u-blox SARA R4/R5/N4, Neoway M590, ESP8266 (obviously)
- Functions:
- Dial, hangup
- DTMF sending
**Location**
- GPS/GNSS
- SIM808, SIM7000, SIM7500/7600/7800, BG96, BG95, u-blox
- NOTE: u-blox chips do _NOT_ have embedded GPS - this functionality only works if a secondary GPS is connected to primary cellular chip over I2C
- GSM location service
- SIM800, SIM7000, Quectel, u-blox
**Credits**
- Primary Authors/Contributors:
- [vshymanskyy](https://github.com/vshymanskyy)
- [SRGDamia1](https://github.com/SRGDamia1/)
- SIM7000:
- [captFuture](https://github.com/captFuture/)
- [FStefanni](https://github.com/FStefanni/)
- Sequans Monarch:
- [nootropicdesign](https://github.com/nootropicdesign/)
- Quectel M9C60
- [V1pr](https://github.com/V1pr)
- Quectel M95
- [replicadeltd](https://github.com/replicadeltd)
- UBLOX SARA-R5
- [Sebastian Bergner](https://github.com/sebastianbergner)
- SIMCOM A7672X
- [Giovanni de Rosso Unruh](https://github.com/giovannirosso)
- Other Contributors:
- https://github.com/vshymanskyy/TinyGSM/graphs/contributors
## Getting Started
#### First Steps
1. Using your phone:
- Disable PIN code on the SIM card
- Check your balance
- Check that APN, User, Pass are correct and you have internet
2. Ensure the SIM card is correctly inserted into the module
3. Ensure that GSM antenna is firmly attached
4. Ensure that you have a stable power supply to the module of at least **2A**.
5. Check if serial connection is working (Hardware Serial is recommended)
Send an ```AT``` command using [this sketch](tools/AT_Debug/AT_Debug.ino)
6. Try out the [WebClient](https://github.com/vshymanskyy/TinyGSM/blob/master/examples/WebClient/WebClient.ino) example
#### Writing your own code
The general flow of your code should be:
- Define the module that you are using (choose one and only one)
- ie, ```#define TINY_GSM_MODEM_SIM800```
- Included TinyGSM
- ```#include <TinyGsmClient.h>```
- Create a TinyGSM modem instance
- ```TinyGsm modem(SerialAT);```
- Create one or more TinyGSM client instances
- For a single connection, use
- ```TinyGsmClient client(modem);```
or
```TinyGsmClientSecure client(modem);``` (on supported modules)
- For multiple connections (on supported modules) use:
- ```TinyGsmClient clientX(modem, 0);```, ```TinyGsmClient clientY(modem, 1);```, etc
or
- ```TinyGsmClientSecure clientX(modem, 0);```, ```TinyGsmClientSecure clientY(modem, 1);```, etc
- Secure and insecure clients can usually be mixed when using multiple connections.
- The total number of connections possible varies by module
- Begin your serial communication and set all your pins as required to power your module and bring it to full functionality.
- The examples attempt to guess the module's baud rate. In working code, you should use a set baud.
- Wait for the module to be ready (could be as much as 6s, depending on the module)
- Initialize the modem
- ```modem.init()``` or ```modem.restart()```
- restart generally takes longer than init but ensures the module doesn't have lingering connections
- Unlock your SIM, if necessary:
- ```modem.simUnlock(GSM_PIN)```
- If using **WiFi**, specify your SSID information:
- ```modem.networkConnect(wifiSSID, wifiPass)```
- Network registration should be automatic on cellular modules
- Wait for network registration to be successful
- ```modem.waitForNetwork(600000L)```
- If using cellular, establish the GPRS or EPS data connection _after_ your are successfully registered on the network
- ```modem.gprsConnect(apn, gprsUser, gprsPass)``` (or simply ```modem.gprsConnect(apn)```)
- The same command is used for both GPRS or EPS connection
- If using a **Digi** brand cellular XBee, you must specify your GPRS/EPS connection information _before_ waiting for the network. This is true ONLY for _Digi cellular XBees_! _For all other cellular modules, use the GPRS connect function after network registration._
- Connect the TCP or SSL client
```client.connect(server, port)```
- Send out your data.
#### If you have any issues
1. Read the whole README (you're looking at it!), particularly the troubleshooting section below.
2. Some boards require [**special configuration**](https://github.com/vshymanskyy/TinyGSM/wiki/Board-configuration).
3. Try running the Diagnostics sketch
4. Check for [**highlighted topics here**](https://github.com/vshymanskyy/TinyGSM/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22for+reference%22+)
5. If you have a question, please post it in our [Gitter chat](https://gitter.im/tinygsm)
## How does it work?
Many GSM modems, WiFi and radio modules can be controlled by sending AT commands over Serial.
TinyGSM knows which commands to send, and how to handle AT responses, and wraps that into standard Arduino Client interface.
This library is "blocking" in all of its communication.
Depending on the function, your code may be blocked for a long time waiting for the module responses.
Apart from the obvious (ie, `waitForNetwork()`) several other functions may block your code for up to several *minutes*.
The `gprsConnect()` and `client.connect()` functions commonly block the longest, especially in poorer service regions.
The module shutdown and restart may also be quite slow.
This libary *does not* support any sort of "hardware" or pin level controls for the modules.
If you need to turn your module on or reset it using some sort of High/Low/High pin sequence, you must write those functions yourself.
## API Reference
For GPRS data streams, this library provides the standard [Arduino Client](https://www.arduino.cc/en/Reference/ClientConstructor) interface.
For additional functions, please refer to [this example sketch](examples/AllFunctions/AllFunctions.ino)
## Troubleshooting
### Ensure stable data & power connection
Most modules require _**as much as 2A**_ to properly connect to the network.
This is 4x what a "standard" USB will supply!
Improving the power supply actually solves stability problems in **many** cases!
- Read about [**powering your module**](https://github.com/vshymanskyy/TinyGSM/wiki/Powering-GSM-module).
- Keep your wires as short as possible
- Consider soldering them for a stable connection
- Do not put your wires next to noisy signal sources (buck converters, antennas, oscillators etc.)
- If everything else seems to be working but you are unable to connect to the network, check your power supply!
### Baud rates
Most modules support some sort of "auto-bauding" feature where the module will attempt to adjust it's baud rate to match what it is receiving.
TinyGSM also implements its own auto bauding function (`TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);`).
While very useful when initially connecting to a module and doing tests, these should **NOT** be used in any sort of production code.
Once you've established communication with the module, set the baud rate using the `setBaud(#)` function and stick with that rate.
### Broken initial configuration
Sometimes (especially if you played with AT commands), your module configuration may become invalid.
This may result in problems such as:
* Can't connect to the GPRS network
* Can't connect to the server
* Sent/received data contains invalid bytes
* etc.
To return module to **Factory Defaults**, use this sketch:
File -> Examples -> TinyGSM -> tools -> [FactoryReset](https://github.com/vshymanskyy/TinyGSM/blob/master/tools/FactoryReset/FactoryReset.ino)
In some cases, you may need to set an initial APN to connect to the cellular network.
Try using the ```gprsConnect(APN)``` function to set an initial APN if you are unable to register on the network.
You may need set the APN again after registering.
(In most cases, you should set the APN after registration.)
### Failed connection or no data received
The first connection with a new SIM card, a new module, or at a new location/tower may take a *LONG* time - up to 15 minutes or even more, especially if the signal quality isn't excellent.
If it is your first connection, you may need to adjust your wait times and possibly go to lunch while you're waiting.
If you are able to open a TCP connection but have the connection close before receiving data, try adding a keep-alive header to your request.
Some modules (ie, the SIM7000 in SSL mode) will immediately throw away any un-read data when the remote server closes the connection - sometimes without even giving a notification that data arrived in the first place.
When using MQTT, to keep a continuous connection you may need to reduce your keep-alive interval (PINGREQ/PINGRESP).
### Diagnostics sketch
Use this sketch to help diagnose SIM card and GPRS connection issues:
File -> Examples -> TinyGSM -> tools -> [Diagnostics](https://github.com/vshymanskyy/TinyGSM/blob/master/tools/Diagnostics/Diagnostics.ino)
If the diagnostics fail, uncomment this line to output some debugging comments from the library:
```cpp
#define TINY_GSM_DEBUG SerialMon
```
In any custom code, ```TINY_GSM_DEBUG``` must be defined before including the TinyGSM library.
If you are unable to see any obvious errors in the library debugging, use [StreamDebugger](https://github.com/vshymanskyy/StreamDebugger) to copy the entire AT command sequence to the main serial port.
In the diagnostics example, simply uncomment the line:
```cpp
#define DUMP_AT_COMMANDS
```
In custom code, you can add this snippit:
```cpp
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
```
### Web request formatting problems - "but it works with PostMan"
This library opens a TCP (or SSL) connection to a server.
In the [OSI model](https://en.wikipedia.org/wiki/OSI_model), that's [layer 4](http://www.tcpipguide.com/free/t_TransportLayerLayer4.htm) (or 5 for SSL).
HTTP (GET/POST), MQTT, and most of the other functions you probably want to use live up at [layer 7](http://www.tcpipguide.com/free/t_ApplicationLayerLayer7.htm).
This means that you need to either manually code the top layer or use another library (like [HTTPClient](https://github.com/arduino-libraries/ArduinoHttpClient) or [PubSubClient](https://pubsubclient.knolleary.net/)) to do it for you.
Tools like [PostMan](https://www.postman.com/) also show layer 7, not layer 4/5 like TinyGSM.
If you are successfully connecting to a server, but getting responses of "bad request" (or no response), the issue is probably your formatting.
Here are some tips for writing layer 7 (particularly HTTP request) manually:
- Look at the "WebClient" example
- Make sure you are including all required headers.
- If you are testing with PostMan, make sure you un-hide and look at the "auto-generated" headers; you'll probably be surprised by how many of them there are.
- Use ```client.print("...")```, or ```client.write(buf, #)```, or even ```client.write(String("..."))```, not ```client.write("...")``` to help prevent text being sent out one character at a time (typewriter style)
- Enclose the entirety of each header or line within a single string or print statement
- use
```cpp
client.print(String("GET ") + resource + " HTTP/1.1\r\n");
```
instead of
```cpp
client.print("GET ");
client.print(resource);
client.println(" HTTP/1.1")
```
- Make sure there is one entirely blank line between the last header and the content of any POST request.
- Add two lines to the last header ```client.print("....\r\n\r\n")``` or put in an extra ```client.println()```
- This is an HTTP requirement and is really easy to miss.
### SoftwareSerial problems
When using ```SoftwareSerial``` (on Uno, Nano, etc), the speed **115200** may not work.
Try selecting **57600**, **38400**, or even lower - the one that works best for you.
In some cases **9600** is unstable, but using **38400** helps, etc.
Be sure to set correct TX/RX pins in the sketch. Please note that not every Arduino pin can serve as TX or RX pin.
**Read more about SoftSerial options and configuration [here](https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html) and [here](https://www.arduino.cc/en/Reference/SoftwareSerial).**
### ESP32 Notes
#### HardwareSerial
When using ESP32 `HardwareSerial`, you may need to specify additional parameters to the `.begin()` call.
Please [refer to this comment](https://github.com/vshymanskyy/TinyGSM/issues/91#issuecomment-356024747).
#### HttpClient
You will not be able to compile the HttpClient or HttpsClient examples with ESP32 core 1.0.2. Upgrade to 1.0.3, downgrade to version 1.0.1 or use the WebClient example.
### SAMD21
When using SAMD21-based boards, you may need to use a sercom uart port instead of `Serial1`.
Please [refer to this comment](https://github.com/vshymanskyy/TinyGSM/issues/102#issuecomment-345548941).
### Goouuu Tech IOT-GA6 vs AI-Thinker A6 confusion
It turns out that **Goouuu Tech IOT-GA6** is not the same as **AI-Thinker A6**. Unfortunately IOT-GA6 is not supported out of the box yet. There are some hints that IOT-GA6 firmware may be updated to match A6... See [this topic](https://github.com/vshymanskyy/TinyGSM/issues/164).
### SIM800 and SSL
Some, but not all, versions of the SIM800 support SSL.
Having SSL support depends on the firmware version and the individual module.
Users have had varying levels of success in using SSL on the SIM800 even with apparently identical firmware.
If you need SSL and it does not appear to be working on your SIM800, try a different module or try using a secondary SSL library.
### Which version of the SIM7000 code to use
There are two versions of the SIM7000 code, one using `TINY_GSM_MODEM_SIM7000` and another with `TINY_GSM_MODEM_SIM7000SSL`.
The `TINY_GSM_MODEM_SIM7000` version *does not support SSL* but supports up to 8 simultaneous connections.
The `TINY_GSM_MODEM_SIM7000SSL` version supports both SSL *and unsecured connections* with up to 2 simultaneous connections.
So why are there two versions?
The "SSL" version uses the SIM7000's "application" commands while the other uses the "TCP-IP toolkit".
Depending on your region/firmware, one or the other may not work for you.
Try both and use whichever is more stable.
If you do not need SSL, I recommend starting with `TINY_GSM_MODEM_SIM7000`.
__________
## License
This project is released under
The GNU Lesser General Public License (LGPL-3.0)

View file

@ -0,0 +1,3 @@
# Allow references to be used to change values
filter=-runtime/references
filter=-build/namespaces

View file

@ -0,0 +1,524 @@
/**************************************************************
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
* NOTE:
* Some of the functions may be unavailable for your modem.
* Just comment them out.
*
**************************************************************/
// Select your modem:
#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_SIM7000SSL
// #define TINY_GSM_MODEM_SIM7080
// #define TINY_GSM_MODEM_SIM5360
// #define TINY_GSM_MODEM_SIM7600
// #define TINY_GSM_MODEM_A7672X
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_SARAR5
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_ESP32
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// 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
#ifndef __AVR_ATmega328P__
#define SerialAT Serial1
// or Software Serial on Uno, Nano
#else
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(2, 3); // RX, TX
#endif
// See all AT commands, if wanted
// #define DUMP_AT_COMMANDS
// Define the serial console for debug prints, if needed
#define TINY_GSM_DEBUG SerialMon
// Range to attempt to autobaud
// NOTE: DO NOT AUTOBAUD in production code. Once you've established
// communication, set a fixed baud rate using modem.setBaud(#).
#define GSM_AUTOBAUD_MIN 9600
#define GSM_AUTOBAUD_MAX 57600
// Add a reception delay, if needed.
// This may be needed for a fast processor at a slow baud rate.
// #define TINY_GSM_YIELD() { delay(2); }
/*
* Tests enabled
*/
#define TINY_GSM_TEST_GPRS true
#define TINY_GSM_TEST_WIFI false
#define TINY_GSM_TEST_TCP true
#define TINY_GSM_TEST_SSL true
#define TINY_GSM_TEST_CALL true
#define TINY_GSM_TEST_SMS true
#define TINY_GSM_TEST_USSD true
#define TINY_GSM_TEST_BATTERY true
#define TINY_GSM_TEST_TEMPERATURE true
#define TINY_GSM_TEST_GSM_LOCATION true
#define TINY_GSM_TEST_GPS true
#define TINY_GSM_TEST_NTP true
#define TINY_GSM_TEST_TIME true
// disconnect and power down modem after tests
#define TINY_GSM_POWERDOWN true
// set GSM PIN, if any
#define GSM_PIN ""
// Set phone numbers, if you want to test SMS and Calls
// #define SMS_TARGET "+380xxxxxxxxx"
// #define CALL_TARGET "+380xxxxxxxxx"
// Your GPRS credentials, if any
const char apn[] = "YourAPN";
// const char apn[] = "ibasis.iot";
const char gprsUser[] = "";
const char gprsPass[] = "";
// Your WiFi connection credentials, if applicable
const char wifiSSID[] = "YourSSID";
const char wifiPass[] = "YourWiFiPass";
// Server details to test TCP/SSL
const char server[] = "vsh.pp.ua";
const char resource[] = "/TinyGSM/logo.txt";
#include <TinyGsmClient.h>
#if TINY_GSM_TEST_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS
#undef TINY_GSM_TEST_GPRS
#undef TINY_GSM_TEST_WIFI
#define TINY_GSM_TEST_GPRS false
#define TINY_GSM_TEST_WIFI true
#endif
#if TINY_GSM_TEST_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#endif
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
// !!!!!!!!!!!
// Set your reset, enable, power pins here
// !!!!!!!!!!!
DBG("Wait...");
delay(6000L);
// Set GSM module baud rate
TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);
// SerialAT.begin(9600);
}
void loop() {
// Restart takes quite some time
// To skip it, call init() instead of restart()
DBG("Initializing modem...");
if (!modem.restart()) {
// if (!modem.init()) {
DBG("Failed to restart modem, delaying 10s and retrying");
// restart autobaud in case GSM just rebooted
// TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);
return;
}
String modemInfo = modem.getModemInfo();
DBG("Modem Info:", modemInfo);
String name = modem.getModemName();
DBG("Modem Name:", name);
String manufacturer = modem.getModemManufacturer();
DBG("Modem Manufacturer:", manufacturer);
String hw_ver = modem.getModemModel();
DBG("Modem Hardware Version:", hw_ver);
String fv_ver = modem.getModemRevision();
DBG("Modem Firware Version:", fv_ver);
#if not defined(TINY_GSM_MODEM_ESP8266) && not defined(TINY_GSM_MODEM_ESP32)
String mod_sn = modem.getModemSerialNumber();
DBG("Modem Serial Number (may be SIM CCID):", mod_sn);
#endif
#if TINY_GSM_TEST_GPRS
// Unlock your SIM card with a PIN if needed
if (GSM_PIN && modem.getSimStatus() != 3) { modem.simUnlock(GSM_PIN); }
#endif
#if TINY_GSM_TEST_WIFI && defined(TINY_GSM_MODEM_HAS_WIFI)
DBG("Setting SSID/password...");
if (!modem.networkConnect(wifiSSID, wifiPass)) {
DBG(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
#endif
#if TINY_GSM_TEST_GPRS && defined(TINY_GSM_MODEM_XBEE)
// The XBee must run the gprsConnect function BEFORE waiting for network!
modem.gprsConnect(apn, gprsUser, gprsPass);
#endif
DBG("Waiting for network...");
if (!modem.waitForNetwork(600000L, true)) {
delay(10000);
return;
}
if (modem.isNetworkConnected()) { DBG("Network connected"); }
#if TINY_GSM_TEST_GPRS
DBG("Connecting to", apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
delay(10000);
return;
}
bool res = modem.isGprsConnected();
DBG("GPRS status:", res ? "connected" : "not connected");
String ccid = modem.getSimCCID();
DBG("CCID:", ccid);
String imei = modem.getIMEI();
DBG("IMEI:", imei);
String imsi = modem.getIMSI();
DBG("IMSI:", imsi);
String cop = modem.getOperator();
DBG("Operator:", cop);
// String prov = modem.getProvider();
// DBG("Provider:", prov);
IPAddress local = modem.localIP();
DBG("Local IP:", local);
int csq = modem.getSignalQuality();
DBG("Signal quality:", csq);
#endif
#if TINY_GSM_TEST_USSD && defined TINY_GSM_MODEM_HAS_SMS
String ussd_balance = modem.sendUSSD("*111#");
DBG("Balance (USSD):", ussd_balance);
String ussd_phone_num = modem.sendUSSD("*161#");
DBG("Phone number (USSD):", ussd_phone_num);
#endif
#if TINY_GSM_TEST_TCP && defined TINY_GSM_MODEM_HAS_TCP
TinyGsmClient client(modem, 0);
const int port = 80;
DBG("Connecting to", server);
if (!client.connect(server, port)) {
DBG("... failed");
} else {
// Make a HTTP GET request:
client.print(String("GET ") + resource + " HTTP/1.0\r\n");
client.print(String("Host: ") + server + "\r\n");
client.print("Connection: close\r\n\r\n");
// Wait for data to arrive
uint32_t start = millis();
while (client.connected() && !client.available() &&
millis() - start < 30000L) {
delay(100);
};
// Read data
start = millis();
char logo[640] = {
'\0',
};
int read_chars = 0;
while (client.connected() && millis() - start < 10000L) {
while (client.available()) {
logo[read_chars] = client.read();
logo[read_chars + 1] = '\0';
read_chars++;
start = millis();
}
}
SerialMon.println(logo);
DBG("##### RECEIVED:", strlen(logo), "CHARACTERS");
client.stop();
}
#endif
#if TINY_GSM_TEST_SSL && defined TINY_GSM_MODEM_HAS_SSL
// TODO: Add test of adding certificcate
TinyGsmClientSecure secureClient(modem, 1);
const int securePort = 443;
DBG("Connecting securely to", server);
if (!secureClient.connect(server, securePort)) {
DBG("... failed");
} else {
// Make a HTTP GET request:
secureClient.print(String("GET ") + resource + " HTTP/1.0\r\n");
secureClient.print(String("Host: ") + server + "\r\n");
secureClient.print("Connection: close\r\n\r\n");
// Wait for data to arrive
uint32_t startS = millis();
while (secureClient.connected() && !secureClient.available() &&
millis() - startS < 30000L) {
delay(100);
};
// Read data
startS = millis();
char logoS[640] = {
'\0',
};
int read_charsS = 0;
while (secureClient.connected() && millis() - startS < 10000L) {
while (secureClient.available()) {
logoS[read_charsS] = secureClient.read();
logoS[read_charsS + 1] = '\0';
read_charsS++;
startS = millis();
}
}
SerialMon.println(logoS);
DBG("##### RECEIVED:", strlen(logoS), "CHARACTERS");
secureClient.stop();
}
#endif
#if TINY_GSM_TEST_CALL && defined(TINY_GSM_MODEM_HAS_CALLING) && \
defined(CALL_TARGET)
DBG("Calling:", CALL_TARGET);
// This is NOT supported on M590
res = modem.callNumber(CALL_TARGET);
DBG("Call:", res ? "OK" : "fail");
if (res) {
delay(1000L);
// Play DTMF A, duration 1000ms
modem.dtmfSend('A', 1000);
// Play DTMF 0..4, default duration (100ms)
for (char tone = '0'; tone <= '4'; tone++) { modem.dtmfSend(tone); }
delay(5000);
res = modem.callHangup();
DBG("Hang up:", res ? "OK" : "fail");
}
#endif
// Test the SMS functions
#if TINY_GSM_TEST_SMS && defined TINY_GSM_MODEM_HAS_SMS && defined SMS_TARGET
res = modem.sendSMS(SMS_TARGET, String("Hello from ") + imei);
DBG("SMS:", res ? "OK" : "fail");
// This is only supported on SIMxxx series
res = modem.sendSMS_UTF8_begin(SMS_TARGET);
if (res) {
auto stream = modem.sendSMS_UTF8_stream();
stream.print(F("Привіііт! Print number: "));
stream.print(595);
res = modem.sendSMS_UTF8_end();
}
DBG("UTF8 SMS:", res ? "OK" : "fail");
#endif
// Test the GSM location functions
#if TINY_GSM_TEST_GSM_LOCATION && defined TINY_GSM_MODEM_HAS_GSM_LOCATION
float gsm_latitude = 0;
float gsm_longitude = 0;
float gsm_accuracy = 0;
int gsm_year = 0;
int gsm_month = 0;
int gsm_day = 0;
int gsm_hour = 0;
int gsm_minute = 0;
int gsm_second = 0;
for (int8_t i = 15; i; i--) {
DBG("Requesting current GSM location");
if (modem.getGsmLocation(&gsm_latitude, &gsm_longitude, &gsm_accuracy,
&gsm_year, &gsm_month, &gsm_day, &gsm_hour,
&gsm_minute, &gsm_second)) {
DBG("Latitude:", String(gsm_latitude, 8),
"\tLongitude:", String(gsm_longitude, 8));
DBG("Accuracy:", gsm_accuracy);
DBG("Year:", gsm_year, "\tMonth:", gsm_month, "\tDay:", gsm_day);
DBG("Hour:", gsm_hour, "\tMinute:", gsm_minute, "\tSecond:", gsm_second);
break;
} else {
DBG("Couldn't get GSM location, retrying in 15s.");
delay(15000L);
}
}
DBG("Retrieving GSM location again as a string");
String location = modem.getGsmLocation();
DBG("GSM Based Location String:", location);
#endif
// Test the GPS functions
#if TINY_GSM_TEST_GPS && defined TINY_GSM_MODEM_HAS_GPS
DBG("Enabling GPS/GNSS/GLONASS and waiting 15s for warm-up");
#if !defined(TINY_GSM_MODEM_SARAR5) // not needed for this module
modem.enableGPS();
#endif
delay(15000L);
float gps_latitude = 0;
float gps_longitude = 0;
float gps_speed = 0;
float gps_altitude = 0;
int gps_vsat = 0;
int gps_usat = 0;
float gps_accuracy = 0;
int gps_year = 0;
int gps_month = 0;
int gps_day = 0;
int gps_hour = 0;
int gps_minute = 0;
int gps_second = 0;
for (int8_t i = 15; i; i--) {
DBG("Requesting current GPS/GNSS/GLONASS location");
if (modem.getGPS(&gps_latitude, &gps_longitude, &gps_speed, &gps_altitude,
&gps_vsat, &gps_usat, &gps_accuracy, &gps_year, &gps_month,
&gps_day, &gps_hour, &gps_minute, &gps_second)) {
DBG("Latitude:", String(gps_latitude, 8),
"\tLongitude:", String(gps_longitude, 8));
DBG("Speed:", gps_speed, "\tAltitude:", gps_altitude);
DBG("Visible Satellites:", gps_vsat, "\tUsed Satellites:", gps_usat);
DBG("Accuracy:", gps_accuracy);
DBG("Year:", gps_year, "\tMonth:", gps_month, "\tDay:", gps_day);
DBG("Hour:", gps_hour, "\tMinute:", gps_minute, "\tSecond:", gps_second);
break;
} else {
DBG("Couldn't get GPS/GNSS/GLONASS location, retrying in 15s.");
delay(15000L);
}
}
DBG("Retrieving GPS/GNSS/GLONASS location again as a string");
String gps_raw = modem.getGPSraw();
#if !defined(TINY_GSM_MODEM_SARAR5) // not available for this module
DBG("GPS/GNSS Based Location String:", gps_raw);
DBG("Disabling GPS");
modem.disableGPS();
#endif
#endif
// Test the Network time functions
#if TINY_GSM_TEST_NTP && defined TINY_GSM_MODEM_HAS_NTP
DBG("Asking modem to sync with NTP");
modem.NTPServerSync("pool.ntp.org", 20);
#endif
#if TINY_GSM_TEST_TIME && defined TINY_GSM_MODEM_HAS_TIME
int ntp_year = 0;
int ntp_month = 0;
int ntp_day = 0;
int ntp_hour = 0;
int ntp_min = 0;
int ntp_sec = 0;
float ntp_timezone = 0;
for (int8_t i = 5; i; i--) {
DBG("Requesting current network time");
if (modem.getNetworkTime(&ntp_year, &ntp_month, &ntp_day, &ntp_hour,
&ntp_min, &ntp_sec, &ntp_timezone)) {
DBG("Year:", ntp_year, "\tMonth:", ntp_month, "\tDay:", ntp_day);
DBG("Hour:", ntp_hour, "\tMinute:", ntp_min, "\tSecond:", ntp_sec);
DBG("Timezone:", ntp_timezone);
break;
} else {
DBG("Couldn't get network time, retrying in 15s.");
delay(15000L);
}
}
DBG("Retrieving time again as a string");
String time = modem.getGSMDateTime(DATE_FULL);
DBG("Current Network Time:", time);
#endif
// Test Battery functions
#if TINY_GSM_TEST_BATTERY && defined TINY_GSM_MODEM_HAS_BATTERY
int8_t chargeState = -99;
int8_t chargePercent = -99;
int16_t milliVolts = -9999;
modem.getBattStats(chargeState, chargePercent, milliVolts);
DBG("Battery charge state:", chargeState);
DBG("Battery charge 'percent':", chargePercent);
DBG("Battery voltage:", milliVolts / 1000.0F);
#endif
// Test temperature functions
#if TINY_GSM_TEST_TEMPERATURE && defined TINY_GSM_MODEM_HAS_TEMPERATURE
float temp = modem.getTemperature();
DBG("Chip temperature:", temp);
#endif
#if TINY_GSM_POWERDOWN
#if TINY_GSM_TEST_GPRS
modem.gprsDisconnect();
delay(5000L);
if (!modem.isGprsConnected()) {
DBG("GPRS disconnected");
} else {
DBG("GPRS disconnect: Failed.");
}
#endif
#if TINY_GSM_TEST_WIFI
modem.networkDisconnect();
DBG("WiFi disconnected");
#endif
// Try to power-off (modem may decide to restart automatically)
// To turn off modem completely, please use Reset/Enable pins
modem.poweroff();
DBG("Poweroff.");
#endif
DBG("End of tests.");
// Do nothing forevermore
while (true) { modem.maintain(); }
}

View file

@ -0,0 +1,116 @@
/**************************************************************
*
* For this example, you need to install Blynk library:
* https://github.com/blynkkk/blynk-library/releases/latest
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
**************************************************************
*
* Blynk is a platform with iOS and Android apps to control
* Arduino, Raspberry Pi and the likes over the Internet.
* You can easily build graphic interfaces for all your
* projects by simply dragging and dropping widgets.
*
* Blynk supports many development boards with WiFi, Ethernet,
* GSM, Bluetooth, BLE, USB/Serial connection methods.
* See more in Blynk library examples and community forum.
*
* http://www.blynk.io/
*
* Change GPRS apm, user, pass, and Blynk auth token to run :)
**************************************************************/
#define BLYNK_TEMPLATE_ID "TMPxxxxxx"
#define BLYNK_TEMPLATE_NAME "Device"
#define BLYNK_AUTH_TOKEN "YourAuthToken"
#define BLYNK_PRINT Serial // Comment this out to disable prints and save space
// Default heartbeat interval for GSM is 60
// If you want override this value, uncomment and set this option:
// #define BLYNK_HEARTBEAT 30
// Select your modem:
#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_SIM7000SSL
// #define TINY_GSM_MODEM_SIM7080
// #define TINY_GSM_MODEM_SIM5360
// #define TINY_GSM_MODEM_SIM7600
// #define TINY_GSM_MODEM_A7672X
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_SARAR5
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_ESP32
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
#include <TinyGsmClient.h>
#include <BlynkSimpleTinyGSM.h>
// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial
// Hardware Serial on Mega, Leonardo, Micro
#ifndef __AVR_ATmega328P__
#define SerialAT Serial1
// or Software Serial on Uno, Nano
#else
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(2, 3); // RX, TX
#endif
// Your GPRS credentials, if any
const char apn[] = "YourAPN";
const char user[] = "";
const char pass[] = "";
// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
const char auth[] = "YourAuthToken";
TinyGsm modem(SerialAT);
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
// Set GSM module baud rate
SerialAT.begin(115200);
delay(6000);
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.println("Initializing modem...");
modem.restart();
String modemInfo = modem.getModemInfo();
SerialMon.print("Modem Info: ");
SerialMon.println(modemInfo);
// Unlock your SIM card with a PIN
// modem.simUnlock("1234");
Blynk.begin(auth, modem, apn, user, pass);
}
void loop() {
Blynk.run();
}

View file

@ -0,0 +1,360 @@
/**************************************************************
*
* For this example, you need to install CRC32 library:
* https://github.com/bakercp/CRC32
* or from http://librarymanager/all#CRC32+checksum
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
* ATTENTION! Downloading big files requires of knowledge of
* the TinyGSM internals and some modem specifics,
* so this is for more experienced developers.
*
**************************************************************/
// Select your modem:
#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_SIM7000SSL
// #define TINY_GSM_MODEM_SIM7080
// #define TINY_GSM_MODEM_SIM5360
// #define TINY_GSM_MODEM_SIM7600
// #define TINY_GSM_MODEM_A7672X
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_SARAR5
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_ESP32
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// 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
#ifndef __AVR_ATmega328P__
#define SerialAT Serial1
// or Software Serial on Uno, Nano
#else
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(2, 3); // RX, TX
#endif
// Increase RX buffer to capture the entire response
// Chips without internal buffering (A6/A7, ESP8266, M590)
// need enough space in the buffer for the entire response
// else data will be lost (and the http library will fail).
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 1024
#endif
// See all AT commands, if wanted
// #define DUMP_AT_COMMANDS
// Define the serial console for debug prints, if needed
#define TINY_GSM_DEBUG SerialMon
// #define LOGGING // <- Logging is for the HTTP library
// Add a reception delay, if needed.
// This may be needed for a fast processor at a slow baud rate.
// #define TINY_GSM_YIELD() { delay(2); }
// Define how you're planning to connect to the internet.
// This is only needed for this example, not in other code.
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
// set GSM PIN, if any
#define GSM_PIN ""
// Your GPRS credentials, if any
const char apn[] = "YourAPN";
const char gprsUser[] = "";
const char gprsPass[] = "";
// Your WiFi connection credentials, if applicable
const char wifiSSID[] = "YourSSID";
const char wifiPass[] = "YourWiFiPass";
// Server details
const char server[] = "vsh.pp.ua";
const int port = 80;
#include <TinyGsmClient.h>
#include <CRC32.h>
// Just in case someone defined the wrong thing..
#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS false
#define TINY_GSM_USE_WIFI true
#endif
#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#endif
const char resource[] = "/TinyGSM/test_1k.bin";
uint32_t knownCRC32 = 0x6f50d767;
uint32_t knownFileSize = 1024; // In case server does not send it
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
TinyGsmClient client(modem);
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
// !!!!!!!!!!!
// Set your reset, enable, power pins here
// !!!!!!!!!!!
SerialMon.println("Wait...");
// Set GSM module baud rate
SerialAT.begin(115200);
delay(6000);
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.println("Initializing modem...");
modem.restart();
// modem.init();
String modemInfo = modem.getModemInfo();
SerialMon.print("Modem Info: ");
SerialMon.println(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
}
void printPercent(uint32_t readLength, uint32_t contentLength) {
// If we know the total length
if (contentLength != (uint32_t)-1) {
SerialMon.print("\r ");
SerialMon.print((100.0 * readLength) / contentLength);
SerialMon.print('%');
} else {
SerialMon.println(readLength);
}
}
void loop() {
#if TINY_GSM_USE_WIFI
// Wifi connection parameters must be set before waiting for the network
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
#endif
#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_XBEE
// The XBee must run the gprsConnect function BEFORE waiting for network!
modem.gprsConnect(apn, gprsUser, gprsPass);
#endif
SerialMon.print("Waiting for network...");
if (!modem.waitForNetwork()) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
if (modem.isNetworkConnected()) { SerialMon.println("Network connected"); }
#if TINY_GSM_USE_GPRS
// GPRS connection parameters are usually set after network registration
SerialMon.print(F("Connecting to "));
SerialMon.print(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
if (modem.isGprsConnected()) { SerialMon.println("GPRS connected"); }
#endif
SerialMon.print(F("Connecting to "));
SerialMon.print(server);
if (!client.connect(server, port)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
// Make a HTTP GET request:
client.print(String("GET ") + resource + " HTTP/1.0\r\n");
client.print(String("Host: ") + server + "\r\n");
client.print("Connection: close\r\n\r\n");
// Let's see what the entire elapsed time is, from after we send the request.
uint32_t timeElapsed = millis();
SerialMon.println(F("Waiting for response header"));
// While we are still looking for the end of the header (i.e. empty line
// FOLLOWED by a newline), continue to read data into the buffer, parsing each
// line (data FOLLOWED by a newline). If it takes too long to get data from
// the client, we need to exit.
const uint32_t clientReadTimeout = 5000;
uint32_t clientReadStartTime = millis();
String headerBuffer;
bool finishedHeader = false;
uint32_t contentLength = 0;
while (!finishedHeader) {
int nlPos;
if (client.available()) {
clientReadStartTime = millis();
while (client.available()) {
char c = client.read();
headerBuffer += c;
// Uncomment the lines below to see the data coming into the buffer
// if (c < 16)
// SerialMon.print('0');
// SerialMon.print(c, HEX);
// SerialMon.print(' ');
// if (isprint(c))
// SerialMon.print(reinterpret_cast<char> c);
// else
// SerialMon.print('*');
// SerialMon.print(' ');
// Let's exit and process if we find a new line
if (headerBuffer.indexOf(F("\r\n")) >= 0) break;
}
} else {
if (millis() - clientReadStartTime > clientReadTimeout) {
// Time-out waiting for data from client
SerialMon.println(F(">>> Client Timeout !"));
break;
}
}
// See if we have a new line.
nlPos = headerBuffer.indexOf(F("\r\n"));
if (nlPos > 0) {
headerBuffer.toLowerCase();
// Check if line contains content-length
if (headerBuffer.startsWith(F("content-length:"))) {
contentLength =
headerBuffer.substring(headerBuffer.indexOf(':') + 1).toInt();
// SerialMon.print(F("Got Content Length: ")); // uncomment for
// SerialMon.println(contentLength); // confirmation
}
headerBuffer.remove(0, nlPos + 2); // remove the line
} else if (nlPos == 0) {
// if the new line is empty (i.e. "\r\n" is at the beginning of the line),
// we are done with the header.
finishedHeader = true;
}
}
// The two cases which are not managed properly are as follows:
// 1. The client doesn't provide data quickly enough to keep up with this
// loop.
// 2. If the client data is segmented in the middle of the 'Content-Length: '
// header,
// then that header may be missed/damaged.
//
uint32_t readLength = 0;
CRC32 crc;
if (finishedHeader && contentLength == knownFileSize) {
SerialMon.println(F("Reading response data"));
clientReadStartTime = millis();
printPercent(readLength, contentLength);
while (readLength < contentLength && client.connected() &&
millis() - clientReadStartTime < clientReadTimeout) {
while (client.available()) {
uint8_t c = client.read();
// SerialMon.print(reinterpret_cast<char>c); // Uncomment this to show
// data
crc.update(c);
readLength++;
if (readLength % (contentLength / 13) == 0) {
printPercent(readLength, contentLength);
}
clientReadStartTime = millis();
}
}
printPercent(readLength, contentLength);
}
timeElapsed = millis() - timeElapsed;
SerialMon.println();
// Shutdown
client.stop();
SerialMon.println(F("Server disconnected"));
#if TINY_GSM_USE_WIFI
modem.networkDisconnect();
SerialMon.println(F("WiFi disconnected"));
#endif
#if TINY_GSM_USE_GPRS
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
#endif
float duration = float(timeElapsed) / 1000;
SerialMon.println();
SerialMon.print("Content-Length: ");
SerialMon.println(contentLength);
SerialMon.print("Actually read: ");
SerialMon.println(readLength);
SerialMon.print("Calc. CRC32: 0x");
SerialMon.println(crc.finalize(), HEX);
SerialMon.print("Known CRC32: 0x");
SerialMon.println(knownCRC32, HEX);
SerialMon.print("Duration: ");
SerialMon.print(duration);
SerialMon.println("s");
// Do nothing forevermore
while (true) { delay(1000); }
}

View file

@ -0,0 +1,265 @@
/**************************************************************
*
* This sketch connects to a website and downloads a page.
* It can be used to perform HTTP/RESTful API calls.
*
* For this example, you need to install ArduinoHttpClient library:
* https://github.com/arduino-libraries/ArduinoHttpClient
* or from http://librarymanager/all#ArduinoHttpClient
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
* For more HTTP API examples, see ArduinoHttpClient library
*
* NOTE: This example may NOT work with the XBee because the
* HttpClient library does not empty to serial buffer fast enough
* and the buffer overflow causes the HttpClient library to stall.
* Boards with faster processors may work, 8MHz boards will not.
**************************************************************/
// Select your modem:
#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_SIM7000SSL
// #define TINY_GSM_MODEM_SIM7080
// #define TINY_GSM_MODEM_SIM5360
// #define TINY_GSM_MODEM_SIM7600
// #define TINY_GSM_MODEM_A7672X
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_SARAR5
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_ESP32
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// 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
#ifndef __AVR_ATmega328P__
#define SerialAT Serial1
// or Software Serial on Uno, Nano
#else
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(2, 3); // RX, TX
#endif
// Increase RX buffer to capture the entire response
// Chips without internal buffering (A6/A7, ESP8266, M590)
// need enough space in the buffer for the entire response
// else data will be lost (and the http library will fail).
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 650
#endif
// See all AT commands, if wanted
// #define DUMP_AT_COMMANDS
// Define the serial console for debug prints, if needed
#define TINY_GSM_DEBUG SerialMon
// #define LOGGING // <- Logging is for the HTTP library
// Range to attempt to autobaud
// NOTE: DO NOT AUTOBAUD in production code. Once you've established
// communication, set a fixed baud rate using modem.setBaud(#).
#define GSM_AUTOBAUD_MIN 9600
#define GSM_AUTOBAUD_MAX 115200
// Add a reception delay, if needed.
// This may be needed for a fast processor at a slow baud rate.
// #define TINY_GSM_YIELD() { delay(2); }
// Define how you're planning to connect to the internet
// These defines are only for this example; they are not needed in other code.
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
// set GSM PIN, if any
#define GSM_PIN ""
// Your GPRS credentials, if any
const char apn[] = "YourAPN";
const char gprsUser[] = "";
const char gprsPass[] = "";
// Your WiFi connection credentials, if applicable
const char wifiSSID[] = "YourSSID";
const char wifiPass[] = "YourWiFiPass";
// Server details
const char server[] = "vsh.pp.ua";
const char resource[] = "/TinyGSM/logo.txt";
const int port = 80;
#include <TinyGsmClient.h>
#include <ArduinoHttpClient.h>
// Just in case someone defined the wrong thing..
#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS false
#define TINY_GSM_USE_WIFI true
#endif
#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#endif
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
TinyGsmClient client(modem);
HttpClient http(client, server, port);
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
// !!!!!!!!!!!
// Set your reset, enable, power pins here
// !!!!!!!!!!!
SerialMon.println("Wait...");
// Set GSM module baud rate
TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);
// SerialAT.begin(9600);
delay(6000);
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.println("Initializing modem...");
modem.restart();
// modem.init();
String modemInfo = modem.getModemInfo();
SerialMon.print("Modem Info: ");
SerialMon.println(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
}
void loop() {
#if TINY_GSM_USE_WIFI
// Wifi connection parameters must be set before waiting for the network
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
#endif
#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_XBEE
// The XBee must run the gprsConnect function BEFORE waiting for network!
modem.gprsConnect(apn, gprsUser, gprsPass);
#endif
SerialMon.print("Waiting for network...");
if (!modem.waitForNetwork()) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
if (modem.isNetworkConnected()) { SerialMon.println("Network connected"); }
#if TINY_GSM_USE_GPRS
// GPRS connection parameters are usually set after network registration
SerialMon.print(F("Connecting to "));
SerialMon.print(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
if (modem.isGprsConnected()) { SerialMon.println("GPRS connected"); }
#endif
SerialMon.print(F("Performing HTTP GET request... "));
int err = http.get(resource);
if (err != 0) {
SerialMon.println(F("failed to connect"));
delay(10000);
return;
}
int status = http.responseStatusCode();
SerialMon.print(F("Response status code: "));
SerialMon.println(status);
if (!status) {
delay(10000);
return;
}
SerialMon.println(F("Response Headers:"));
while (http.headerAvailable()) {
String headerName = http.readHeaderName();
String headerValue = http.readHeaderValue();
SerialMon.println(" " + headerName + " : " + headerValue);
}
int length = http.contentLength();
if (length >= 0) {
SerialMon.print(F("Content length is: "));
SerialMon.println(length);
}
if (http.isResponseChunked()) {
SerialMon.println(F("The response is chunked"));
}
String body = http.responseBody();
SerialMon.println(F("Response:"));
SerialMon.println(body);
SerialMon.print(F("Body length is: "));
SerialMon.println(body.length());
// Shutdown
http.stop();
SerialMon.println(F("Server disconnected"));
#if TINY_GSM_USE_WIFI
modem.networkDisconnect();
SerialMon.println(F("WiFi disconnected"));
#endif
#if TINY_GSM_USE_GPRS
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
#endif
// Do nothing forevermore
while (true) { delay(1000); }
}

View file

@ -0,0 +1,258 @@
/**************************************************************
*
* This sketch connects to a website and downloads a page.
* It can be used to perform HTTP/RESTful API calls.
*
* For this example, you need to install ArduinoHttpClient library:
* https://github.com/arduino-libraries/ArduinoHttpClient
* or from http://librarymanager/all#ArduinoHttpClient
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
* SSL/TLS is not yet supported on the Quectel modems
* The A6/A7/A20 and M590 are not capable of SSL/TLS
*
* For more HTTP API examples, see ArduinoHttpClient library
*
* NOTE: This example may NOT work with the XBee because the
* HttpClient library does not empty to serial buffer fast enough
* and the buffer overflow causes the HttpClient library to stall.
* Boards with faster processors may work, 8MHz boards will not.
**************************************************************/
// Select your modem:
#define TINY_GSM_MODEM_SIM800
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM868
// #define TINY_GSM_MODEM_SIM7000SSL
// #define TINY_GSM_MODEM_SIM7080
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_ESP32
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// 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
#ifndef __AVR_ATmega328P__
#define SerialAT Serial1
// or Software Serial on Uno, Nano
#else
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(2, 3); // RX, TX
#endif
// Increase RX buffer to capture the entire response
// Chips without internal buffering (A6/A7, ESP8266, M590)
// need enough space in the buffer for the entire response
// else data will be lost (and the http library will fail).
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 650
#endif
// See all AT commands, if wanted
// #define DUMP_AT_COMMANDS
// Define the serial console for debug prints, if needed
#define TINY_GSM_DEBUG SerialMon
// #define LOGGING // <- Logging is for the HTTP library
// Range to attempt to autobaud
// NOTE: DO NOT AUTOBAUD in production code. Once you've established
// communication, set a fixed baud rate using modem.setBaud(#).
#define GSM_AUTOBAUD_MIN 9600
#define GSM_AUTOBAUD_MAX 115200
// Add a reception delay, if needed.
// This may be needed for a fast processor at a slow baud rate.
// #define TINY_GSM_YIELD() { delay(2); }
// Define how you're planning to connect to the internet.
// This is only needed for this example, not in other code.
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
// set GSM PIN, if any
#define GSM_PIN ""
// flag to force SSL client authentication, if needed
// #define TINY_GSM_SSL_CLIENT_AUTHENTICATION
// Your GPRS credentials, if any
const char apn[] = "YourAPN";
const char gprsUser[] = "";
const char gprsPass[] = "";
// Your WiFi connection credentials, if applicable
const char wifiSSID[] = "YourSSID";
const char wifiPass[] = "YourWiFiPass";
// Server details
const char server[] = "vsh.pp.ua";
const char resource[] = "/TinyGSM/logo.txt";
const int port = 443;
#include <TinyGsmClient.h>
#include <ArduinoHttpClient.h>
// Just in case someone defined the wrong thing..
#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS false
#define TINY_GSM_USE_WIFI true
#endif
#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#endif
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
TinyGsmClientSecure client(modem);
HttpClient http(client, server, port);
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
// !!!!!!!!!!!
// Set your reset, enable, power pins here
// !!!!!!!!!!!
SerialMon.println("Wait...");
// Set GSM module baud rate
TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);
// SerialAT.begin(9600);
delay(6000);
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.println("Initializing modem...");
modem.restart();
// modem.init();
String modemInfo = modem.getModemInfo();
SerialMon.print("Modem Info: ");
SerialMon.println(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
}
void loop() {
#if TINY_GSM_USE_WIFI
// Wifi connection parameters must be set before waiting for the network
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
#endif
#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_XBEE
// The XBee must run the gprsConnect function BEFORE waiting for network!
modem.gprsConnect(apn, gprsUser, gprsPass);
#endif
SerialMon.print("Waiting for network...");
if (!modem.waitForNetwork()) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
if (modem.isNetworkConnected()) { SerialMon.println("Network connected"); }
#if TINY_GSM_USE_GPRS
// GPRS connection parameters are usually set after network registration
SerialMon.print(F("Connecting to "));
SerialMon.print(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
if (modem.isGprsConnected()) { SerialMon.println("GPRS connected"); }
#endif
SerialMon.print(F("Performing HTTPS GET request... "));
http.connectionKeepAlive(); // Currently, this is needed for HTTPS
int err = http.get(resource);
if (err != 0) {
SerialMon.println(F("failed to connect"));
delay(10000);
return;
}
int status = http.responseStatusCode();
SerialMon.print(F("Response status code: "));
SerialMon.println(status);
if (!status) {
delay(10000);
return;
}
SerialMon.println(F("Response Headers:"));
while (http.headerAvailable()) {
String headerName = http.readHeaderName();
String headerValue = http.readHeaderValue();
SerialMon.println(" " + headerName + " : " + headerValue);
}
int length = http.contentLength();
if (length >= 0) {
SerialMon.print(F("Content length is: "));
SerialMon.println(length);
}
if (http.isResponseChunked()) {
SerialMon.println(F("The response is chunked"));
}
String body = http.responseBody();
SerialMon.println(F("Response:"));
SerialMon.println(body);
SerialMon.print(F("Body length is: "));
SerialMon.println(body.length());
// Shutdown
http.stop();
SerialMon.println(F("Server disconnected"));
#if TINY_GSM_USE_WIFI
modem.networkDisconnect();
SerialMon.println(F("WiFi disconnected"));
#endif
#if TINY_GSM_USE_GPRS
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
#endif
// Do nothing forevermore
while (true) { delay(1000); }
}

View file

@ -0,0 +1,294 @@
/**************************************************************
*
* For this example, you need to install PubSubClient library:
* https://github.com/knolleary/pubsubclient
* or from http://librarymanager/all#PubSubClient
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
* For more MQTT examples, see PubSubClient library
*
**************************************************************
* This example connects to HiveMQ's showcase broker.
*
* You can quickly test sending and receiving messages from the HiveMQ webclient
* available at http://www.hivemq.com/demos/websocket-client/.
*
* Subscribe to the topic GsmClientTest/ledStatus
* Publish "toggle" to the topic GsmClientTest/led and the LED on your board
* should toggle and you should see a new message published to
* GsmClientTest/ledStatus with the newest LED status.
*
**************************************************************/
// Select your modem:
#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_SIM7000SSL
// #define TINY_GSM_MODEM_SIM7080
// #define TINY_GSM_MODEM_SIM5360
// #define TINY_GSM_MODEM_SIM7600
// #define TINY_GSM_MODEM_A7672X
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_SARAR5
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_ESP32
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// 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
#ifndef __AVR_ATmega328P__
#define SerialAT Serial1
// or Software Serial on Uno, Nano
#else
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(2, 3); // RX, TX
#endif
// See all AT commands, if wanted
// #define DUMP_AT_COMMANDS
// Define the serial console for debug prints, if needed
#define TINY_GSM_DEBUG SerialMon
// Range to attempt to autobaud
// NOTE: DO NOT AUTOBAUD in production code. Once you've established
// communication, set a fixed baud rate using modem.setBaud(#).
#define GSM_AUTOBAUD_MIN 9600
#define GSM_AUTOBAUD_MAX 115200
// Add a reception delay, if needed.
// This may be needed for a fast processor at a slow baud rate.
// #define TINY_GSM_YIELD() { delay(2); }
// Define how you're planning to connect to the internet.
// This is only needed for this example, not in other code.
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
// set GSM PIN, if any
#define GSM_PIN ""
// Your GPRS credentials, if any
const char apn[] = "YourAPN";
const char gprsUser[] = "";
const char gprsPass[] = "";
// Your WiFi connection credentials, if applicable
const char wifiSSID[] = "YourSSID";
const char wifiPass[] = "YourWiFiPass";
// MQTT details
const char* broker = "broker.hivemq.com";
const char* topicLed = "GsmClientTest/led";
const char* topicInit = "GsmClientTest/init";
const char* topicLedStatus = "GsmClientTest/ledStatus";
#include <TinyGsmClient.h>
#include <PubSubClient.h>
// Just in case someone defined the wrong thing..
#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS false
#define TINY_GSM_USE_WIFI true
#endif
#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#endif
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
TinyGsmClient client(modem);
PubSubClient mqtt(client);
#define LED_PIN 13
int ledStatus = LOW;
uint32_t lastReconnectAttempt = 0;
void mqttCallback(char* topic, byte* payload, unsigned int len) {
SerialMon.print("Message arrived [");
SerialMon.print(topic);
SerialMon.print("]: ");
SerialMon.write(payload, len);
SerialMon.println();
// Only proceed if incoming message's topic matches
if (String(topic) == topicLed) {
ledStatus = !ledStatus;
digitalWrite(LED_PIN, ledStatus);
mqtt.publish(topicLedStatus, ledStatus ? "1" : "0");
}
}
boolean mqttConnect() {
SerialMon.print("Connecting to ");
SerialMon.print(broker);
// Connect to MQTT Broker
boolean status = mqtt.connect("GsmClientTest");
// Or, if you want to authenticate MQTT:
// boolean status = mqtt.connect("GsmClientName", "mqtt_user", "mqtt_pass");
if (status == false) {
SerialMon.println(" fail");
return false;
}
SerialMon.println(" success");
mqtt.publish(topicInit, "GsmClientTest started");
mqtt.subscribe(topicLed);
return mqtt.connected();
}
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
pinMode(LED_PIN, OUTPUT);
// !!!!!!!!!!!
// Set your reset, enable, power pins here
// !!!!!!!!!!!
SerialMon.println("Wait...");
// Set GSM module baud rate
TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);
// SerialAT.begin(9600);
delay(6000);
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.println("Initializing modem...");
modem.restart();
// modem.init();
String modemInfo = modem.getModemInfo();
SerialMon.print("Modem Info: ");
SerialMon.println(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
// Wifi connection parameters must be set before waiting for the network
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
#endif
#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_XBEE
// The XBee must run the gprsConnect function BEFORE waiting for network!
modem.gprsConnect(apn, gprsUser, gprsPass);
#endif
SerialMon.print("Waiting for network...");
if (!modem.waitForNetwork()) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
if (modem.isNetworkConnected()) { SerialMon.println("Network connected"); }
#if TINY_GSM_USE_GPRS
// GPRS connection parameters are usually set after network registration
SerialMon.print(F("Connecting to "));
SerialMon.print(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
if (modem.isGprsConnected()) { SerialMon.println("GPRS connected"); }
#endif
// MQTT Broker setup
mqtt.setServer(broker, 1883);
mqtt.setCallback(mqttCallback);
}
void loop() {
// Make sure we're still registered on the network
if (!modem.isNetworkConnected()) {
SerialMon.println("Network disconnected");
if (!modem.waitForNetwork(180000L, true)) {
SerialMon.println(" fail");
delay(10000);
return;
}
if (modem.isNetworkConnected()) {
SerialMon.println("Network re-connected");
}
#if TINY_GSM_USE_GPRS
// and make sure GPRS/EPS is still connected
if (!modem.isGprsConnected()) {
SerialMon.println("GPRS disconnected!");
SerialMon.print(F("Connecting to "));
SerialMon.print(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
if (modem.isGprsConnected()) { SerialMon.println("GPRS reconnected"); }
}
#endif
}
if (!mqtt.connected()) {
SerialMon.println("=== MQTT NOT CONNECTED ===");
// Reconnect every 10 seconds
uint32_t t = millis();
if (t - lastReconnectAttempt > 10000L) {
lastReconnectAttempt = t;
if (mqttConnect()) { lastReconnectAttempt = 0; }
}
delay(100);
return;
}
mqtt.loop();
}

View file

@ -0,0 +1,252 @@
/**************************************************************
*
* This sketch connects to a website and downloads a page.
* It can be used to perform HTTP/RESTful API calls.
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
**************************************************************/
// Select your modem:
#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_SIM7000SSL
// #define TINY_GSM_MODEM_SIM7080
// #define TINY_GSM_MODEM_SIM5360
// #define TINY_GSM_MODEM_SIM7600
// #define TINY_GSM_MODEM_A7672X
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_SARAR5
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_ESP32
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// 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
#ifndef __AVR_ATmega328P__
#define SerialAT Serial1
// or Software Serial on Uno, Nano
#else
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(2, 3); // RX, TX
#endif
// Increase RX buffer to capture the entire response
// Chips without internal buffering (A6/A7, ESP8266, M590)
// need enough space in the buffer for the entire response
// else data will be lost (and the http library will fail).
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 650
#endif
// See all AT commands, if wanted
// #define DUMP_AT_COMMANDS
// Define the serial console for debug prints, if needed
#define TINY_GSM_DEBUG SerialMon
// Range to attempt to autobaud
// NOTE: DO NOT AUTOBAUD in production code. Once you've established
// communication, set a fixed baud rate using modem.setBaud(#).
#define GSM_AUTOBAUD_MIN 9600
#define GSM_AUTOBAUD_MAX 115200
// Add a reception delay, if needed.
// This may be needed for a fast processor at a slow baud rate.
// #define TINY_GSM_YIELD() { delay(2); }
// Uncomment this if you want to use SSL
// #define USE_SSL
// Define how you're planning to connect to the internet.
// This is only needed for this example, not in other code.
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
// set GSM PIN, if any
#define GSM_PIN ""
// Your GPRS credentials, if any
const char apn[] = "YourAPN";
const char gprsUser[] = "";
const char gprsPass[] = "";
// Your WiFi connection credentials, if applicable
const char wifiSSID[] = "YourSSID";
const char wifiPass[] = "YourWiFiPass";
// Server details
const char server[] = "vsh.pp.ua";
const char resource[] = "/TinyGSM/logo.txt";
#include <TinyGsmClient.h>
// Just in case someone defined the wrong thing..
#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS false
#define TINY_GSM_USE_WIFI true
#endif
#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#endif
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
#if defined(TINY_GSM_MODEM_HAS_SSL)
#define USE_SSL
#endif
#ifdef USE_SSL
TinyGsmClientSecure client(modem);
const int port = 443;
#else
TinyGsmClient client(modem);
const int port = 80;
#endif
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
// !!!!!!!!!!!
// Set your reset, enable, power pins here
// !!!!!!!!!!!
SerialMon.println("Wait...");
// Set GSM module baud rate
TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);
// SerialAT.begin(9600);
delay(6000);
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.println("Initializing modem...");
modem.restart();
// modem.init();
String modemInfo = modem.getModemInfo();
SerialMon.print("Modem Info: ");
SerialMon.println(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
}
void loop() {
#if TINY_GSM_USE_WIFI
// Wifi connection parameters must be set before waiting for the network
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
#endif
#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_XBEE
// The XBee must run the gprsConnect function BEFORE waiting for network!
modem.gprsConnect(apn, gprsUser, gprsPass);
#endif
SerialMon.print("Waiting for network...");
if (!modem.waitForNetwork()) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
if (modem.isNetworkConnected()) { SerialMon.println("Network connected"); }
#if TINY_GSM_USE_GPRS
// GPRS connection parameters are usually set after network registration
SerialMon.print(F("Connecting to "));
SerialMon.print(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
if (modem.isGprsConnected()) { SerialMon.println("GPRS connected"); }
#endif
SerialMon.print("Connecting to ");
SerialMon.println(server);
if (!client.connect(server, port)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
// Make a HTTP GET request:
SerialMon.println("Performing HTTP GET request...");
client.print(String("GET ") + resource + " HTTP/1.1\r\n");
client.print(String("Host: ") + server + "\r\n");
client.print("Connection: close\r\n\r\n");
client.println();
uint32_t timeout = millis();
while (client.connected() && millis() - timeout < 10000L) {
// Print available data
while (client.available()) {
char c = client.read();
SerialMon.print(c);
timeout = millis();
}
}
SerialMon.println();
// Shutdown
client.stop();
SerialMon.println(F("Server disconnected"));
#if TINY_GSM_USE_WIFI
modem.networkDisconnect();
SerialMon.println(F("WiFi disconnected"));
#endif
#if TINY_GSM_USE_GPRS
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
#endif
// Do nothing forevermore
while (true) { delay(1000); }
}

View file

@ -0,0 +1,41 @@
[
{
"name": "PubSubClient",
"owner": "knolleary",
"library id": "89",
"url": "https://github.com/knolleary/pubsubclient.git",
"version": "~2.8",
"note": "A client library for MQTT messaging.",
"authors": ["Nick O'Leary"]
},
{
"name": "Blynk",
"owner": "blynkkk",
"library id": "415",
"url": "https://github.com/blynkkk/blynk-library.git",
"version": "~0.6.7",
"authors": ["Volodymyr Shymanskyy"],
"frameworks": "*",
"platforms": "*"
},
{
"name": "AceCRC",
"owner": "bxparks",
"library id": "1202",
"url": "https://github.com/bxparks/AceCRC.git",
"version": "~1.0.1",
"authors": ["Brian T. Park"],
"frameworks": "*",
"platforms": "*"
},
{
"name": "StreamDebugger",
"owner": "vshymanskyy",
"library id": "1286",
"url": "https://github.com/vshymanskyy/StreamDebugger.git",
"version": "~1.0.1",
"authors": ["Volodymyr Shymanskyy"],
"frameworks": "*",
"platforms": "*"
}
]

View file

@ -0,0 +1,136 @@
/**************************************************************
*
* This sketch connects to a website and downloads a page.
* It can be used to perform HTTP/RESTful API calls.
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
**************************************************************/
// Hologram Dash uses UBLOX U2 modems
#define TINY_GSM_MODEM_UBLOX
// Increase RX buffer if needed
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 512
#endif
#include <TinyGsmClient.h>
// Uncomment this if you want to see all AT commands
// #define DUMP_AT_COMMANDS
// Uncomment this if you want to use SSL
// #define USE_SSL
// Set serial for debug console (to the Serial Monitor, speed 115200)
#define SerialMon Serial
// We'll be using SerialSystem in Passthrough mode
#define SerialAT SerialSystem
// Your GPRS credentials
// Leave empty, if missing user or pass
const char apn[] = "YourAPN";
const char user[] = "";
const char pass[] = "";
// Server details
const char server[] = "vsh.pp.ua";
const char resource[] = "/TinyGSM/logo.txt";
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm mdm(debugger);
#else
TinyGsm mdm(SerialAT);
#endif
#ifdef USE_SSL
TinyGsmClientSecure client(mdm);
const int port = 443;
#else
TinyGsmClient client(mdm);
const int port = 80;
#endif
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
// Set up Passthrough
HologramCloud.enterPassthrough();
delay(6000);
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.println(F("Initializing modem..."));
mdm.restart();
String modemInfo = mdm.getModemInfo();
SerialMon.print(F("Modem: "));
SerialMon.println(modemInfo);
// Unlock your SIM card with a PIN
//mdm.simUnlock("1234");
}
void loop() {
SerialMon.print(F("Waiting for network..."));
if (!mdm.waitForNetwork()) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
SerialMon.print(F("Connecting to "));
SerialMon.print(apn);
if (!mdm.gprsConnect(apn, user, pass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
SerialMon.print(F("Connecting to "));
SerialMon.print(server);
if (!client.connect(server, port)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
// Make a HTTP GET request:
client.print(String("GET ") + resource + " HTTP/1.0\r\n");
client.print(String("Host: ") + server + "\r\n");
client.print("Connection: close\r\n\r\n");
uint32_t timeout = millis();
while (client.connected() && millis() - timeout < 10000L) {
// Print available data
while (client.available()) {
char c = client.read();
SerialMon.print(c);
timeout = millis();
}
}
SerialMon.println();
// Shutdown
client.stop();
SerialMon.println(F("Server disconnected"));
mdm.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
// Do nothing forevermore
while (true) {
delay(1000);
}
}

View file

@ -0,0 +1,161 @@
/**************************************************************
*
* This sketch connects to a website and downloads a page.
* It can be used to perform HTTP/RESTful API calls.
*
* For this example, you need to install ArduinoHttpClient library:
* https://github.com/arduino-libraries/ArduinoHttpClient
* or from http://librarymanager/all#ArduinoHttpClient
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
* For more HTTP API examples, see ArduinoHttpClient library
*
**************************************************************/
// Industruino uses SIM800H
#define TINY_GSM_MODEM_SIM800
// Increase RX buffer if needed
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 512
#endif
#include <TinyGsmClient.h>
#include <ArduinoHttpClient.h>
// Uncomment this if you want to see all AT commands
// #define DUMP_AT_COMMANDS
// Uncomment this if you want to use SSL
// #define USE_SSL
// Set serial for debug console (to the Serial Monitor, speed 115200)
#define SerialMon SerialUSB
// Select Serial1 or Serial depending on your module configuration
#define SerialAT Serial1
// Your GPRS credentials
// Leave empty, if missing user or pass
const char apn[] = "YourAPN";
const char user[] = "";
const char pass[] = "";
// Server details
const char server[] = "vsh.pp.ua";
const char resource[] = "/TinyGSM/logo.txt";
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
#ifdef USE_SSL
TinyGsmClientSecure client(modem);
HttpClient http(client, server, 443);
#else
TinyGsmClient client(modem);
HttpClient http(client, server, 80);
#endif
void setup() {
// Turn on modem with 1 second pulse on D6
pinMode(6, OUTPUT);
digitalWrite(6, HIGH);
delay(1000);
digitalWrite(6, LOW);
// Set console baud rate
SerialMon.begin(115200);
delay(10);
// Set GSM module baud rate
SerialAT.begin(115200);
delay(6000);
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.println(F("Initializing modem..."));
modem.restart();
String modemInfo = modem.getModemInfo();
SerialMon.print(F("Modem: "));
SerialMon.println(modemInfo);
// Unlock your SIM card with a PIN
//modem.simUnlock("1234");
}
void loop() {
SerialMon.print(F("Waiting for network..."));
if (!modem.waitForNetwork()) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
SerialMon.print(F("Connecting to "));
SerialMon.print(apn);
if (!modem.gprsConnect(apn, user, pass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
SerialMon.print(F("Performing HTTP GET request... "));
int err = http.get(resource);
if (err != 0) {
SerialMon.println(F("failed to connect"));
delay(10000);
return;
}
int status = http.responseStatusCode();
SerialMon.println(status);
if (!status) {
delay(10000);
return;
}
while (http.headerAvailable()) {
String headerName = http.readHeaderName();
String headerValue = http.readHeaderValue();
//SerialMon.println(headerName + " : " + headerValue);
}
int length = http.contentLength();
if (length >= 0) {
SerialMon.print(F("Content length is: "));
SerialMon.println(length);
}
if (http.isResponseChunked()) {
SerialMon.println(F("The response is chunked"));
}
String body = http.responseBody();
SerialMon.println(F("Response:"));
SerialMon.println(body);
SerialMon.print(F("Body length is: "));
SerialMon.println(body.length());
// Shutdown
http.stop();
SerialMon.println(F("Server disconnected"));
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
// Do nothing forevermore
while (true) {
delay(1000);
}
}

View file

@ -0,0 +1,36 @@
const char cert[] PROGMEM =
"-----BEGIN CERTIFICATE-----\n"
"MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB\n"
"hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n"
"A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV\n"
"BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5\n"
"MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT\n"
"EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR\n"
"Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh\n"
"dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR\n"
"6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X\n"
"pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC\n"
"9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV\n"
"/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf\n"
"Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z\n"
"+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w\n"
"qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah\n"
"SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC\n"
"u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf\n"
"Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq\n"
"crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E\n"
"FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB\n"
"/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl\n"
"wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM\n"
"4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV\n"
"2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna\n"
"FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ\n"
"CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK\n"
"boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke\n"
"jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL\n"
"S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb\n"
"QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl\n"
"0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB\n"
"NVOFBkpdn627G190\n"
"-----END CERTIFICATE-----\n";

View file

@ -0,0 +1,74 @@
const char cert[] PROGMEM =
{
0x30, 0x82, 0x03, 0x4a, 0x30, 0x82, 0x02, 0x32, 0xa0, 0x03, 0x02, 0x01,
0x02, 0x02, 0x10, 0x44, 0xaf, 0xb0, 0x80, 0xd6, 0xa3, 0x27, 0xba, 0x89,
0x30, 0x39, 0x86, 0x2e, 0xf8, 0x40, 0x6b, 0x30, 0x0d, 0x06, 0x09, 0x2a,
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x3f,
0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1b, 0x44,
0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61,
0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43,
0x6f, 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
0x0e, 0x44, 0x53, 0x54, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
0x20, 0x58, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x30, 0x30, 0x39, 0x33,
0x30, 0x32, 0x31, 0x31, 0x32, 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x32, 0x31,
0x30, 0x39, 0x33, 0x30, 0x31, 0x34, 0x30, 0x31, 0x31, 0x35, 0x5a, 0x30,
0x3f, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1b,
0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x53, 0x69, 0x67, 0x6e,
0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
0x43, 0x6f, 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03,
0x13, 0x0e, 0x44, 0x53, 0x54, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
0x41, 0x20, 0x58, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
0x00, 0xdf, 0xaf, 0xe9, 0x97, 0x50, 0x08, 0x83, 0x57, 0xb4, 0xcc, 0x62,
0x65, 0xf6, 0x90, 0x82, 0xec, 0xc7, 0xd3, 0x2c, 0x6b, 0x30, 0xca, 0x5b,
0xec, 0xd9, 0xc3, 0x7d, 0xc7, 0x40, 0xc1, 0x18, 0x14, 0x8b, 0xe0, 0xe8,
0x33, 0x76, 0x49, 0x2a, 0xe3, 0x3f, 0x21, 0x49, 0x93, 0xac, 0x4e, 0x0e,
0xaf, 0x3e, 0x48, 0xcb, 0x65, 0xee, 0xfc, 0xd3, 0x21, 0x0f, 0x65, 0xd2,
0x2a, 0xd9, 0x32, 0x8f, 0x8c, 0xe5, 0xf7, 0x77, 0xb0, 0x12, 0x7b, 0xb5,
0x95, 0xc0, 0x89, 0xa3, 0xa9, 0xba, 0xed, 0x73, 0x2e, 0x7a, 0x0c, 0x06,
0x32, 0x83, 0xa2, 0x7e, 0x8a, 0x14, 0x30, 0xcd, 0x11, 0xa0, 0xe1, 0x2a,
0x38, 0xb9, 0x79, 0x0a, 0x31, 0xfd, 0x50, 0xbd, 0x80, 0x65, 0xdf, 0xb7,
0x51, 0x63, 0x83, 0xc8, 0xe2, 0x88, 0x61, 0xea, 0x4b, 0x61, 0x81, 0xec,
0x52, 0x6b, 0xb9, 0xa2, 0xe2, 0x4b, 0x1a, 0x28, 0x9f, 0x48, 0xa3, 0x9e,
0x0c, 0xda, 0x09, 0x8e, 0x3e, 0x17, 0x2e, 0x1e, 0xdd, 0x20, 0xdf, 0x5b,
0xc6, 0x2a, 0x8a, 0xab, 0x2e, 0xbd, 0x70, 0xad, 0xc5, 0x0b, 0x1a, 0x25,
0x90, 0x74, 0x72, 0xc5, 0x7b, 0x6a, 0xab, 0x34, 0xd6, 0x30, 0x89, 0xff,
0xe5, 0x68, 0x13, 0x7b, 0x54, 0x0b, 0xc8, 0xd6, 0xae, 0xec, 0x5a, 0x9c,
0x92, 0x1e, 0x3d, 0x64, 0xb3, 0x8c, 0xc6, 0xdf, 0xbf, 0xc9, 0x41, 0x70,
0xec, 0x16, 0x72, 0xd5, 0x26, 0xec, 0x38, 0x55, 0x39, 0x43, 0xd0, 0xfc,
0xfd, 0x18, 0x5c, 0x40, 0xf1, 0x97, 0xeb, 0xd5, 0x9a, 0x9b, 0x8d, 0x1d,
0xba, 0xda, 0x25, 0xb9, 0xc6, 0xd8, 0xdf, 0xc1, 0x15, 0x02, 0x3a, 0xab,
0xda, 0x6e, 0xf1, 0x3e, 0x2e, 0xf5, 0x5c, 0x08, 0x9c, 0x3c, 0xd6, 0x83,
0x69, 0xe4, 0x10, 0x9b, 0x19, 0x2a, 0xb6, 0x29, 0x57, 0xe3, 0xe5, 0x3d,
0x9b, 0x9f, 0xf0, 0x02, 0x5d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x42,
0x30, 0x40, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55,
0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xc4, 0xa7,
0xb1, 0xa4, 0x7b, 0x2c, 0x71, 0xfa, 0xdb, 0xe1, 0x4b, 0x90, 0x75, 0xff,
0xc4, 0x15, 0x60, 0x85, 0x89, 0x10, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
0x01, 0x00, 0xa3, 0x1a, 0x2c, 0x9b, 0x17, 0x00, 0x5c, 0xa9, 0x1e, 0xee,
0x28, 0x66, 0x37, 0x3a, 0xbf, 0x83, 0xc7, 0x3f, 0x4b, 0xc3, 0x09, 0xa0,
0x95, 0x20, 0x5d, 0xe3, 0xd9, 0x59, 0x44, 0xd2, 0x3e, 0x0d, 0x3e, 0xbd,
0x8a, 0x4b, 0xa0, 0x74, 0x1f, 0xce, 0x10, 0x82, 0x9c, 0x74, 0x1a, 0x1d,
0x7e, 0x98, 0x1a, 0xdd, 0xcb, 0x13, 0x4b, 0xb3, 0x20, 0x44, 0xe4, 0x91,
0xe9, 0xcc, 0xfc, 0x7d, 0xa5, 0xdb, 0x6a, 0xe5, 0xfe, 0xe6, 0xfd, 0xe0,
0x4e, 0xdd, 0xb7, 0x00, 0x3a, 0xb5, 0x70, 0x49, 0xaf, 0xf2, 0xe5, 0xeb,
0x02, 0xf1, 0xd1, 0x02, 0x8b, 0x19, 0xcb, 0x94, 0x3a, 0x5e, 0x48, 0xc4,
0x18, 0x1e, 0x58, 0x19, 0x5f, 0x1e, 0x02, 0x5a, 0xf0, 0x0c, 0xf1, 0xb1,
0xad, 0xa9, 0xdc, 0x59, 0x86, 0x8b, 0x6e, 0xe9, 0x91, 0xf5, 0x86, 0xca,
0xfa, 0xb9, 0x66, 0x33, 0xaa, 0x59, 0x5b, 0xce, 0xe2, 0xa7, 0x16, 0x73,
0x47, 0xcb, 0x2b, 0xcc, 0x99, 0xb0, 0x37, 0x48, 0xcf, 0xe3, 0x56, 0x4b,
0xf5, 0xcf, 0x0f, 0x0c, 0x72, 0x32, 0x87, 0xc6, 0xf0, 0x44, 0xbb, 0x53,
0x72, 0x6d, 0x43, 0xf5, 0x26, 0x48, 0x9a, 0x52, 0x67, 0xb7, 0x58, 0xab,
0xfe, 0x67, 0x76, 0x71, 0x78, 0xdb, 0x0d, 0xa2, 0x56, 0x14, 0x13, 0x39,
0x24, 0x31, 0x85, 0xa2, 0xa8, 0x02, 0x5a, 0x30, 0x47, 0xe1, 0xdd, 0x50,
0x07, 0xbc, 0x02, 0x09, 0x90, 0x00, 0xeb, 0x64, 0x63, 0x60, 0x9b, 0x16,
0xbc, 0x88, 0xc9, 0x12, 0xe6, 0xd2, 0x7d, 0x91, 0x8b, 0xf9, 0x3d, 0x32,
0x8d, 0x65, 0xb4, 0xe9, 0x7c, 0xb1, 0x57, 0x76, 0xea, 0xc5, 0xb6, 0x28,
0x39, 0xbf, 0x15, 0x65, 0x1c, 0xc8, 0xf6, 0x77, 0x96, 0x6a, 0x0a, 0x8d,
0x77, 0x0b, 0xd8, 0x91, 0x0b, 0x04, 0x8e, 0x07, 0xdb, 0x29, 0xb6, 0x0a,
0xee, 0x9d, 0x82, 0x35, 0x35, 0x10
};

View file

@ -0,0 +1,22 @@
const char cert[] PROGMEM =
"-----BEGIN CERTIFICATE-----\n"
"MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\n"
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n"
"DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\n"
"PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\n"
"Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n"
"AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\n"
"rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\n"
"OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\n"
"xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\n"
"7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\n"
"aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\n"
"HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\n"
"SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\n"
"ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\n"
"AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\n"
"R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\n"
"JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\n"
"Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n"
"-----END CERTIFICATE-----\n";

View file

@ -0,0 +1,90 @@
/**************************************************************
*
* This sketch uploads SSL certificates to the SIM8xx
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
**************************************************************/
// This example is specific to SIM8xx
#define TINY_GSM_MODEM_SIM800
// Select your certificate:
#include "DSTRootCAX3.h"
//#include "DSTRootCAX3.der.h"
//#include "COMODORSACertificationAuthority.h"
// Select the file you want to write into
// (the file is stored on the modem)
#define CERT_FILE "C:\\USER\\CERT.CRT"
#include <TinyGsmClient.h>
// Set serial for debug console (to the Serial Monitor, speed 115200)
#define SerialMon Serial
// Use Hardware Serial for AT commands
#define SerialAT Serial1
// Uncomment this if you want to see all AT commands
// #define DUMP_AT_COMMANDS
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
// Set GSM module baud rate
SerialAT.begin(115200);
delay(6000);
SerialMon.println(F("Initializing modem..."));
modem.init();
modem.sendAT(GF("+FSCREATE=" CERT_FILE));
if (modem.waitResponse() != 1) return;
const int cert_size = sizeof(cert);
modem.sendAT(GF("+FSWRITE=" CERT_FILE ",0,"), cert_size, GF(",10"));
if (modem.waitResponse(GF(">")) != 1) { return; }
for (int i = 0; i < cert_size; i++) {
char c = pgm_read_byte(&cert[i]);
modem.stream.write(c);
}
modem.stream.write(AT_NL);
modem.stream.flush();
if (modem.waitResponse(2000) != 1) return;
modem.sendAT(GF("+SSLSETCERT=\"" CERT_FILE "\""));
if (modem.waitResponse() != 1) return;
if (modem.waitResponse(5000L, GF(AT_NL "+SSLSETCERT:")) != 1) return;
const int retCode = modem.stream.readStringUntil('\n').toInt();
SerialMon.println();
SerialMon.println();
SerialMon.println(F("****************************"));
SerialMon.print(F("Setting Certificate: "));
SerialMon.println((0 == retCode) ? "OK" : "FAILED");
SerialMon.println(F("****************************"));
}
void loop() {
if (SerialAT.available()) { SerialMon.write(SerialAT.read()); }
if (SerialMon.available()) { SerialAT.write(SerialMon.read()); }
delay(0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="500" height="80"
>
<text x="0" y="0" font-size="18px" font-family="'Ubuntu Mono',monospace" white-space="pre" xml:space="preserve" >
<tspan x="0" dy="1em"> _____ _____ _____ _____ </tspan>
<tspan x="0" dy="1em"> | | |\ | \_/ | ___ |_____ | | |</tspan>
<tspan x="0" dy="1em"> | | | \| | |_____| _____|| | |</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 533 B

View file

@ -0,0 +1,5 @@
_____ _____ _____ _____
| | |\ | \_/ | ___ |_____ | | |
| | | \| | |_____| _____|| | |

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -0,0 +1,209 @@
7d b4 24 2f 83 f0 f8 7f 8e e6 1c ae c8 3f 8c 51
6b ac 77 80 f5 13 90 37 fa 05 ee f5 b5 21 3a 94
b8 73 d6 7d 22 93 75 44 9b d0 40 fe 98 c5 b1 0d
3a 72 d9 fe 7a d5 6a e0 9a 77 4e 82 66 57 25 93
09 90 65 fc d7 96 1d 6d 79 f6 38 e7 4a cc ad 03
05 dd fa b5 1d 4d 28 62 a2 c0 86 40 16 5a e7 08
99 81 97 81 ec 74 77 d1 7d e4 e0 ea a6 71 6c 53
19 a0 5c 7b bc 02 76 22 e3 b2 2f 26 4a 09 10 a3
dd fc 8d 1f 5e a9 f1 7f 22 82 2d ca 71 26 24 7d
95 4e 11 ea ad d9 ac 23 c8 e2 f0 fd b9 30 0a 65
d7 79 31 ab 29 a2 68 f1 ee 21 50 5d 33 8a 72 97
34 bd f1 4d 24 f9 62 b7 93 21 80 c6 33 8d bf 5e
40 17 de 57 e5 75 7b 5c f0 80 0f cd 2e f9 83 3e
96 c3 62 60 d7 44 b3 2f df 23 4f 10 d1 cd ac 70
18 e5 ea 17 29 aa 77 af c5 5b c3 37 6d 26 72 71
e2 09 b5 be 63 a5 ca 17 d9 2a b9 67 f2 2c b8 f1
13 4c 41 e2 61 3f 8f 96 8c 7c 61 1d 7e 8e 22 98
62 57 71 d5 b6 87 39 24 9f 1e 37 27 f3 79 1e 3b
8b 41 28 28 ff 70 e0 31 29 f6 ca 63 82 48 1d 83
cf 39 91 25 45 cd 9c 40 25 d2 92 dc 20 48 90 bd
89 28 2d 28 c3 83 11 bb 9a 66 b0 fe 6e f5 30 b6
f4 42 39 2f 4e 30 65 28 c6 bc d0 ac 44 20 0b 91
da d3 d1 57 5e d9 77 b8 01 48 44 2c c5 3e ea f4
d4 ed d9 e0 1e d7 f5 89 37 3c b1 57 15 c5 79 79
38 c4 23 2e cb 64 ec 3c 94 d1 27 7d 4d a4 e0 7f
62 d9 f0 62 b8 7d a6 50 91 3d 90 44 10 01 b0 57
8a ab 87 4f 41 7e be 28 1b 6a e4 21 89 0e d2 28
46 a1 a9 ba 29 67 57 31 d2 a1 68 eb 92 5d 06 28
a5 8b b7 ff aa 91 b7 bd 29 f4 3a 3b 5c 4a 1c 26
72 37 6b a0 38 03 83 a4 29 7d b1 fc a7 ba 18 5d
b8 a1 70 fe e3 a7 01 1b 8d 65 9e 25 17 3d 94 76
a5 27 7a c0 31 8e 82 2b c5 8a 1e c0 41 7b bc 82
1b bb 64 98 55 05 33 e5 03 5a 54 65 d6 2e 0d 4a
9e 59 ac a8 41 a9 b2 6b 8e e9 19 c2 c2 f6 2c a7
d6 63 c3 59 f7 37 41 5d 97 8f 03 af 5e 74 fb ea
59 05 c3 2a a8 c5 da fd 77 f2 f9 2b ca 04 93 2a
6a da 4a a5 76 27 70 a0 43 a0 0e aa 0d 0c d5 65
95 d8 5d 27 06 eb f3 42 0a 97 91 aa ab 05 14 c4
69 54 59 75 2e c0 a6 90 bd a3 40 1c 3a 75 78 b6
fe d1 7d 9d 1f 51 32 81 63 6e cb a0 a1 fe d0 7e
90 e1 b4 5f 8c d1 ce ab c4 29 43 9f 99 82 2d 28
3c 0c 28 6f b6 fc 11 e3 6d a1 4b 71 1c 89 4a ea
a7 0d b3 d1 eb 6f 94 73 5a 3e f4 1b e8 82 5d 8e
f3 3b 10 16 9d 22 d8 9c 27 24 10 6c e0 ab 1e 6e
1d 38 d4 65 9d 38 dd 3f 47 ce b4 d8 4f cd fd cc
80 74 54 b6 f7 96 f6 82 43 ae b5 d9 6e ac 26 57
de b0 d0 21 7b 25 3f a6 36 0b 4c a9 84 c9 44 1f
02 af a1 51 8a d2 18 92 0f 2c 55 a5 19 18 c8 ed
de 9c 24 30 e0 a4 fd 2f 90 e2 9d c5 04 e1 29 6a
e9 d0 81 e4 49 0b cc b8 3c 6a 97 d4 9c 9b 18 86
97 22 38 4a 16 88 c2 9b 06 fc 3f 4f 7f 4a 3c 1d
37 be 89 07 14 fe 94 a2 13 03 62 38 9e be 92 2a
5c b4 a9 85 3b 14 80 42 c0 85 ec 36 50 ea 4b fd
1b 84 ae f4 9c 98 a4 a3 dc ac 25 b7 1a 36 0f 56
97 02 e6 4b 20 ff dc f4 8a fd a9 83 3a 4f 02 ed
81 93 b5 fb 39 39 ef dc 46 8f 63 ee 0f b8 a6 06
58 30 d0 27 59 62 a4 49 96 52 cf a3 26 73 24 c2
d0 b8 b6 b3 5e 86 e3 73 17 78 7c 89 0f 9b fe ea
92 c4 37 43 6e 77 45 2f a6 da 49 d0 e7 de ee f7
2b 40 a1 c8 79 d4 63 ff f7 aa 17 15 54 57 af 8d
b3 9c 11 f0 10 c0 ad 2f e1 9f 45 43 ec 32 09 c3
d1 17 73 99 69 41 b5 ae 31 67 02 83 67 a0 f1 bf
2b b0 20 71 a2 77 69 67 81 d9 27 86 cb 07 f5 f9
d3 f9 84 ca 98 4d 7e d7 c7 62 57 39 78 36 6b fd
a1 ea 26 26 5a f0 24 a7 5f 7e 14 55 50 39 48 fb
f5 44 9c ad 55 c5 e0 74 ce 36 85 87 f9 9d 41 86
5d 35 63 4d 0e b4 74 80 97 e7 50 17 cd 62 04 7f
02 32 70 51 20 44 fc 0b b2 e6 4d 5e b4 e9 66 a8
74 fb c7 cd d8 6a 47 86 35 ac 81 20 71 0e 27 d0
e8 24 8d 8b 8f ef 4f ac 30 22 55 a3 99 12 8e 48
13 16 cc 2d c9 95 63 75 e0 4b 1d f1 93 f1 ac 4e
a7 8f de 9c 70 a0 cb 63 e6 3f 36 d8 50 3e 99 16
85 4b 26 0f 91 23 ac 2a 40 f4 6b 05 28 ec f2 80
d6 87 0b 90 75 4a 3b 92 ec a6 89 4f 4f 0c 75 9d
7e 73 49 c8 dc e7 55 a6 83 82 c1 08 ec 34 2d 38
b9 5d be 6a 21 c4 62 87 19 02 9f 6a ad 0b 1f 98
f2 f6 e5 97 9a dd 32 1f 21 7f 34 a6 75 54 f7 1d
e7 e5 0d 6b 2e 24 64 21 d9 92 5e eb 2f fa c3 95
40 97 2d 44 b1 fa 2f e8 ea 87 4f fd 10 2b 73 e9
e1 3b d5 67 b3 64 33 e6 f4 87 7c 18 3b 11 21 a3
d9 c5 b7 b3 ae c7 52 d4 98 92 17 09 1f 60 4d bd
11 d8 7b 18 2a 89 b9 01 8d c0 f2 c5 e5 19 0b d4
d1 c9 9c 59 18 f7 f2 2a 6b 2e 9f 74 6a fc e4 44
82 65 10 72 a3 b5 7a 6a 56 19 27 5d 00 d8 d2 4b
d3 ad 4b 88 9b 45 78 e8 9d ff 33 e5 86 34 7f 70
b2 f7 33 23 00 14 cb 11 96 18 e3 89 f3 0a ea ee
47 69 c6 3c 33 a1 49 ad d2 97 af a5 f5 35 8e 1e
07 81 ee 48 32 96 e4 b1 26 6b 86 c7 de fd 31 f0
c0 2b 3f d0 77 11 33 4d 9a 36 d9 df 0d 02 34 43
ab 97 c8 c4 09 58 96 30 45 a6 52 2d 6c 5f 88 3a
1b d1 63 98 e5 7b d4 d6 c4 21 33 ad 51 3f 4b 9f
3d 58 ab 2b 51 5a 39 77 04 95 7d 8b 43 be 84 15
1a bb 90 30 2e b6 68 b1 c6 3c 45 cf 85 0c 90 99
66 f7 04 98 81 1c 66 e9 f0 1e 70 e5 bf 9b e8 32
36 7d 21 d0 7b 15 74 6c 18 fd 4b 51 5d 65 8b e7
06 9f 71 0b b9 b5 7d 15 0a e0 11 32 51 08 1f 0b
56 83 69 92 20 61 df bb 1d db 01 28 65 94 86 33
48 4b 64 6c 51 8f 4e ac 29 04 e6 2b d6 b2 91 a5
86 9e 22 10 e3 45 2a d7 2f 0c 32 7f ba 66 f3 de
4e c0 c0 2f b3 41 d1 12 fd 7e 6d 15 d4 d0 d6 4b
7f 00 83 17 b4 e8 48 af 34 63 d5 f8 64 2b 22 db
d6 95 93 07 79 51 4d 4d a9 b8 7c 4d 9b 6b f3 d3
97 87 1b c9 21 45 8d ef 18 c9 54 6a 1b 45 91 0d
77 9c c2 ac bc 12 08 68 fb 54 a6 e8 4d e6 e6 a5
6e 2f 8b c1 f2 9f d5 d9 e5 19 a2 87 9c 73 c5 b0
f1 1c 97 f8 85 b7 85 31 12 f4 ca 0d 17 d3 54 4d
b7 63 fe 88 c7 5e f2 05 95 9d d7 17 a1 c5 95 7f
30 9b c2 6a 23 40 cd a1 37 6d 9e d7 90 11 36 41
30 c9 36 27 76 02 c2 a0 42 26 24 06 c6 f0 ec 0e
76 de 93 96 52 3d fa d4 fe 28 48 99 7d a7 37 e1
ac eb e2 df 43 08 74 f8 79 15 62 1f ac e2 db 39
24 69 d6 0f 5c b8 9b 9b 83 5a 7d 21 6e 7b c9 73
42 d4 37 7e ee 28 2d 1a ec 3f 28 e7 93 8c d0 78
ea 35 cd 2c ed fb 5b d7 e4 4c c2 fb 3b 62 8f 9c
13 93 59 8a 15 f0 ff c9 99 9d fa 73 56 4a a4 a3
32 20 09 37 37 de 9f de 5c ed 73 0f 1f 4c 17 58
31 f0 ad fe 89 9b 34 d4 99 a4 4a 4b 5e 44 22 c0
b7 9e be d7 3c 59 7a a6 7e 2a 28 8d 89 8a 2e c3
22 2c c9 f8 b5 7a 81 92 f4 d8 36 14 a3 bb d6 68
b9 59 48 7d ce 0a 90 cb 53 b9 3c d4 1f 31 e6 42
ca 2f 48 c0 50 d2 12 19 ce 53 15 fa c0 b3 64 c4
52 43 56 14 6c 10 c3 6f 16 70 bc ca 38 dd 63 2e
7d 5e 36 d4 ce 61 02 06 72 95 ba f6 5d b6 15 59
c0 ff 28 5a 3a 74 3e 85 19 18 ee bc 99 bc 67 63
0f 4e 04 5a f3 d1 f0 85 11 f1 80 7a 37 fd 88 b9
c0 f5 18 36 16 8b f2 8b 3d 4c 62 1a 14 d9 36 bc
34 3d 1d 6e 44 bf 78 f3 5e 31 d1 2b 5e e5 15 be
cd 3b 48 a5 2b ca 74 93 43 3f 83 25 d0 9a cc d4
71 b4 83 50 cb 97 92 2d a9 3e b2 0c 9c 1e d8 84
8d 7e da fb 8b c8 3d 61 97 79 4a 25 d6 de 18 b8
1e bf 3a 2e 43 82 27 70 3b 5a 41 06 75 29 38 be
97 33 72 98 99 86 f9 41 b8 32 38 be 16 c6 fb d1
32 4e 0d e0 dc 1d 86 a0 94 29 62 df f3 6f f7 9b
0e 55 fb f3 99 93 34 57 68 c9 d3 a5 d0 6b 8a 2b
9b 5f ca 9f 77 a0 83 22 15 96 80 5b 98 5a d6 68
7f 75 c5 39 98 6d cb e8 f7 da dd 9f 5d 31 57 da
6b 61 9f c2 f1 d9 7e 03 6a cf 02 29 79 ad 91 25
6c 2b fb 3a 7d 08 d3 93 7a 58 a0 70 93 1c 5b 4d
42 51 f0 fd b6 da 62 7a 90 4e 2f d1 92 25 9b 59
24 4c 1c 81 be c3 6f 06 80 67 bb 28 87 e0 f6 b6
f0 42 33 5c c8 c3 2a 6f 75 e9 c5 74 f8 62 96 8d
0a af 5b 0b 83 dc 68 51 5f 63 16 ab 4c 25 e0 12
05 f2 6c 95 29 1e 02 af 42 ac 4c 6c 57 8a cc a1
d7 dd da be ad da 2e 52 87 a1 4b 7d 69 33 70 05
07 3d ac 48 df 69 56 5d 88 d0 f3 b6 da 54 22 83
28 b5 ae 98 f9 ab 01 ee f6 7d f1 27 db ed f6 d1
fe bc 68 aa 70 0a f6 dc 87 23 63 8a 48 27 d5 f8
a8 90 00 30 de e5 87 22 ee 09 6a 16 a9 3b ca 70
9c ce 0e d5 d2 55 06 ef e7 c6 31 74 ac 8e 99 d2
cb 87 ee 92 b9 54 f8 f1 15 95 0c f5 86 56 23 a4
1d 75 4b ca 6a d9 d0 9d 11 e2 e5 0c a0 7e c7 a9
84 b6 57 8a 70 ae 44 f4 f9 33 63 1a 2b 4e 35 38
11 27 08 2f b4 2d f8 66 eb 17 2e 15 53 9e 80 54
63 d7 67 ba c8 d5 0d db 06 c5 f1 4b 18 6b e6 92
3d ba 91 24 43 df 03 5b ea cb d8 d7 90 a9 35 f1
6d d7 a8 87 2f 21 50 e9 f5 1e 82 dc b8 0a 8b f7
0c 4f f3 a4 64 7c 6e 38 da 4c c7 71 eb a3 f2 fd
4b 76 a1 5b 5f 98 ba 5b 73 66 7e 85 c4 18 be 81
92 94 e3 00 af 02 40 35 97 6f 37 0f 25 6d f0 ce
71 70 0d 69 43 21 45 22 90 64 62 e0 da de f0 a4
6d 63 7b 85 f9 b9 7a fa bf 37 52 ed bd 1c c2 4c
ea 2f a2 ca 05 08 ae c2 76 27 76 6b 8b 66 82 26
28 33 c1 58 8d ef e7 96 83 bb fe e3 6b ba 98 cf
1b e4 cf b1 76 71 31 c2 c7 a0 e9 92 d1 ac d8 d3
25 3d e5 fb 26 77 2b 1f 4d ee e9 77 71 53 d6 e2
7e b5 e2 49 9e 37 8d 15 85 33 19 90 d8 a5 2e 02
31 6a 57 3b 0f 3d e3 7d 88 39 32 6d 8d d2 63 87
6e 31 59 1a 51 e0 6d 59 b5 5f 90 1a f3 5d 30 94
f1 1e 11 23 9e ba 53 94 b3 0a a2 32 3e bd 3d 8e
64 59 03 52 ec 27 cb 40 f5 e3 c9 b3 f4 e4 43 b6
cf 09 76 f6 fe da 22 3f 93 30 77 fa 7d 19 73 72
51 ed f3 80 3a 19 dd 5e dd 3d 3b 3a 29 10 c6 3b
78 35 1a eb 9b 5c 4d 09 62 8b 73 b9 51 c7 32 28
c4 94 35 e8 10 51 58 5c 5d ad 6a ad 22 00 73 20
f0 41 23 04 37 44 bc 5a be 46 be 3f 25 b6 ab ec
ce 8f 9e d5 50 a4 02 12 70 f3 7c a2 8b 19 0c da
b5 85 cb a4 59 27 c1 82 b8 93 da 06 fc aa 59 4e
b7 25 53 07 4a f0 47 46 de ea 2e ce 5e 97 59 1e
42 21 ed 56 ad 53 8f 0c 9b ae cd 99 e7 ad e2 f4
23 74 eb c0 b3 97 0a 75 f5 16 56 ed 29 79 e2 a5
45 de cc 4b 98 f4 24 08 35 6f 2e 64 cb 59 df db
f4 67 ce e6 00 af e6 d9 e4 01 5a 92 da d9 5b b0
03 fb 92 6f 0b 6e 55 19 24 0f 01 f9 c2 85 ee b3
0f d8 d6 17 f5 e5 7c 35 6a a6 94 a1 c9 c5 b5 4b
6d 66 b5 99 52 d6 77 23 07 d8 fe ab 3c 5f ba 0d
d2 3c 6c 76 aa 3c dd 73 3a 84 04 4a d7 fe 9e c7
23 07 cf e0 91 57 92 c5 89 ef 53 b7 89 85 f4 71
cf 66 4e 97 0e 9b 42 0f 6b f7 65 d3 b7 9c b0 0b
c1 c7 7f 48 2e 71 f2 a7 cc 11 50 0c c8 86 e3 03
30 26 c6 8a c7 c0 23 4b aa 11 3c 23 f6 9d 01 2a
25 18 63 93 48 13 f8 1c 60 ac ad f1 e3 e8 bb 1f
0b fb fe 6a 30 f4 98 8c a2 5c e2 42 a7 28 1d 13
92 5f e7 57 4a 7f 8d eb 0a ec 57 c8 10 d7 4c 33
dd 77 6c 95 f1 44 7d 5a 2d 2b 1e 85 4a 1f 19 94
24 88 fa 3f 6b 84 d6 c4 23 34 fc e7 30 b7 55 ec
f6 fd e5 51 20 b3 6c a0 0c a0 a7 08 8e 15 cf e5
5f f9 71 52 e8 a5 9c 63 6b 2c c5 83 e7 bc b2 7e
e5 ef 26 11 3d 7f c4 e2 b8 48 d6 90 37 43 e9 e5
ee 65 ec 65 2c 42 5a a8 33 f1 49 35 8a d1 17 2a
3d e7 83 d3 96 24 b9 42 92 9d 04 35 5d 47 17 9c
48 f3 4f f8 61 3f 17 2f 29 d6 0e 6c 7f 24 04 e4
5f f2 07 80 00 7c 29 4f 5f 60 b6 a8 32 1c 7a 09
be f8 3f be d2 82 2d fc f0 71 a0 d9 12 36 3e 91
af 3b e8 39 74 f4 64 91 2a a3 5a fb 02 d1 2e 73
7e a6 ed 7b 71 74 7f 48 c9 c7 99 22 f4 a0 2f 08
68 b5 1c 2b 4b ca 01 54 fd 83 73 3e 56 1f df f3
aa 0a 02 53 fc 7b e6 36 9b ee 22 b2 ee e2 c1 03
c6 aa 0b 94 77 6d 51 1c cf 2b c8 c9 51 ec 11 aa
75 24 66 67 ab ff 1f 74 f4 00 82 1f 33 07 4e 70

Binary file not shown.

View file

@ -0,0 +1,21 @@
cd 3b 48 a5 2b ca 74 93 43 3f 83 25 d0 9a cc d4
71 b4 83 50 cb 97 92 2d a9 3e b2 0c 9c 1e d8 84
8d 7e da fb 8b c8 3d 61 97 79 4a 25 d6 de 18 b8
1e bf 3a 2e 43 82 27 70 3b 5a 41 06 75 29 38 be
97 33 72 98 99 86 f9 41 b8 32 38 be 16 c6 fb d1
32 4e 0d e0 dc 1d 86 a0 94 29 62 df f3 6f f7 9b
0e 55 fb f3 99 93 34 57 68 c9 d3 a5 d0 6b 8a 2b
9b 5f ca 9f 77 a0 83 22 15 96 80 5b 98 5a d6 68
7f 75 c5 39 98 6d cb e8 f7 da dd 9f 5d 31 57 da
6b 61 9f c2 f1 d9 7e 03 6a cf 02 29 79 ad 91 25
6c 2b fb 3a 7d 08 d3 93 7a 58 a0 70 93 1c 5b 4d
42 51 f0 fd b6 da 62 7a 90 4e 2f d1 92 25 9b 59
24 4c 1c 81 be c3 6f 06 80 67 bb 28 87 e0 f6 b6
f0 42 33 5c c8 c3 2a 6f 75 e9 c5 74 f8 62 96 8d
0a af 5b 0b 83 dc 68 51 5f 63 16 ab 4c 25 e0 12
05 f2 6c 95 29 1e 02 af 42 ac 4c 6c 57 8a cc a1
d7 dd da be ad da 2e 52 87 a1 4b 7d 69 33 70 05
07 3d ac 48 df 69 56 5d 88 d0 f3 b6 da 54 22 83
28 b5 ae 98 f9 ab 01 ee f6 7d f1 27 db ed f6 d1
fe bc 68 aa 70 0a f6 dc 87 23 63 8a 48 27 d5 f8
a8 90 00 30 de e5 87 22 ee 09 6a 16 a9 3b F

Binary file not shown.

View file

@ -0,0 +1,10 @@
01 #$%@`"'~^&-*+=!?.,:;_\|/<>()[]{} 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
02 #$%@`"'~^&-*+=!?.,:;_\|/<>()[]{} 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
03 #$%@`"'~^&-*+=!?.,:;_\|/<>()[]{} 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
04 #$%@`"'~^&-*+=!?.,:;_\|/<>()[]{} 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
05 #$%@`"'~^&-*+=!?.,:;_\|/<>()[]{} 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
06 #$%@`"'~^&-*+=!?.,:;_\|/<>()[]{} 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
07 #$%@`"'~^&-*+=!?.,:;_\|/<>()[]{} 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
08 #$%@`"'~^&-*+=!?.,:;_\|/<>()[]{} 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
09 #$%@`"'~^&-*+=!?.,:;_\|/<>()[]{} 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
10 #$%@`"'~^&-*+=!?.,:;_\|/<>()[]{} 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

View file

@ -0,0 +1,29 @@
#######################################
# Data types (KEYWORD1)
#######################################
TinyGsm KEYWORD1
TinyGsmClient KEYWORD1
TinyGsmClientSecure KEYWORD1
SerialAT KEYWORD1
SerialMon KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
restart KEYWORD2
waitForNetwork KEYWORD2
getSimStatus KEYWORD2
gprsConnect KEYWORD2
gprsDisconnect KEYWORD2
isGprsConnected KEYWORD2
isNetworkConnected KEYWORD2
factoryReset KEYWORD2
#######################################
# Literals (LITERAL1)
#######################################
DATE_FULL LITERAL1
DATE_TIME LITERAL1
DATE_DATE LITERAL1

View file

@ -0,0 +1,32 @@
{
"name": "TinyGSM",
"version": "0.12.0",
"description": "A small Arduino library for GPRS modules, that just works. Includes examples for Blynk, MQTT, File Download, and Web Client. Supports many GSM, LTE, and WiFi modules with AT command interfaces.",
"keywords": "GSM, AT commands, AT, SIM800, SIM900, A6, A7, M590, ESP8266, SIM7000, SIM800A, SIM800C, SIM800L, SIM800H, SIM808, SIM868, SIM900A, SIM900D, SIM908, SIM968, M95, MC60, MC60E, BG96, BG95, ublox, Quectel, SIMCOM, AI Thinker, LTE, LTE-M",
"authors": [
{
"name": "Volodymyr Shymanskyy",
"url": "https://github.com/vshymanskyy",
"maintainer": true
}
],
"repository": {
"type": "git",
"url": "https://github.com/vshymanskyy/TinyGSM.git"
},
"homepage": "https://github.com/vshymanskyy/TinyGSM",
"export": {
"include": [
"LICENSE",
"library.json",
"library.properties",
"README.md",
"keywords.txt",
"src/*",
"examples/*"
]
},
"frameworks": ["arduino", "energia", "wiringpi"],
"platforms": "*",
"headers": "TinyGsmClient.h"
}

View file

@ -0,0 +1,10 @@
name=TinyGSM
version=0.12.0
author=Volodymyr Shymanskyy
maintainer=Volodymyr Shymanskyy
sentence=A small Arduino library for GPRS modules, that just works.
paragraph=Includes examples for Blynk, MQTT, File Download, and Web Client. Supports many GSM, LTE, and WiFi modules with AT command interfaces.
category=Communication
url=https://github.com/vshymanskyy/TinyGSM
architectures=*
includes=TinyGsmClient.h

View file

@ -0,0 +1,47 @@
/*
Client.h - Base class that provides Client
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef client_h
#define client_h
#include "Print.h"
#include "Stream.h"
#include "ArduinoCompat/IPAddress.h"
class Client : public Stream {
public:
virtual int connect(IPAddress ip, uint16_t port) = 0;
virtual int connect(const char* host, uint16_t port) = 0;
virtual size_t write(uint8_t) = 0;
virtual size_t write(const uint8_t* buf, size_t size) = 0;
virtual int available() = 0;
virtual int read() = 0;
virtual int read(uint8_t* buf, size_t size) = 0;
virtual int peek() = 0;
virtual void flush() = 0;
virtual void stop() = 0;
virtual uint8_t connected() = 0;
virtual operator bool() = 0;
protected:
uint8_t* rawIPAddress(IPAddress& addr) {
return addr.raw_address();
};
};
#endif

View file

@ -0,0 +1,146 @@
/*
IPAddress.h - Base class that provides IPAddress
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef IPAddress_h
#define IPAddress_h
#include <stdint.h>
#include "Printable.h"
#include "WString.h"
// A class to make it easier to handle and pass around IP addresses
class IPAddress : public Printable {
private:
union {
uint8_t bytes[4]; // IPv4 address
uint32_t dword;
} _address;
// Access the raw byte array containing the address. Because this returns a pointer
// to the internal structure rather than a copy of the address this function should only
// be used when you know that the usage of the returned uint8_t* will be transient and not
// stored.
uint8_t* raw_address() { return _address.bytes; };
public:
// Constructors
IPAddress() {
_address.dword = 0;
}
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) {
_address.bytes[0] = first_octet;
_address.bytes[1] = second_octet;
_address.bytes[2] = third_octet;
_address.bytes[3] = fourth_octet;
}
IPAddress(uint32_t address) {
_address.dword = address;
}
IPAddress(const uint8_t *address) {
memcpy(_address.bytes, address, sizeof(_address.bytes));
}
bool fromString(const char *address) {
uint16_t acc = 0; // Accumulator
uint8_t dots = 0;
while (*address)
{
char c = *address++;
if (c >= '0' && c <= '9')
{
acc = acc * 10 + (c - '0');
if (acc > 255) {
// Value out of [0..255] range
return false;
}
}
else if (c == '.')
{
if (dots == 3) {
// Too much dots (there must be 3 dots)
return false;
}
_address.bytes[dots++] = acc;
acc = 0;
}
else
{
// Invalid char
return false;
}
}
if (dots != 3) {
// Too few dots (there must be 3 dots)
return false;
}
_address.bytes[3] = acc;
return true;
}
bool fromString(const String &address) { return fromString(address.c_str()); }
// Overloaded cast operator to allow IPAddress objects to be used where a pointer
// to a four-byte uint8_t array is expected
operator uint32_t() const { return _address.dword; };
bool operator==(const IPAddress& addr) const { return _address.dword == addr._address.dword; };
bool operator==(const uint8_t* addr) const {
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
}
// Overloaded index operator to allow getting and setting individual octets of the address
uint8_t operator[](int index) const { return _address.bytes[index]; };
uint8_t& operator[](int index) { return _address.bytes[index]; };
// Overloaded copy operators to allow initialisation of IPAddress objects from other types
IPAddress& operator=(const uint8_t *address) {
memcpy(_address.bytes, address, sizeof(_address.bytes));
return *this;
}
IPAddress& operator=(uint32_t address) {
_address.dword = address;
return *this;
}
virtual size_t printTo(Print& p) const {
size_t n = 0;
for (int i =0; i < 3; i++)
{
n += p.print(_address.bytes[i], DEC);
n += p.print('.');
}
n += p.print(_address.bytes[3], DEC);
return n;
}
friend class EthernetClass;
friend class UDP;
friend class Client;
friend class Server;
friend class DhcpClass;
friend class DNSClient;
};
const IPAddress INADDR_NONE(0,0,0,0);
#endif

View file

@ -0,0 +1,6 @@
#ifndef TINYGSM_H
#define TINYGSM_H
#include "TinyGsmClient.h"
#endif

View file

@ -0,0 +1,146 @@
/**
* @file TinyGsmBattery.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMBATTERY_H_
#define SRC_TINYGSMBATTERY_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_BATTERY
template <class modemType>
class TinyGsmBattery {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* Battery functions
*/
/**
* @brief Get the current battery voltage.
*
* @note Unless you have a battery directly connected to your modem module,
* this will be the input voltage going to the module from your main processor
* board, not the battery voltage of your main processor.
*
* @return *int16_t* The battery voltage measured by the modem module.
*/
int16_t getBattVoltage() {
return thisModem().getBattVoltageImpl();
}
/**
* @brief Get the current battery percent.
*
* @note Unless you have a battery directly connected to your modem module,
* this will be the percent from the input voltage going to the module from
* your main processor board, not the battery percent of your main processor.
*
* @return *int8_t* The current battery percent.
*/
int8_t getBattPercent() {
return thisModem().getBattPercentImpl();
}
/**
* @brief Get the battery charging state.
*
* @return *int8_t* The battery charge state.
*/
int8_t getBattChargeState() {
return thisModem().getBattChargeStateImpl();
}
/**
* @brief Get the all battery state
*
* @param chargeState A reference to an int to set to the battery charge state
* @param percent A reference to an int to set to the battery percent
* @param milliVolts A reference to an int to set to the battery voltage
* @return *true* The battery stats were updated by the module.
* @return *false* There was a failure in updating the battery stats from the
* module.
*/
bool getBattStats(int8_t& chargeState, int8_t& percent, int16_t& milliVolts) {
return thisModem().getBattStatsImpl(chargeState, percent, milliVolts);
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmBattery() {}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* Battery functions
*/
protected:
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
int16_t getBattVoltageImpl() {
thisModem().sendAT(GF("+CBC"));
if (thisModem().waitResponse(GF("+CBC:")) != 1) { return 0; }
thisModem().streamSkipUntil(','); // Skip battery charge status
thisModem().streamSkipUntil(','); // Skip battery charge level
// return voltage in mV
uint16_t res = thisModem().streamGetIntBefore('\n');
// Wait for final OK
thisModem().waitResponse();
return res;
}
int8_t getBattPercentImpl() {
thisModem().sendAT(GF("+CBC"));
if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; }
thisModem().streamSkipUntil(','); // Skip battery charge status
// Read battery charge level
int8_t res = thisModem().streamGetIntBefore(',');
// Wait for final OK
thisModem().waitResponse();
return res;
}
int8_t getBattChargeStateImpl() {
thisModem().sendAT(GF("+CBC"));
if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; }
// Read battery charge status
int8_t res = thisModem().streamGetIntBefore(',');
// Wait for final OK
thisModem().waitResponse();
return res;
}
bool getBattStatsImpl(int8_t& chargeState, int8_t& percent,
int16_t& milliVolts) {
thisModem().sendAT(GF("+CBC"));
if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; }
chargeState = thisModem().streamGetIntBefore(',');
percent = thisModem().streamGetIntBefore(',');
milliVolts = thisModem().streamGetIntBefore('\n');
// Wait for final OK
thisModem().waitResponse();
return true;
}
};
#endif // SRC_TINYGSMBATTERY_H_

View file

@ -0,0 +1,100 @@
/**
* @file TinyGsmGPS.tpp
* @author Adrian Cervera Andes
* @license LGPL-3.0
* @copyright Copyright (c) 2021 Adrian Cervera Andes
* @date Jan 2021
*/
#ifndef SRC_TINYGSMBLUETOOTH_H_
#define SRC_TINYGSMBLUETOOTH_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_BLUETOOTH
template <class modemType>
class TinyGsmBluetooth {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* Bluetooth functions
*/
/**
* @brief Enable module Bluetooth
*
* @return *true* Bluetooth was succcessfully enabled
* @return *false* Bluetooth failed to enable
*/
bool enableBluetooth() {
return thisModem().enableBluetoothImpl();
}
/**
* @brief Disable module Bluetooth
*
* @return *true* Bluetooth was succcessfully disabled
* @return *false* Bluetooth failed to disable
*/
bool disableBluetooth() {
return thisModem().disableBluetoothImpl();
}
/**
* @brief Set the Bluetooth visibility
*
* @param visible True to make the modem visible over Bluetooth, false to make
* it invisible.
* @return *true* Bluetooth visibility was successfully changed.
* @return *false* Bluetooth visibility failed to change
*/
bool setBluetoothVisibility(bool visible) {
return thisModem().setBluetoothVisibilityImpl(visible);
}
/**
* @brief Set the Bluetooth host name
*
* @param name The name visible to other Bluetooth objects
* @return *true* Bluetooth host name was successfully changed.
* @return *false* Bluetooth host name failed to change
*/
bool setBluetoothHostName(const char* name) {
return thisModem().setBluetoothHostNameImpl(name);
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmBluetooth() {}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* Bluetooth functions
*/
bool enableBluetoothImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool disableBluetoothImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool setBluetoothVisibilityImpl(bool visible) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool setBluetoothHostNameImpl(const char* name) TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
#endif // SRC_TINYGSMBLUETOOTH_H_

View file

@ -0,0 +1,133 @@
/**
* @file TinyGsmCalling.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCALLING_H_
#define SRC_TINYGSMCALLING_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_CALLING
template <class modemType>
class TinyGsmCalling {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* Phone Call functions
*/
/**
* @brief Answer an incoming phone call
*
* @return *true* The call was answered.
* @return *false* The call was not answered.
*/
bool callAnswer() {
return thisModem().callAnswerImpl();
}
/**
* @brief Make a phone call
*
* @param number The number to call
* @return *true* The call was successfully made
* @return *false* The call wasn't made
*/
bool callNumber(const String& number) {
return thisModem().callNumberImpl(number);
}
/**
* @brief Hang up an in-progress phone call
*
* @return *true* The call was hung up.
* @return *false* The call was not hung up.
*/
bool callHangup() {
return thisModem().callHangupImpl();
}
/**
* @brief Sent a DTMF (dual tone multi frequency) message over a phone call.
*
* @param cmd The command to send
* @param duration_ms The tone duration
* @return *true* The message was sent
* @return *false* The message failed to send
*/
bool dtmfSend(char cmd, int duration_ms = 100) {
return thisModem().dtmfSendImpl(cmd, duration_ms);
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmCalling() {}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* Phone Call functions
*/
protected:
bool callAnswerImpl() {
thisModem().sendAT(GF("A"));
return thisModem().waitResponse() == 1;
}
// Returns true on pick-up, false on error/busy
bool callNumberImpl(const String& number) {
if (number == GF("last")) {
thisModem().sendAT(GF("DL"));
} else {
thisModem().sendAT(GF("D"), number, ";");
}
int8_t status = thisModem().waitResponse(60000L, GF("OK"), GF("BUSY"),
GF("NO ANSWER"), GF("NO CARRIER"));
switch (status) {
case 1: return true;
case 2:
case 3: return false;
default: return false;
}
}
bool callHangupImpl() {
thisModem().sendAT(GF("H"));
return thisModem().waitResponse() == 1;
}
// 0-9,*,#,A,B,C,D
bool dtmfSendImpl(char cmd, int duration_ms = 100) {
duration_ms = constrain(duration_ms, 100, 1000);
thisModem().sendAT(GF("+VTD="),
duration_ms / 100); // VTD accepts in 1/10 of a second
thisModem().waitResponse();
thisModem().sendAT(GF("+VTS="), cmd);
return thisModem().waitResponse(10000L) == 1;
}
};
#endif // SRC_TINYGSMCALLING_H_

View file

@ -0,0 +1,135 @@
/**
* @file TinyGsmClient.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENT_H_
#define SRC_TINYGSMCLIENT_H_
#if defined(TINY_GSM_MODEM_SIM800)
#include "TinyGsmClientSIM800.h"
typedef TinyGsmSim800 TinyGsm;
typedef TinyGsmSim800::GsmClientSim800 TinyGsmClient;
typedef TinyGsmSim800::GsmClientSecureSim800 TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868)
#include "TinyGsmClientSIM808.h"
typedef TinyGsmSim808 TinyGsm;
typedef TinyGsmSim808::GsmClientSim800 TinyGsmClient;
typedef TinyGsmSim808::GsmClientSecureSim800 TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_SIM900)
#include "TinyGsmClientSIM800.h"
typedef TinyGsmSim800 TinyGsm;
typedef TinyGsmSim800::GsmClientSim800 TinyGsmClient;
#elif defined(TINY_GSM_MODEM_SIM7000)
#include "TinyGsmClientSIM7000.h"
typedef TinyGsmSim7000 TinyGsm;
typedef TinyGsmSim7000::GsmClientSim7000 TinyGsmClient;
#elif defined(TINY_GSM_MODEM_SIM7000SSL)
#include "TinyGsmClientSIM7000SSL.h"
typedef TinyGsmSim7000SSL TinyGsm;
typedef TinyGsmSim7000SSL::GsmClientSim7000SSL TinyGsmClient;
typedef TinyGsmSim7000SSL::GsmClientSecureSIM7000SSL TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_SIM7070) || defined(TINY_GSM_MODEM_SIM7080) || \
defined(TINY_GSM_MODEM_SIM7090)
#include "TinyGsmClientSIM7080.h"
typedef TinyGsmSim7080 TinyGsm;
typedef TinyGsmSim7080::GsmClientSim7080 TinyGsmClient;
typedef TinyGsmSim7080::GsmClientSecureSIM7080 TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_SIM5320) || defined(TINY_GSM_MODEM_SIM5360) || \
defined(TINY_GSM_MODEM_SIM5300) || defined(TINY_GSM_MODEM_SIM7100)
#include "TinyGsmClientSIM5360.h"
typedef TinyGsmSim5360 TinyGsm;
typedef TinyGsmSim5360::GsmClientSim5360 TinyGsmClient;
#elif defined(TINY_GSM_MODEM_SIM7600) || defined(TINY_GSM_MODEM_SIM7800) || \
defined(TINY_GSM_MODEM_SIM7500)
#include "TinyGsmClientSIM7600.h"
typedef TinyGsmSim7600 TinyGsm;
typedef TinyGsmSim7600::GsmClientSim7600 TinyGsmClient;
#elif defined(TINY_GSM_MODEM_UBLOX)
#include "TinyGsmClientUBLOX.h"
typedef TinyGsmUBLOX TinyGsm;
typedef TinyGsmUBLOX::GsmClientUBLOX TinyGsmClient;
typedef TinyGsmUBLOX::GsmClientSecureUBLOX TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_SARAR4)
#include "TinyGsmClientSaraR4.h"
typedef TinyGsmSaraR4 TinyGsm;
typedef TinyGsmSaraR4::GsmClientSaraR4 TinyGsmClient;
typedef TinyGsmSaraR4::GsmClientSecureR4 TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_SARAR5)
#include "TinyGsmClientSaraR5.h"
typedef TinyGsmSaraR5 TinyGsm;
typedef TinyGsmSaraR5::GsmClientSaraR5 TinyGsmClient;
typedef TinyGsmSaraR5::GsmClientSecureR5 TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_M95)
#include "TinyGsmClientM95.h"
typedef TinyGsmM95 TinyGsm;
typedef TinyGsmM95::GsmClientM95 TinyGsmClient;
#elif defined(TINY_GSM_MODEM_BG96) || defined(TINY_GSM_MODEM_BG95) || \
defined(TINY_GSM_MODEM_BG95SSL)
#include "TinyGsmClientBG96.h"
typedef TinyGsmBG96 TinyGsm;
typedef TinyGsmBG96::GsmClientBG96 TinyGsmClient;
typedef TinyGsmBG96::GsmClientSecureBG96 TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_A6) || defined(TINY_GSM_MODEM_A7)
#include "TinyGsmClientA6.h"
typedef TinyGsmA6 TinyGsm;
typedef TinyGsmA6::GsmClientA6 TinyGsmClient;
#elif defined(TINY_GSM_MODEM_M590)
#include "TinyGsmClientM590.h"
typedef TinyGsmM590 TinyGsm;
typedef TinyGsmM590::GsmClientM590 TinyGsmClient;
#elif defined(TINY_GSM_MODEM_MC60) || defined(TINY_GSM_MODEM_MC60E)
#include "TinyGsmClientMC60.h"
typedef TinyGsmMC60 TinyGsm;
typedef TinyGsmMC60::GsmClientMC60 TinyGsmClient;
#elif defined(TINY_GSM_MODEM_ESP8266) || defined(TINY_GSM_MODEM_ESP32)
#define TINY_GSM_MODEM_HAS_WIFI
#include "TinyGsmClientESP8266.h"
typedef TinyGsmESP8266 TinyGsm;
typedef TinyGsmESP8266::GsmClientESP8266 TinyGsmClient;
typedef TinyGsmESP8266::GsmClientSecureESP8266 TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_XBEE)
#define TINY_GSM_MODEM_HAS_WIFI
#include "TinyGsmClientXBee.h"
typedef TinyGsmXBee TinyGsm;
typedef TinyGsmXBee::GsmClientXBee TinyGsmClient;
typedef TinyGsmXBee::GsmClientSecureXBee TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_SEQUANS_MONARCH)
#include "TinyGsmClientSequansMonarch.h"
typedef TinyGsmSequansMonarch TinyGsm;
typedef TinyGsmSequansMonarch::GsmClientSequansMonarch TinyGsmClient;
typedef TinyGsmSequansMonarch::GsmClientSecureSequansMonarch
TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_A7672X)
#include "TinyGsmClientA7672x.h"
typedef TinyGsmA7672X TinyGsm;
typedef TinyGsmA7672X::GsmClientA7672X TinyGsmClient;
typedef TinyGsmA7672X::GsmClientSecureA7672X TinyGsmClientSecure;
#else
#error "Please define GSM modem model"
#endif
#endif // SRC_TINYGSMCLIENT_H_

View file

@ -0,0 +1,553 @@
/**
* @file TinyGsmClientA6.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTA6_H_
#define SRC_TINYGSMCLIENTA6_H_
// #pragma message("TinyGSM: TinyGsmClientA6")
// #define TINY_GSM_DEBUG Serial
#define TINY_GSM_MUX_COUNT 8
#define TINY_GSM_NO_MODEM_BUFFER
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "Ai-Thinker"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#if defined(TINY_GSM_MODEM_A7)
#define MODEM_MODEL "A7"
#else
#define MODEM_MODEL "A6"
#endif
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmCalling.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmBattery.tpp"
enum A6RegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmA6 : public TinyGsmModem<TinyGsmA6>,
public TinyGsmGPRS<TinyGsmA6>,
public TinyGsmTCP<TinyGsmA6, TINY_GSM_MUX_COUNT>,
public TinyGsmCalling<TinyGsmA6>,
public TinyGsmSMS<TinyGsmA6>,
public TinyGsmTime<TinyGsmA6>,
public TinyGsmBattery<TinyGsmA6> {
friend class TinyGsmModem<TinyGsmA6>;
friend class TinyGsmGPRS<TinyGsmA6>;
friend class TinyGsmTCP<TinyGsmA6, TINY_GSM_MUX_COUNT>;
friend class TinyGsmCalling<TinyGsmA6>;
friend class TinyGsmSMS<TinyGsmA6>;
friend class TinyGsmTime<TinyGsmA6>;
friend class TinyGsmBattery<TinyGsmA6>;
/*
* Inner Client
*/
public:
class GsmClientA6 : public GsmClient {
friend class TinyGsmA6;
public:
GsmClientA6() {}
explicit GsmClientA6(TinyGsmA6& modem, uint8_t = 0) {
init(&modem, -1);
}
bool init(TinyGsmA6* modem, uint8_t = 0) {
this->at = modem;
this->mux = -1;
sock_connected = false;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
uint8_t newMux = -1;
sock_connected = at->modemConnect(host, port, &newMux, timeout_s);
if (sock_connected) {
mux = newMux;
at->sockets[mux] = this;
}
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
TINY_GSM_YIELD();
at->sendAT(GF("+CIPCLOSE="), mux);
sock_connected = false;
at->waitResponse(maxWaitMs);
rx.clear();
}
void stop() override {
stop(1000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
// Doesn't support SSL
/*
* Constructor
*/
public:
explicit TinyGsmA6(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientA6"));
if (!testAT()) { return false; }
// sendAT(GF("&FZ")); // Factory + Reset
// waitResponse();
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) { return false; }
#ifdef TINY_GSM_DEBUG
sendAT(GF("+CMEE=2")); // turn on verbose error codes
#else
sendAT(GF("+CMEE=0")); // turn off error codes
#endif
waitResponse();
sendAT(
GF("+CMER=3,0,0,2")); // Set unsolicited result code output destination
waitResponse();
DBG(GF("### Modem:"), getModemName());
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
// Gets the modem serial number
String getModemSerialNumberImpl() {
sendAT(GF("GSN")); // Not CGSN
String res;
if (waitResponse(1000L, res) != 1) { return ""; }
cleanResponseString(res);
return res;
}
bool factoryDefaultImpl() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
waitResponse();
sendAT(GF("&W")); // Write configuration
return waitResponse() == 1;
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
sendAT(GF("+RST=1"));
delay(3000);
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+CPOF"));
// +CPOF: MS OFF OK
return waitResponse() == 1;
}
bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false)
TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Generic network functions
*/
public:
A6RegStatus getRegistrationStatus() {
return (A6RegStatus)getRegistrationStatusXREG("CREG");
}
protected:
bool isNetworkConnectedImpl() {
A6RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
String getLocalIPImpl() {
sendAT(GF("+CIFSR"));
String res;
if (waitResponse(10000L, res) != 1) { return ""; }
cleanResponseString(res);
return res;
}
/*
* Secure socket layer (SSL) functions
*/
protected:
// No functions of this type supported
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect();
sendAT(GF("+CGATT=1"));
if (waitResponse(60000L) != 1) { return false; }
// TODO(?): wait AT+CGATT?
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
waitResponse();
if (!user) user = "";
if (!pwd) pwd = "";
sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
if (waitResponse(60000L) != 1) { return false; }
sendAT(GF("+CGACT=1,1"));
waitResponse(60000L);
sendAT(GF("+CIPMUX=1"));
if (waitResponse() != 1) { return false; }
return true;
}
bool gprsDisconnectImpl() {
// Shut the TCP/IP connection
sendAT(GF("+CIPSHUT"));
if (waitResponse(60000L) != 1) { return false; }
for (int i = 0; i < 3; i++) {
sendAT(GF("+CGATT=0"));
if (waitResponse(5000L) == 1) { return true; }
}
return false;
}
String getOperatorImpl() {
sendAT(GF("+COPS=3,0")); // Set format
waitResponse();
sendAT(GF("+COPS?"));
if (waitResponse(GF(AT_NL "+COPS:")) != 1) { return ""; }
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
/*
* SIM card functions
*/
protected:
String getSimCCIDImpl() {
sendAT(GF("+CCID"));
if (waitResponse(GF(AT_NL "+SCID: SIM Card ID:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Phone Call functions
*/
protected:
// Returns true on pick-up, false on error/busy
bool callNumberImpl(const String& number) {
if (number == GF("last")) {
sendAT(GF("DLST"));
} else {
sendAT(GF("D\""), number, "\";");
}
if (waitResponse(5000L) != 1) { return false; }
if (waitResponse(60000L, GF(AT_NL "+CIEV: \"CALL\",1"),
GF(AT_NL "+CIEV: \"CALL\",0"), GFP(GSM_ERROR)) != 1) {
return false;
}
int8_t rsp = waitResponse(60000L, GF(AT_NL "+CIEV: \"SOUNDER\",0"),
GF(AT_NL "+CIEV: \"CALL\",0"));
int8_t rsp2 = waitResponse(300L, GF(AT_NL "BUSY" AT_NL),
GF(AT_NL "NO ANSWER" AT_NL));
return rsp == 1 && rsp2 == 0;
}
// 0-9,*,#,A,B,C,D
bool dtmfSendImpl(char cmd, uint8_t duration_ms = 100) {
duration_ms = constrain(duration_ms, 100, 1000);
// The duration parameter is not working, so we simulate it using delay..
// TODO(?): Maybe there's another way...
// sendAT(GF("+VTD="), duration_ms / 100);
// waitResponse();
sendAT(GF("+VTS="), cmd);
if (waitResponse(10000L) == 1) {
delay(duration_ms);
return true;
}
return false;
}
/*
* Audio functions
*/
public:
bool audioSetHeadphones() {
sendAT(GF("+SNFS=0"));
return waitResponse() == 1;
}
bool audioSetSpeaker() {
sendAT(GF("+SNFS=1"));
return waitResponse() == 1;
}
bool audioMuteMic(bool mute) {
sendAT(GF("+CMUT="), mute);
return waitResponse() == 1;
}
/*
* Text messaging (SMS) functions
*/
protected:
String sendUSSDImpl(const String& code) {
sendAT(GF("+CMGF=1"));
waitResponse();
sendAT(GF("+CSCS=\"HEX\""));
waitResponse();
sendAT(GF("+CUSD=1,\""), code, GF("\",15"));
if (waitResponse(10000L) != 1) { return ""; }
if (waitResponse(GF(AT_NL "+CUSD:")) != 1) { return ""; }
streamSkipUntil('"');
String hex = stream.readStringUntil('"');
streamSkipUntil(',');
int8_t dcs = streamGetIntBefore('\n');
if (dcs == 15) {
return TinyGsmDecodeHex7bit(hex);
} else if (dcs == 72) {
return TinyGsmDecodeHex16bit(hex);
} else {
return hex;
}
}
/*
* GSM Location functions
*/
// No functions of this type supported
/*
* GPS/GNSS/GLONASS location functions
*/
// No functions of this type supported
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
// Note - the clock probably has to be set manaually first
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
protected:
int16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
// Needs a '?' after CBC, unlike most
int8_t getBattPercentImpl() {
sendAT(GF("+CBC?"));
if (waitResponse(GF(AT_NL "+CBC:")) != 1) { return false; }
streamSkipUntil(','); // Skip battery charge status
// Read battery charge level
int8_t res = streamGetIntBefore('\n');
// Wait for final OK
waitResponse();
return res;
}
// Needs a '?' after CBC, unlike most
bool getBattStatsImpl(int8_t& chargeState, int8_t& percent,
int16_t& milliVolts) {
sendAT(GF("+CBC?"));
if (waitResponse(GF(AT_NL "+CBC:")) != 1) { return false; }
chargeState = streamGetIntBefore(',');
percent = streamGetIntBefore('\n');
milliVolts = 0;
// Wait for final OK
waitResponse();
return true;
}
/*
* Temperature functions
*/
// No functions of this type supported
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t* mux,
int timeout_s = 75) {
uint32_t startMillis = millis();
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
sendAT(GF("+CIPSTART="), GF("\"TCP"), GF("\",\""), host, GF("\","), port);
if (waitResponse(timeout_ms, GF(AT_NL "+CIPNUM:")) != 1) { return false; }
int8_t newMux = streamGetIntBefore('\n');
int8_t rsp = waitResponse((timeout_ms - (millis() - startMillis)),
GF("CONNECT OK" AT_NL), GF("CONNECT FAIL" AT_NL),
GF("ALREADY CONNECT" AT_NL));
if (waitResponse() != 1) { return false; }
*mux = newMux;
return (1 == rsp);
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
if (waitResponse(2000L, GF(AT_NL ">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(10000L, GFP(GSM_OK), GF(AT_NL "FAIL")) != 1) { return 0; }
return len;
}
bool modemGetConnected(uint8_t) {
sendAT(GF("+CIPSTATUS")); // TODO(?) mux?
int8_t res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""),
GF(",\"CLOSING\""), GF(",\"INITIAL\""));
waitResponse();
return 1 == res;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF("+CIPRCV:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore(',');
int16_t len_orig = len;
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
if (len > sockets[mux]->rx.free()) {
DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
// reset the len to read to the amount free
len = sockets[mux]->rx.free();
}
while (len--) { moveCharFromStreamToFifo(mux); }
// TODO(?) Deal with missing characters
if (len_orig != sockets[mux]->available()) {
DBG("### Different number of characters received than expected: ",
sockets[mux]->available(), " vs ", len_orig);
}
}
data = "";
DBG("### Got Data: ", len_orig, "on", mux);
return true;
} else if (data.endsWith(GF("+TCPCLOSED:"))) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientA6* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTA6_H_

View file

@ -0,0 +1,775 @@
/**
* @file TinyGsmClientA7672x.h
* @author Giovanni de Rosso Unruh
* @license LGPL-3.0
* @copyright Copyright (c) 2022 Giovanni de Rosso Unruh
* @date Oct 2022
*/
#ifndef SRC_TINYGSMCLIENTA7672X_H_
#define SRC_TINYGSMCLIENTA7672X_H_
// #define TINY_GSM_DEBUG Serial
#define TINY_GSM_MUX_COUNT 10
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "SIMCom"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#define MODEM_MODEL "A7672x"
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmSSL.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmCalling.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmGSMLocation.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmNTP.tpp"
#include "TinyGsmBattery.tpp"
#include "TinyGsmTemperature.tpp"
enum A7672xRegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmA7672X : public TinyGsmModem<TinyGsmA7672X>,
public TinyGsmGPRS<TinyGsmA7672X>,
public TinyGsmTCP<TinyGsmA7672X, TINY_GSM_MUX_COUNT>,
public TinyGsmSSL<TinyGsmA7672X, TINY_GSM_MUX_COUNT>,
public TinyGsmCalling<TinyGsmA7672X>,
public TinyGsmSMS<TinyGsmA7672X>,
public TinyGsmGSMLocation<TinyGsmA7672X>,
public TinyGsmTime<TinyGsmA7672X>,
public TinyGsmNTP<TinyGsmA7672X>,
public TinyGsmBattery<TinyGsmA7672X>,
public TinyGsmTemperature<TinyGsmA7672X> {
friend class TinyGsmModem<TinyGsmA7672X>;
friend class TinyGsmGPRS<TinyGsmA7672X>;
friend class TinyGsmTCP<TinyGsmA7672X, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSSL<TinyGsmA7672X, TINY_GSM_MUX_COUNT>;
friend class TinyGsmCalling<TinyGsmA7672X>;
friend class TinyGsmSMS<TinyGsmA7672X>;
friend class TinyGsmGSMLocation<TinyGsmA7672X>;
friend class TinyGsmTime<TinyGsmA7672X>;
friend class TinyGsmNTP<TinyGsmA7672X>;
friend class TinyGsmBattery<TinyGsmA7672X>;
friend class TinyGsmTemperature<TinyGsmA7672X>;
/*
* Inner Client
*/
public:
class GsmClientA7672X : public GsmClient {
friend class TinyGsmA7672X;
public:
GsmClientA7672X() {}
explicit GsmClientA7672X(TinyGsmA7672X& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmA7672X* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+CIPCLOSE="), mux);
sock_connected = false;
at->waitResponse();
}
void stop() override {
stop(15000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
public:
class GsmClientSecureA7672X : public GsmClientA7672X {
public:
GsmClientSecureA7672X() {}
explicit GsmClientSecureA7672X(TinyGsmA7672X& modem, uint8_t mux = 0)
: GsmClientA7672X(modem, mux) {}
public:
bool addCertificate(const char* certificateName, const char* cert,
const uint16_t len) {
return at->addCertificate(certificateName, cert, len);
}
bool setCertificate(const char* certificateName) {
return at->setCertificate(certificateName, mux);
}
bool deleteCertificate(const char* certificateName) {
return at->deleteCertificate(certificateName);
}
int connect(const char* host, uint16_t port, int timeout_s) override {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+CCHCLOSE="), mux); //, GF(",1")); // Quick close
sock_connected = false;
at->waitResponse();
}
void stop() override {
stop(15000L);
}
};
/*
* Constructor
*/
public:
explicit TinyGsmA7672X(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: A7672X"));
if (!testAT(2000)) { return false; }
// sendAT(GF("&FZ")); // Factory + Reset
// waitResponse();
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) { return false; }
#ifdef TINY_GSM_DEBUG
sendAT(GF("V1")); // turn on verbose error codes
#else
sendAT(GF("V0")); // turn off error codes
#endif
waitResponse();
DBG(GF("### Modem:"), getModemName());
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
bool factoryDefaultImpl() {
sendAT(GF("&F")); // Factory + Reset
waitResponse();
sendAT(GF("+IFC=0,0")); // No Flow Control
waitResponse();
sendAT(GF("+ICF=2,2")); // 8 data 0 parity 1 stop
waitResponse();
sendAT(GF("+CSCLK=0")); // Control UART Sleep always work
waitResponse();
sendAT(GF("&W")); // Write configuration
return waitResponse() == 1;
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
sendAT(GF("+CRESET"));
waitResponse();
if (!setPhoneFunctionality(0)) { return false; }
if (!setPhoneFunctionality(1, true)) { return false; }
delay(3000);
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+CPOF"));
return waitResponse(10000L) == 1;
}
// This command is used to enable UART Sleep or always work. If set to 0,
// UART always work. If set to 1, ensure that DTR is pulled high and the
// module can go to DTR sleep. If set to 2, the module will enter RXsleep. RX
// wakeup directly sends data through the serial port (for example: AT) to
// wake up
bool sleepEnableImpl(bool enable = true) {
sendAT(GF("+CSCLK="),
enable ? "2" : "1"); // 2: RXsleep (at wakeup) 1: DTR sleep
return waitResponse() == 1;
}
// <fun> 0 minimum functionality
// <fun> 1 full functionality, online mode
// <fun> 4 disable phone both transmit and receive RF circuits
// <fun> 5 Factory Test Mode (The A7600's 5 and 1 have the same function)
// <fun> 6 Reset
// <fun> 7 Offline Mode
// <rst> 0 do not reset the ME before setting it to <fun> power level
// <rst> 1 reset the ME before setting it to <fun> power level. This
// valueonlytakes effect when <fun> equals 1
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
sendAT(GF("+CFUN="), fun, reset ? ",1" : ",0");
return waitResponse(10000L) == 1;
}
/*
* Generic network functions
*/
public:
A7672xRegStatus getRegistrationStatus() {
return (A7672xRegStatus)getRegistrationStatusXREG("CREG");
}
String getLocalIPImpl() {
if (hasSSL) {
sendAT(GF("+CCHADDR"));
if (waitResponse(GF("+CCHADDR:")) != 1) { return ""; }
} else {
sendAT(GF("+CGPADDR=1"));
if (waitResponse(GF("+CGPADDR:")) != 1) { return ""; }
}
streamSkipUntil(','); // Skip context id
String res = stream.readStringUntil('\r');
if (waitResponse() != 1) { return ""; }
return res;
}
protected:
bool isNetworkConnectedImpl() {
A7672xRegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
/*
* Secure socket layer (SSL) functions
*/
public:
// The name of the certificate/key/password file. The file name must
// havetype like ".pem" or ".der".
// The certificate like - const char ca_cert[] PROGMEM = R"EOF(-----BEGIN...
// len of certificate like - sizeof(ca_cert)
bool addCertificate(const char* certificateName, const char* cert,
const uint16_t len) {
sendAT(GF("+CCERTDOWN="), certificateName, GF(","), len);
if (waitResponse(GF(">")) != 1) { return false; }
stream.write(cert, len);
stream.flush();
return waitResponse() == 1;
}
bool deleteCertificate(const char* certificateName) { // todo test
sendAT(GF("+CCERTDELE="), certificateName);
return waitResponse() == 1;
}
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect();
// Define the PDP context
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
waitResponse();
// Activate the PDP context
sendAT(GF("+CGACT=1,1"));
waitResponse(60000L);
// Attach to GPRS
sendAT(GF("+CGATT=1"));
if (waitResponse(60000L) != 1) { return false; }
// Set to get data manually
sendAT(GF("+CIPRXGET=1"));
if (waitResponse() != 1) { return false; }
// Get Local IP Address, only assigned after connection
sendAT(GF("+CGPADDR=1"));
if (waitResponse(10000L) != 1) { return false; }
// Configure Domain Name Server (DNS)
sendAT(GF("+CDNSCFG=\"8.8.8.8\",\"8.8.4.4\""));
if (waitResponse() != 1) { return false; }
return true;
}
bool gprsDisconnectImpl() {
// Shut the TCP/IP connection
sendAT(GF("+NETCLOSE"));
if (waitResponse(60000L) != 1) { return false; }
sendAT(GF("+CGATT=0")); // Detach from GPRS
if (waitResponse(60000L) != 1) { return false; }
return true;
}
String getProviderImpl() {
sendAT(GF("+CSPN?"));
if (waitResponse(GF("+CSPN:")) != 1) { return ""; }
streamSkipUntil('"'); /* Skip mode and format */
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
/*
* SIM card functions
*/
protected:
SimStatus getSimStatusImpl(uint32_t timeout_ms = 10000L) {
for (uint32_t start = millis(); millis() - start < timeout_ms;) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF("+CPIN:")) != 1) {
delay(1000);
continue;
}
int8_t status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"),
GF("SIM not inserted"), GF("SIM REMOVED"));
waitResponse();
switch (status) {
case 2:
case 3: return SIM_LOCKED;
case 1: return SIM_READY;
default: return SIM_ERROR;
}
}
return SIM_ERROR;
}
String getSimCCIDImpl() {
sendAT(GF("+CICCID"));
if (waitResponse(GF(AT_NL "+ICCID:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Phone Call functions
*/
public:
bool setGsmBusy(bool busy = true) {
sendAT(GF("+CCFC=1,"), busy ? 1 : 0);
return waitResponse() == 1;
}
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
/*
* GSM Location functions
*/
// No functions of this type supported
/*
* GPS/GNSS/GLONASS location functions
*/
// No functions of this type supported
/*
* Time functions
*/
// No functions of this type supported
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
// No functions of this type supported
/*
* Temperature functions
*/
protected:
float getTemperatureImpl() {
String res = "";
sendAT(GF("+CPMUTEMP"));
if (waitResponse(1000L, res)) { return 0; }
res = res.substring(res.indexOf(':'), res.indexOf('\r'));
float temp = res.toFloat();
waitResponse();
return temp;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75) {
int8_t rsp;
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
// +CTCPKA:<keepalive>,<keepidle>,<keepcount>,<keepinterval>
sendAT(GF("+CTCPKA=1,2,5,1"));
if (waitResponse(2000L) != 1) { return false; }
if (ssl) {
hasSSL = true;
// set the ssl version
// AT+CSSLCFG="sslversion",<ssl_ctx_index>,<sslversion>
// <ctxindex> PDP context identifier
// <sslversion> 0: QAPI_NET_SSL_PROTOCOL_UNKNOWN
// 1: QAPI_NET_SSL_PROTOCOL_TLS_1_0
// 2: QAPI_NET_SSL_PROTOCOL_TLS_1_1
// 3: QAPI_NET_SSL_PROTOCOL_TLS_1_2
// 4: QAPI_NET_SSL_PROTOCOL_DTLS_1_0
// 5: QAPI_NET_SSL_PROTOCOL_DTLS_1_2
// NOTE: despite docs using caps, "sslversion" must be in lower case
sendAT(GF("+CSSLCFG=\"sslversion\",0,3")); // TLS 1.2
if (waitResponse(5000L) != 1) return false;
if (certificates[mux] != "") {
/* Configure the server root CA of the specified SSL context
AT + CSSLCFG = "cacert", <ssl_ ctx_index>,<ca_file> */
sendAT(GF("+CSSLCFG=\"cacert\",0,"), certificates[mux].c_str());
if (waitResponse(5000L) != 1) return false;
}
// set the SSL SNI (server name indication)
// AT+CSSLCFG="enableSNI",<ssl_ctx_index>,<enableSNI_flag>
// NOTE: despite docs using caps, "sni" must be in lower case
sendAT(GF("+CSSLCFG=\"enableSNI\",0,1"));
if (waitResponse(2000L) != 1) { return false; }
// Configure the report mode of sending and receiving data
/* +CCHSET=<report_send_result>,<recv_mode>
* <report_send_result> Whether to report result of CCHSEND, the default
* value is 0: 0 No. 1 Yes. Module will report +CCHSEND:
* <session_id>,<err> to MCU when complete sending data.
*
* <recv_mode> The receiving mode, the default value is 0:
* 0 Output the data to MCU whenever received data.
* 1 Module caches the received data and notifies MCU with+CCHEVENT:
* <session_id>,RECV EVENT. MCU can use AT+CCHRECV to receive the cached
* data (only in manual receiving mode).
*/
sendAT(GF("+CCHSET=1,1"));
if (waitResponse(2000L) != 1) { return false; }
sendAT(GF("+CCHSTART"));
if (waitResponse(2000L) != 1) { return false; }
sendAT(GF("+CCHSSLCFG="), mux, GF(",0"));
if (waitResponse(2000L) != 1) { return false; }
sendAT(GF("+CCHOPEN="), 0, GF(",\""), host, GF("\","), port, GF(",2"));
rsp = waitResponse(timeout_ms, GF("+CCHOPEN: 0,0" AT_NL),
GF("+CCHOPEN: 0,1" AT_NL), GF("+CCHOPEN: 0,4" AT_NL),
GF("ERROR" AT_NL), GF("CLOSE OK" AT_NL));
} else {
sendAT(GF("+NETOPEN"));
if (waitResponse(2000L) != 1) { return false; }
sendAT(GF("+NETOPEN?"));
if (waitResponse(2000L) != 1) { return false; }
sendAT(GF("+CIPOPEN="), 0, ',', GF("\"TCP"), GF("\",\""), host, GF("\","),
port);
rsp = waitResponse(
timeout_ms, GF("+CIPOPEN: 0,0" AT_NL), GF("+CIPOPEN: 0,1" AT_NL),
GF("+CIPOPEN: 0,4" AT_NL), GF("ERROR" AT_NL),
GF("CLOSE OK" AT_NL)); // Happens when HTTPS handshake fails
}
return (1 == rsp);
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
if (hasSSL)
sendAT(GF("+CCHSEND="), mux, ',', (uint16_t)len);
else
sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse() != 1) { return 0; }
if (waitResponse(10000L, GF("+CCHSEND: 0,0" AT_NL),
GF("+CCHSEND: 0,4" AT_NL), GF("+CCHSEND: 0,9" AT_NL),
GF("ERROR" AT_NL), GF("CLOSE OK" AT_NL)) != 1) {
return 0;
}
return len;
}
size_t modemRead(size_t size, uint8_t mux) {
int16_t len_requested = 0;
int16_t len_confirmed = 0;
if (!sockets[mux]) return 0;
if (hasSSL) {
sendAT(GF("+CCHRECV?")); // TODO(Rosso): Optimize this!
String res = "";
waitResponse(2000L, res);
int16_t len =
res.substring(res.indexOf(',') + 1, res.lastIndexOf(',')).toInt();
sendAT(GF("+CCHRECV="), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CCHRECV:")) != 1) { return 0; }
//+CCHRECV: DATA,0,<msg>
streamSkipUntil(','); // Skip DATA
streamSkipUntil(','); // Skip mux
len_requested = streamGetIntBefore('\n');
len_confirmed = len; // streamGetIntBefore(',');
} else {
sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX
streamSkipUntil(','); // Skip mux
len_requested = streamGetIntBefore(',');
// ^^ Requested number of data bytes (1-1460 bytes)to be read
len_confirmed = streamGetIntBefore('\n');
// ^^ Confirmed number of data bytes to be read, which may be less than
// requested. 0 indicates that no data can be read.
// SRGD NOTE: Contrary to above (which is copied from AT command manual)
// this is actually be the number of bytes that will be remaining in the
// buffer after the read.
}
for (int i = 0; i < len_requested; i++) {
uint32_t startMillis = millis();
while (!stream.available() &&
(millis() - startMillis < sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char c = stream.read();
sockets[mux]->rx.put(c);
}
// DBG("### READ:", len_requested, " bytes from connection ", mux);
// sockets[mux]->sock_available = modemGetAvailable(mux);
sockets[mux]->sock_available = len_confirmed;
waitResponse();
return len_requested;
}
size_t modemGetAvailable(uint8_t mux) {
if (!sockets[mux]) return 0;
size_t result = 0;
if (hasSSL) {
sendAT(GF("+CCHRECV?")); // TODO(Rosso): Optimize this!
String res = "";
waitResponse(2000L, res);
result =
res.substring(res.indexOf(',') + 1, res.lastIndexOf(',')).toInt();
} else {
sendAT(GF("+CIPRXGET=4,"), mux);
result = 0;
if (waitResponse(GF("+CIPRXGET:")) == 1) {
streamSkipUntil(','); // Skip mode 4
streamSkipUntil(','); // Skip mux
result = streamGetIntBefore('\n');
waitResponse();
}
}
// DBG("### Available:", result, "on", mux);
if (result == 0) { sockets[mux]->sock_connected = modemGetConnected(mux); }
return result;
}
bool modemGetConnected(uint8_t mux) {
int8_t res = 0;
if (hasSSL) {
bool connected = this->sockets[mux]->sock_connected;
// DBG("### Connected:", connected);
return connected;
} else {
sendAT(GF("+CIPACK="), mux);
waitResponse(GF("+CIPACK:"));
res = waitResponse(2000L); //(GF(",\"CONNECTED\""), GF(",\"CLOSED\""),
// GF(",\"CLOSING\""), GF(",\"REMOTE
// CLOSING\""), GF(",\"INITIAL\""));
waitResponse();
}
return 1 == res;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF(AT_NL "+CIPRXGET:"))) {
int8_t mode = streamGetIntBefore(',');
if (mode == 1) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
}
data = "";
DBG("### Got Data:", mux);
return true;
} else {
data += mode;
return false;
}
} else if (data.endsWith(GF("RECV EVENT" AT_NL))) {
sendAT(GF("+CCHRECV?"));
String res = "";
waitResponse(2000L, res);
int8_t mux = res.substring(res.lastIndexOf(',') + 1).toInt();
int16_t len =
res.substring(res.indexOf(',') + 1, res.lastIndexOf(',')).toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
}
data = "";
DBG("### Got Data:", len, "on", mux);
return true;
} else if (data.endsWith(GF("+CCHRECV: 0,0" AT_NL))) {
int8_t mux = data.substring(data.lastIndexOf(',') + 1).toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = true;
}
data = "";
DBG("### ACK:", mux);
return true;
} else if (data.endsWith(GF("+IPCLOSE:"))) {
int8_t mux = streamGetIntBefore(',');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
streamSkipUntil('\n');
DBG("### TCP Closed: ", mux);
return true;
} else if (data.endsWith(GF("+CCHCLOSE:"))) {
int8_t mux = streamGetIntBefore(',');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
streamSkipUntil('\n');
DBG("### SSL Closed: ", mux);
return true;
} else if (data.endsWith(GF("+CCH_PEER_CLOSED:"))) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### SSL Closed: ", mux);
return true;
} else if (data.endsWith(GF("*PSNWID:"))) {
streamSkipUntil('\n'); // Refresh network name by network
data = "";
DBG("### Network name updated.");
return true;
} else if (data.endsWith(GF("*PSUTTZ:"))) {
streamSkipUntil('\n'); // Refresh time and time zone by network
data = "";
DBG("### Network time and time zone updated.");
return true;
} else if (data.endsWith(GF("+CTZV:"))) {
streamSkipUntil('\n'); // Refresh network time zone by network
data = "";
DBG("### Network time zone updated.");
return true;
} else if (data.endsWith(GF("DST:"))) {
streamSkipUntil('\n'); // Refresh Network Daylight Saving Time by network
data = "";
DBG("### Daylight savings time state updated.");
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientA7672X* sockets[TINY_GSM_MUX_COUNT];
bool hasSSL = false;
String certificates[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTA7672X_H_

View file

@ -0,0 +1,849 @@
/**
* @file TinyGsmClientBG96.h
* @author Volodymyr Shymanskyy and Aurelien BOUIN (SSL)
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Apr 2018, Aug 2023 (SSL)
*/
#ifndef SRC_TINYGSMCLIENTBG96_H_
#define SRC_TINYGSMCLIENTBG96_H_
// #pragma message("TinyGSM: TinyGsmClientBG96")
// #define TINY_GSM_DEBUG Serial
#define TINY_GSM_MUX_COUNT 12
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "Quectel"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#if defined(TINY_GSM_MODEM_BG95) || defined(TINY_GSM_MODEM_BG95SSL)
#define MODEM_MODEL "BG95"
#else
#define MODEM_MODEL "BG96"
#endif
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmSSL.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmCalling.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmGPS.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmNTP.tpp"
#include "TinyGsmBattery.tpp"
#include "TinyGsmTemperature.tpp"
enum BG96RegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmBG96 : public TinyGsmModem<TinyGsmBG96>,
public TinyGsmGPRS<TinyGsmBG96>,
public TinyGsmTCP<TinyGsmBG96, TINY_GSM_MUX_COUNT>,
public TinyGsmSSL<TinyGsmBG96, TINY_GSM_MUX_COUNT>,
public TinyGsmCalling<TinyGsmBG96>,
public TinyGsmSMS<TinyGsmBG96>,
public TinyGsmGPS<TinyGsmBG96>,
public TinyGsmTime<TinyGsmBG96>,
public TinyGsmNTP<TinyGsmBG96>,
public TinyGsmBattery<TinyGsmBG96>,
public TinyGsmTemperature<TinyGsmBG96> {
friend class TinyGsmModem<TinyGsmBG96>;
friend class TinyGsmGPRS<TinyGsmBG96>;
friend class TinyGsmTCP<TinyGsmBG96, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSSL<TinyGsmBG96, TINY_GSM_MUX_COUNT>;
friend class TinyGsmCalling<TinyGsmBG96>;
friend class TinyGsmSMS<TinyGsmBG96>;
friend class TinyGsmGPS<TinyGsmBG96>;
friend class TinyGsmTime<TinyGsmBG96>;
friend class TinyGsmNTP<TinyGsmBG96>;
friend class TinyGsmBattery<TinyGsmBG96>;
friend class TinyGsmTemperature<TinyGsmBG96>;
/*
* Inner Client
*/
public:
class GsmClientBG96 : public GsmClient {
friend class TinyGsmBG96;
public:
GsmClientBG96() {}
explicit GsmClientBG96(TinyGsmBG96& modem, uint8_t mux = 0) {
ssl_sock = false;
init(&modem, mux);
}
bool init(TinyGsmBG96* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
virtual void stop(uint32_t maxWaitMs) {
uint32_t startMillis = millis();
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+QICLOSE="), mux);
sock_connected = false;
at->waitResponse((maxWaitMs - (millis() - startMillis)));
}
void stop() override {
stop(15000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
protected:
bool ssl_sock;
};
/*
* Inner Secure Client
*/
public:
class GsmClientSecureBG96 : public GsmClientBG96 {
public:
GsmClientSecureBG96() {}
explicit GsmClientSecureBG96(TinyGsmBG96& modem, uint8_t mux = 0)
: GsmClientBG96(modem, mux) {
ssl_sock = true;
}
bool setCertificate(const String& certificateName) {
return at->setCertificate(certificateName, mux);
}
void stop(uint32_t maxWaitMs) override {
uint32_t startMillis = millis();
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+QSSLCLOSE="), mux);
sock_connected = false;
at->waitResponse((maxWaitMs - (millis() - startMillis)));
}
void stop() override {
stop(15000L);
}
};
/*
* Constructor
*/
public:
explicit TinyGsmBG96(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientBG96"));
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();
DBG(GF("### Modem:"), getModemName());
// Disable time and time zone URC's
sendAT(GF("+CTZR=0"));
if (waitResponse(10000L) != 1) { return false; }
// Enable automatic time zone update
sendAT(GF("+CTZU=1"));
if (waitResponse(10000L) != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
if (!setPhoneFunctionality(1, true)) { return false; }
waitResponse(10000L, GF("APP RDY"));
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+QPOWD=1"));
waitResponse(300); // returns OK first
return waitResponse(300, GF("POWERED DOWN")) == 1;
}
// When entering into sleep mode is enabled, DTR is pulled up, and WAKEUP_IN
// is pulled up, the module can directly enter into sleep mode.If entering
// into sleep mode is enabled, DTR is pulled down, and WAKEUP_IN is pulled
// down, there is a need to pull the DTR pin and the WAKEUP_IN pin up first,
// and then the module can enter into sleep mode.
bool sleepEnableImpl(bool enable = true) {
sendAT(GF("+QSCLK="), enable);
return waitResponse() == 1;
}
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false,
uint32_t timeout_ms = 15500L) {
sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
return waitResponse(timeout_ms, GF("OK")) == 1;
}
/*
* Generic network functions
*/
public:
BG96RegStatus getRegistrationStatus() {
// Check first for EPS registration
BG96RegStatus epsStatus = (BG96RegStatus)getRegistrationStatusXREG("CEREG");
// If we're connected on EPS, great!
if (epsStatus == REG_OK_HOME || epsStatus == REG_OK_ROAMING) {
return epsStatus;
} else {
// Otherwise, check generic network status
return (BG96RegStatus)getRegistrationStatusXREG("CREG");
}
}
protected:
bool isNetworkConnectedImpl() {
BG96RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
/*
* Secure socket layer (SSL) functions
*/
// Follows functions as inherited from TinyGsmSSL.tpp
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect();
// Configure the TCPIP Context
sendAT(GF("+QICSGP=1,1,\""), apn, GF("\",\""), user, GF("\",\""), pwd,
GF("\""));
if (waitResponse() != 1) { return false; }
// Activate GPRS/CSD Context
sendAT(GF("+QIACT=1"));
if (waitResponse(150000L) != 1) { return false; }
// Attach to Packet Domain service - is this necessary?
sendAT(GF("+CGATT=1"));
if (waitResponse(60000L) != 1) { return false; }
return true;
}
bool gprsDisconnectImpl() {
sendAT(GF("+QIDEACT=1")); // Deactivate the bearer context
if (waitResponse(40000L) != 1) { return false; }
return true;
}
String getProviderImpl() {
sendAT(GF("+QSPN?"));
if (waitResponse(GF("+QSPN:")) != 1) { return ""; }
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"'); // read the provider
waitResponse(); // skip anything else
return res;
}
/*
* SIM card functions
*/
protected:
String getSimCCIDImpl() {
sendAT(GF("+QCCID"));
if (waitResponse(GF(AT_NL "+QCCID:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Phone Call functions
*/
// Follows all phone call functions as inherited from TinyGsmCalling.tpp
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
/*
* GSM Location functions
*/
protected:
// NOTE: As of application firmware version 01.016.01.016 triangulated
// locations can be obtained via the QuecLocator service and accompanying AT
// commands. As this is a separate paid service which I do not have access
// to, I am not implementing it here.
/*
* GPS/GNSS/GLONASS location functions
*/
protected:
// enable GPS
bool enableGPSImpl() {
sendAT(GF("+QGPS=1"));
if (waitResponse() != 1) { return false; }
return true;
}
bool disableGPSImpl() {
sendAT(GF("+QGPSEND"));
if (waitResponse() != 1) { return false; }
return true;
}
// get the RAW GPS output
String getGPSrawImpl() {
sendAT(GF("+QGPSLOC=2"));
if (waitResponse(10000L, GF(AT_NL "+QGPSLOC: ")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
// get GPS informations
bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0,
int* vsat = 0, int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
int* minute = 0, int* second = 0) {
sendAT(GF("+QGPSLOC=2"));
if (waitResponse(10000L, GF(AT_NL "+QGPSLOC: ")) != 1) {
// NOTE: Will return an error if the position isn't fixed
return false;
}
// init variables
float ilat = 0;
float ilon = 0;
float ispeed = 0;
float ialt = 0;
int iusat = 0;
float iaccuracy = 0;
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
float secondWithSS = 0;
// UTC date & Time
ihour = streamGetIntLength(2); // Two digit hour
imin = streamGetIntLength(2); // Two digit minute
secondWithSS = streamGetFloatBefore(','); // 6 digit second with subseconds
ilat = streamGetFloatBefore(','); // Latitude
ilon = streamGetFloatBefore(','); // Longitude
iaccuracy = streamGetFloatBefore(','); // Horizontal precision
ialt = streamGetFloatBefore(','); // Altitude from sea level
streamSkipUntil(','); // GNSS positioning mode
streamSkipUntil(','); // Course Over Ground based on true north
streamSkipUntil(','); // Speed Over Ground in Km/h
ispeed = streamGetFloatBefore(','); // Speed Over Ground in knots
iday = streamGetIntLength(2); // Two digit day
imonth = streamGetIntLength(2); // Two digit month
iyear = streamGetIntBefore(','); // Two digit year
iusat = streamGetIntBefore(','); // Number of satellites,
streamSkipUntil('\n'); // The error code of the operation. If it is not
// 0, it is the type of error.
// Set pointers
if (lat != nullptr) *lat = ilat;
if (lon != nullptr) *lon = ilon;
if (speed != nullptr) *speed = ispeed;
if (alt != nullptr) *alt = ialt;
if (vsat != nullptr) *vsat = 0;
if (usat != nullptr) *usat = iusat;
if (accuracy != nullptr) *accuracy = iaccuracy;
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = static_cast<int>(secondWithSS);
waitResponse(); // Final OK
return true;
}
/*
* Time functions
*/
protected:
String getGSMDateTimeImpl(TinyGSMDateTimeFormat format) {
sendAT(GF("+QLTS=2"));
if (waitResponse(2000L, GF("+QLTS: \"")) != 1) { return ""; }
String res;
switch (format) {
case DATE_FULL: res = stream.readStringUntil('"'); break;
case DATE_TIME:
streamSkipUntil(',');
res = stream.readStringUntil('"');
break;
case DATE_DATE: res = stream.readStringUntil(','); break;
}
waitResponse(); // Ends with OK
return res;
}
// The BG96 returns UTC time instead of local time as other modules do in
// response to CCLK, so we're using QLTS where we can specifically request
// local time.
bool getNetworkUTCTimeImpl(int* year, int* month, int* day, int* hour,
int* minute, int* second, float* timezone) {
sendAT(GF("+QLTS=1"));
if (waitResponse(2000L, GF("+QLTS: \"")) != 1) { return false; }
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
int isec = 0;
int itimezone = 0;
// Date & Time
iyear = streamGetIntBefore('/');
imonth = streamGetIntBefore('/');
iday = streamGetIntBefore(',');
ihour = streamGetIntBefore(':');
imin = streamGetIntBefore(':');
isec = streamGetIntLength(2);
char tzSign = stream.read();
itimezone = streamGetIntBefore(',');
if (tzSign == '-') { itimezone = itimezone * -1; }
streamSkipUntil('\n'); // DST flag
// Set pointers
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = isec;
if (timezone != nullptr) *timezone = static_cast<float>(itimezone) / 4.0;
// Final OK
waitResponse(); // Ends with OK
return true;
}
// The BG96 returns UTC time instead of local time as other modules do in
// response to CCLK, so we're using QLTS where we can specifically request
// local time.
bool getNetworkTimeImpl(int* year, int* month, int* day, int* hour,
int* minute, int* second, float* timezone) {
sendAT(GF("+QLTS=2"));
if (waitResponse(2000L, GF("+QLTS: \"")) != 1) { return false; }
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
int isec = 0;
int itimezone = 0;
// Date & Time
iyear = streamGetIntBefore('/');
imonth = streamGetIntBefore('/');
iday = streamGetIntBefore(',');
ihour = streamGetIntBefore(':');
imin = streamGetIntBefore(':');
isec = streamGetIntLength(2);
char tzSign = stream.read();
itimezone = streamGetIntBefore(',');
if (tzSign == '-') { itimezone = itimezone * -1; }
streamSkipUntil('\n'); // DST flag
// Set pointers
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = isec;
if (timezone != nullptr) *timezone = static_cast<float>(itimezone) / 4.0;
// Final OK
waitResponse(); // Ends with OK
return true;
}
/*
* NTP server functions
*/
byte NTPServerSyncImpl(String server = "pool.ntp.org", byte = -5) {
// Request network synchronization
// AT+QNTP=<contextID>,<server>[,<port>][,<autosettime>]
sendAT(GF("+QNTP=1,\""), server, '"');
if (waitResponse(10000L, GF("+QNTP:"))) {
String result = stream.readStringUntil(',');
streamSkipUntil('\n');
result.trim();
if (TinyGsmIsValidNumber(result)) { return result.toInt(); }
} else {
return -1;
}
return -1;
}
String ShowNTPErrorImpl(byte error) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
// Follows all battery functions as inherited from TinyGsmBattery.tpp
/*
* Temperature functions
*/
protected:
// get temperature in degree celsius
uint16_t getTemperatureImpl() {
sendAT(GF("+QTEMP"));
if (waitResponse(GF(AT_NL "+QTEMP:")) != 1) { return 0; }
// return temperature in C
uint16_t res =
streamGetIntBefore(','); // read PMIC (primary ic) temperature
streamSkipUntil(','); // skip XO temperature ??
streamSkipUntil('\n'); // skip PA temperature ??
// Wait for final OK
waitResponse();
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
int timeout_s = 150) {
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
bool ssl = sockets[mux]->ssl_sock;
if (ssl) {
// set the ssl version
// AT+QSSLCFG="sslversion",<ctxindex>,<sslversion>
// <ctxindex> PDP context identifier
// <sslversion> 0: QAPI_NET_SSL_3.0
// 1: QAPI_NET_SSL_PROTOCOL_TLS_1_0
// 2: QAPI_NET_SSL_PROTOCOL_TLS_1_1
// 3: QAPI_NET_SSL_PROTOCOL_TLS_1_2
// 4: ALL
// NOTE: despite docs using caps, "sslversion" must be in lower case
sendAT(GF("+QSSLCFG=\"sslversion\",0,3")); // TLS 1.2
if (waitResponse(5000L) != 1) return false;
// set the ssl cipher_suite
// AT+QSSLCFG="ciphersuite",<ctxindex>,<cipher_suite>
// <ctxindex> PDP context identifier
// <cipher_suite> 0: TODO
// 1: TODO
// 0X0035: TLS_RSA_WITH_AES_256_CBC_SHA
// 0XFFFF: ALL
// NOTE: despite docs using caps, "sslversion" must be in lower case
sendAT(GF(
"+QSSLCFG=\"ciphersuite\",0,0X0035")); // TLS_RSA_WITH_AES_256_CBC_SHA
if (waitResponse(5000L) != 1) return false;
// set the ssl sec level
// AT+QSSLCFG="seclevel",<ctxindex>,<sec_level>
// <ctxindex> PDP context identifier
// <sec_level> 0: TODO
// 1: TODO
// 0X0035: TLS_RSA_WITH_AES_256_CBC_SHA
// 0XFFFF: ALL
// NOTE: despite docs using caps, "sslversion" must be in lower case
sendAT(GF("+QSSLCFG=\"seclevel\",0,1"));
if (waitResponse(5000L) != 1) return false;
if (certificates[mux] != "") {
// apply the correct certificate to the connection
// AT+QSSLCFG="cacert",<ctxindex>,<caname>
// <ctxindex> PDP context identifier
// <certname> certificate name
// sendAT(GF("+CASSLCFG="), mux, ",CACERT,\"",
// certificates[mux].c_str(),
// "\"");
sendAT(GF("+QSSLCFG=\"cacert\",0,\""), certificates[mux].c_str(),
GF("\""));
if (waitResponse(5000L) != 1) return false;
}
// <PDPcontextID>(1-16), <connectID>(0-11),
// "TCP/UDP/TCP LISTENER/UDPSERVICE", "<IP_address>/<domain_name>",
// <remote_port>,<local_port>,<access_mode>(0-2; 0=buffer)
// may need previous AT+QSSLCFG
sendAT(GF("+QSSLOPEN=1,1,"), mux, GF(",\""), host, GF("\","), port,
GF(",0"));
waitResponse();
if (waitResponse(timeout_ms, GF(AT_NL "+QSSLOPEN:")) != 1) {
return false;
}
// 20230629 -> +QSSLOPEN: <clientID>,<err>
// clientID is mux
// err must be 0
if (streamGetIntBefore(',') != mux) { return false; }
// Read status
return (0 == streamGetIntBefore('\n'));
} else {
// AT+QIOPEN=1,0,"TCP","220.180.239.212",8009,0,0
// <PDPcontextID>(1-16), <connectID>(0-11),
// "TCP/UDP/TCP LISTENER/UDPSERVICE", "<IP_address>/<domain_name>",
// <remote_port>,<local_port>,<access_mode>(0-2; 0=buffer)
sendAT(GF("+QIOPEN=1,"), mux, GF(",\""), GF("TCP"), GF("\",\""), host,
GF("\","), port, GF(",0,0"));
waitResponse();
if (waitResponse(timeout_ms, GF(AT_NL "+QIOPEN:")) != 1) { return false; }
if (streamGetIntBefore(',') != mux) { return false; }
}
// Read status
return (0 == streamGetIntBefore('\n'));
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
bool ssl = sockets[mux]->ssl_sock;
if (ssl) {
sendAT(GF("+QSSLSEND="), mux, ',', (uint16_t)len);
} else {
sendAT(GF("+QISEND="), mux, ',', (uint16_t)len);
}
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(GF(AT_NL "SEND OK")) != 1) { return 0; }
// TODO(?): Wait for ACK? (AT+QISEND=id,0 or AT+QSSLSEND=id,0)
return len;
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) return 0;
bool ssl = sockets[mux]->ssl_sock;
if (ssl) {
sendAT(GF("+QSSLRECV="), mux, ',', (uint16_t)size);
if (waitResponse(GF("+QSSLRECV:")) != 1) {
DBG("### READ: For unknown reason close");
return 0;
}
} else {
sendAT(GF("+QIRD="), mux, ',', (uint16_t)size);
if (waitResponse(GF("+QIRD:")) != 1) { return 0; }
}
int16_t len = streamGetIntBefore('\n');
for (int i = 0; i < len; i++) { moveCharFromStreamToFifo(mux); }
waitResponse();
// DBG("### READ:", len, "from", mux);
sockets[mux]->sock_available = modemGetAvailable(mux);
return len;
}
size_t modemGetAvailable(uint8_t mux) {
if (!sockets[mux]) return 0;
bool ssl = sockets[mux]->ssl_sock;
size_t result = 0;
if (ssl) {
sendAT(GF("+QSSLRECV="), mux, GF(",0"));
if (waitResponse(GF("+QSSLRECV:")) == 1) {
streamSkipUntil(','); // Skip total received
streamSkipUntil(','); // Skip have read
result = streamGetIntBefore('\n');
if (result) { DBG("### DATA AVAILABLE:", result, "on", mux); }
waitResponse();
}
} else {
sendAT(GF("+QIRD="), mux, GF(",0"));
if (waitResponse(GF("+QIRD:")) == 1) {
streamSkipUntil(','); // Skip total received
streamSkipUntil(','); // Skip have read
result = streamGetIntBefore('\n');
if (result) { DBG("### DATA AVAILABLE:", result, "on", mux); }
waitResponse();
}
}
if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
return result;
}
bool modemGetConnected(uint8_t mux) {
bool ssl = sockets[mux]->ssl_sock;
if (ssl) {
sendAT(GF("+QSSLSTATE=1,"), mux);
// +QSSLSTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1"
if (waitResponse(GF("+QSSLSTATE:")) != 1) { return false; }
streamSkipUntil(','); // Skip clientID
streamSkipUntil(','); // Skip "SSLClient"
streamSkipUntil(','); // Skip remote ip
streamSkipUntil(','); // Skip remote port
streamSkipUntil(','); // Skip local port
int8_t res = streamGetIntBefore(','); // socket state
waitResponse();
// 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
return 2 == res;
} else {
sendAT(GF("+QISTATE=1,"), mux);
// +QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1"
if (waitResponse(GF("+QISTATE:")) != 1) { return false; }
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip socket type
streamSkipUntil(','); // Skip remote ip
streamSkipUntil(','); // Skip remote port
streamSkipUntil(','); // Skip local port
int8_t res = streamGetIntBefore(','); // socket state
waitResponse();
// 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
return 2 == res;
}
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF(AT_NL "+QIURC:"))) {
streamSkipUntil('\"');
String urc = stream.readStringUntil('\"');
streamSkipUntil(',');
if (urc == "recv") {
int8_t mux = streamGetIntBefore('\n');
DBG("### URC RECV:", mux);
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
}
} else if (urc == "closed") {
int8_t mux = streamGetIntBefore('\n');
DBG("### URC CLOSE:", mux);
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
} else {
streamSkipUntil('\n');
}
data = "";
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientBG96* sockets[TINY_GSM_MUX_COUNT];
String certificates[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTBG96_H_

View file

@ -0,0 +1,546 @@
/**
* @file TinyGsmClientESP8266.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTESP8266_H_
#define SRC_TINYGSMCLIENTESP8266_H_
// #pragma message("TinyGSM: TinyGsmClientESP8266")
// #define TINY_GSM_DEBUG Serial
#define TINY_GSM_MUX_COUNT 5
#define TINY_GSM_NO_MODEM_BUFFER
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "Espressif"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#if defined(TINY_GSM_MODEM_ESP8266)
#define MODEM_MODEL "ESP8266"
#else
#define MODEM_MODEL "ESP32"
#endif
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmSSL.tpp"
#include "TinyGsmWifi.tpp"
static uint8_t TINY_GSM_TCP_KEEP_ALIVE = 120;
// <stat> status of ESP8266 station interface
// 0: ESP8266 station is not initialized.
// 1: ESP8266 station is initialized, but not started a Wi-Fi connection yet.
// 2 : ESP8266 station connected to an AP and has obtained IP
// 3 : ESP8266 station created a TCP or UDP transmission
// 4 : the TCP or UDP transmission of ESP8266 station disconnected
// 5 : ESP8266 station did NOT connect to an AP
enum ESP8266RegStatus {
REG_UNINITIALIZED = 0,
REG_UNREGISTERED = 1,
REG_OK_IP = 2,
REG_OK_TCP = 3,
REG_OK_NO_TCP = 4,
REG_DENIED = 5,
REG_UNKNOWN = 6,
};
class TinyGsmESP8266 : public TinyGsmModem<TinyGsmESP8266>,
public TinyGsmTCP<TinyGsmESP8266, TINY_GSM_MUX_COUNT>,
public TinyGsmSSL<TinyGsmESP8266, TINY_GSM_MUX_COUNT>,
public TinyGsmWifi<TinyGsmESP8266> {
friend class TinyGsmModem<TinyGsmESP8266>;
friend class TinyGsmTCP<TinyGsmESP8266, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSSL<TinyGsmESP8266, TINY_GSM_MUX_COUNT>;
friend class TinyGsmWifi<TinyGsmESP8266>;
/*
* Inner Client
*/
public:
class GsmClientESP8266 : public GsmClient {
friend class TinyGsmESP8266;
public:
GsmClientESP8266() {}
explicit GsmClientESP8266(TinyGsmESP8266& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmESP8266* modem, uint8_t mux = 0) {
this->at = modem;
sock_connected = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
TINY_GSM_YIELD();
at->sendAT(GF("+CIPCLOSE="), mux);
sock_connected = false;
at->waitResponse(maxWaitMs);
rx.clear();
}
void stop() override {
stop(5000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
public:
class GsmClientSecureESP8266 : public GsmClientESP8266 {
public:
GsmClientSecureESP8266() {}
explicit GsmClientSecureESP8266(TinyGsmESP8266& modem, uint8_t mux = 0)
: GsmClientESP8266(modem, mux) {}
public:
int connect(const char* host, uint16_t port, int timeout_s) override {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
};
/*
* Constructor
*/
public:
explicit TinyGsmESP8266(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientESP8266"));
if (!testAT()) { return false; }
if (pin && strlen(pin) > 0) {
DBG("ESP8266 modules do not use an unlock pin!");
}
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) { return false; }
sendAT(GF("+CIPMUX=1")); // Enable Multiple Connections
if (waitResponse() != 1) { return false; }
sendAT(GF("+CWMODE=1")); // Put into "station" mode
if (waitResponse() != 1) {
sendAT(GF("+CWMODE_CUR=1")); // Attempt "current" station mode command
// for some firmware variants if needed
if (waitResponse() != 1) { return false; }
}
DBG(GF("### Modem:"), getModemName());
return true;
}
bool setBaudImpl(uint32_t baud) {
sendAT(GF("+UART_CUR="), baud, "8,1,0,0");
if (waitResponse() != 1) {
sendAT(GF("+UART="), baud,
"8,1,0,0"); // Really old firmwares might need this
// if (waitResponse() != 1) {
// sendAT(GF("+IPR="), baud); // First release firmwares might need
// this
return waitResponse() == 1;
// }
}
return false;
}
String getModemInfoImpl() {
sendAT(GF("+GMR"));
String res;
if (waitResponse(1000L, res) != 1) { return ""; }
cleanResponseString(res);
return res;
}
// Gets the modem hardware version
String getModemManufacturerImpl() {
return MODEM_MANUFACTURER;
}
// Gets the modem hardware version
String getModemModelImpl() {
String model = MODEM_MODEL;
sendAT(GF("+GMR"));
streamSkipUntil('\n'); // skip the AT version
streamSkipUntil('\n'); // skip the SDK version
streamSkipUntil('\n'); // skip the compile time
// read the hardware from the Bin version
streamSkipUntil('('); // skip the text "Bin version"
String wroom = stream.readStringUntil(
')'); // read the WRoom version in the parethesis
streamSkipUntil('('); // skip the bin version itself
if (waitResponse(1000L) == 1) { // wait for the ending OK
return wroom;
}
return model;
}
// Gets the modem firmware version
String getModemRevisionImpl() {
sendAT(GF("GMR")); // GMR instead of CGMR
String res;
if (waitResponse(1000L, res) != 1) { return ""; }
cleanResponseString(res);
return res;
}
// Gets the modem serial number
String getModemSerialNumberImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
bool factoryDefaultImpl() {
sendAT(GF("+RESTORE"));
return waitResponse() == 1;
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
sendAT(GF("+RST"));
if (waitResponse(10000L) != 1) { return false; }
if (waitResponse(10000L, GF(AT_NL "ready" AT_NL)) != 1) { return false; }
delay(500);
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+GSLP=0")); // Power down indefinitely - until manually reset!
return waitResponse() == 1;
}
bool radioOffImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false)
TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Generic network functions
*/
public:
ESP8266RegStatus getRegistrationStatus() {
sendAT(GF("+CIPSTATUS"));
if (waitResponse(3000, GF("STATUS:")) != 1) return REG_UNKNOWN;
// after "STATUS:" it should return the status number (0,1,2,3,4,5),
// followed by an OK
// Since there are more possible status number codes than the arguments for
// waitResponse, we'll capture the response in a string and then parse it.
String res;
if (waitResponse(3000L, res) != 1) { return REG_UNKNOWN; }
res.trim();
int8_t status = res.toInt();
return (ESP8266RegStatus)status;
}
protected:
int8_t getSignalQualityImpl() {
sendAT(GF("+CWJAP?"));
int8_t res1 = waitResponse(GF("No AP"), GF("+CWJAP:"));
if (res1 != 2) {
waitResponse();
sendAT(GF("+CWJAP_CUR?")); // attempt "current" as used by some firmware
// versions
int8_t res1 = waitResponse(GF("No AP"), GF("+CWJAP_CUR:"));
if (res1 != 2) {
waitResponse();
return 0;
}
}
streamSkipUntil(','); // Skip SSID
streamSkipUntil(','); // Skip BSSID/MAC address
streamSkipUntil(','); // Skip Chanel number
int8_t res2 = stream.parseInt(); // Read RSSI
waitResponse(); // Returns an OK after the value
return res2;
}
bool isNetworkConnectedImpl() {
ESP8266RegStatus s = getRegistrationStatus();
if (s == REG_OK_IP || s == REG_OK_TCP) {
// with these, we're definitely connected
return true;
} else if (s == REG_OK_NO_TCP) {
// with this, we may or may not be connected
if (getLocalIP() == "") {
return false;
} else {
return true;
}
} else {
return false;
}
}
String getLocalIPImpl() {
// attempt with and without 'current' flag
sendAT(GF("+CIPSTA?"));
int8_t res1 = waitResponse(GF("ERROR"), GF("+CIPSTA:"));
if (res1 != 2) {
sendAT(GF("+CIPSTA_CUR?"));
res1 = waitResponse(GF("ERROR"), GF("+CIPSTA_CUR:"));
if (res1 != 2) { return ""; }
}
String res2 = stream.readStringUntil('\n');
res2.replace("ip:", ""); // newer firmwares have this
res2.replace("\"", "");
res2.trim();
waitResponse();
return res2;
}
/*
* Secure socket layer (SSL) functions
*/
// Follows functions as inherited from TinyGsmSSL.tpp
/*
* WiFi functions
*/
protected:
bool networkConnectImpl(const char* ssid, const char* pwd) {
// attempt first without than with the 'current' flag used in some firmware
// versions
sendAT(GF("+CWJAP=\""), ssid, GF("\",\""), pwd, GF("\""));
if (waitResponse(30000L, GFP(GSM_OK), GF(AT_NL "FAIL" AT_NL)) != 1) {
sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\""));
if (waitResponse(30000L, GFP(GSM_OK), GF(AT_NL "FAIL" AT_NL)) != 1) {
return false;
}
}
return true;
}
bool networkDisconnectImpl() {
sendAT(GF("+CWQAP"));
bool retVal = waitResponse(10000L) == 1;
waitResponse(GF("WIFI DISCONNECT"));
return retVal;
}
/*
* GPRS functions
*/
// No functions of this type supported
/*
* SIM card functions
*/
// No functions of this type supported
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// No functions of this type supported
/*
* GSM Location functions
*/
// No functions of this type supported
/*
* GPS/GNSS/GLONASS location functions
*/
// No functions of this type supported
/*
* Time functions
*/
// No functions of this type supported
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
// No functions of this type supported
/*
* Temperature functions
*/
// No functions of this type supported
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75) {
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
if (ssl) {
sendAT(GF("+CIPSSLSIZE=4096"));
waitResponse();
}
sendAT(GF("+CIPSTART="), mux, ',', ssl ? GF("\"SSL") : GF("\"TCP"),
GF("\",\""), host, GF("\","), port, GF(","),
TINY_GSM_TCP_KEEP_ALIVE);
// TODO(?): Check mux
int8_t rsp = waitResponse(timeout_ms, GFP(GSM_OK), GFP(GSM_ERROR),
GF("ALREADY CONNECT"));
// if (rsp == 3) waitResponse();
// May return "ERROR" after the "ALREADY CONNECT"
return (1 == rsp);
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(10000L, GF(AT_NL "SEND OK" AT_NL)) != 1) { return 0; }
return len;
}
bool modemGetConnected(uint8_t mux) {
sendAT(GF("+CIPSTATUS"));
if (waitResponse(3000, GF("STATUS:")) != 1) { return false; }
// after "STATUS:" it should return the status number (0,1,2,3,4,5),
// followed by an OK
// Hopefully we'll catch the "3" here, but fall back to the OK or Error
int8_t status = waitResponse(GF("3"), GFP(GSM_OK), GFP(GSM_ERROR));
// if the status is anything but 3, there are no connections open
if (status != 1) {
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
if (sockets[muxNo]) { sockets[muxNo]->sock_connected = false; }
}
return false;
}
bool verified_connections[TINY_GSM_MUX_COUNT] = {0, 0, 0, 0, 0};
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
uint8_t has_status = waitResponse(GF("+CIPSTATUS:"), GFP(GSM_OK),
GFP(GSM_ERROR));
if (has_status == 1) {
int8_t returned_mux = streamGetIntBefore(',');
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip type
streamSkipUntil(','); // Skip remote IP
streamSkipUntil(','); // Skip remote port
streamSkipUntil(','); // Skip local port
streamSkipUntil('\n'); // Skip client/server type
verified_connections[returned_mux] = 1;
}
if (has_status == 2) break; // once we get to the ok, stop
}
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
if (sockets[muxNo]) {
sockets[muxNo]->sock_connected = verified_connections[muxNo];
}
}
return verified_connections[mux];
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF("+IPD,"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore(':');
int16_t len_orig = len;
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
if (len > sockets[mux]->rx.free()) {
DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
// reset the len to read to the amount free
len = sockets[mux]->rx.free();
}
while (len--) { moveCharFromStreamToFifo(mux); }
// TODO(SRGDamia1): deal with buffer overflow/missed characters
if (len_orig != sockets[mux]->available()) {
DBG("### Different number of characters received than expected: ",
sockets[mux]->available(), " vs ", len_orig);
}
}
data = "";
DBG("### Got Data: ", len_orig, "on", mux);
return true;
} else if (data.endsWith(GF("CLOSED"))) {
int8_t muxStart = TinyGsmMax(0,
data.lastIndexOf(AT_NL, data.length() - 8));
int8_t coma = data.indexOf(',', muxStart);
int8_t mux = data.substring(muxStart, coma).toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
streamSkipUntil('\n'); // throw away the new line
data = "";
DBG("### Closed: ", mux);
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientESP8266* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTESP8266_H_

View file

@ -0,0 +1,504 @@
/**
* @file TinyGsmClientM590.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTM590_H_
#define SRC_TINYGSMCLIENTM590_H_
// #pragma message("TinyGSM: TinyGsmClientM590")
// #define TINY_GSM_DEBUG Serial
#define TINY_GSM_MUX_COUNT 2
#define TINY_GSM_NO_MODEM_BUFFER
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "Neoway"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#define MODEM_MODEL "M590"
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmTime.tpp"
enum M590RegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 3,
REG_DENIED = 2,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmM590 : public TinyGsmModem<TinyGsmM590>,
public TinyGsmGPRS<TinyGsmM590>,
public TinyGsmTCP<TinyGsmM590, TINY_GSM_MUX_COUNT>,
public TinyGsmSMS<TinyGsmM590>,
public TinyGsmTime<TinyGsmM590> {
friend class TinyGsmModem<TinyGsmM590>;
friend class TinyGsmGPRS<TinyGsmM590>;
friend class TinyGsmTCP<TinyGsmM590, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSMS<TinyGsmM590>;
friend class TinyGsmTime<TinyGsmM590>;
/*
* Inner Client
*/
public:
class GsmClientM590 : public GsmClient {
friend class TinyGsmM590;
public:
GsmClientM590() {}
explicit GsmClientM590(TinyGsmM590& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmM590* modem, uint8_t mux = 0) {
this->at = modem;
sock_connected = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
TINY_GSM_YIELD();
at->sendAT(GF("+TCPCLOSE="), mux);
sock_connected = false;
at->waitResponse(maxWaitMs);
rx.clear();
}
void stop() override {
stop(1000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
// NOT SUPPORTED
/*
* Constructor
*/
public:
explicit TinyGsmM590(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientM590"));
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();
DBG(GF("### Modem:"), getModemName());
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
// This is extracted from the modem info
String getModemNameImpl() {
sendAT(GF("I"));
String factory = stream.readStringUntil('\n'); // read the factory
factory.trim();
String model = stream.readStringUntil('\n'); // read the model
model.trim();
streamSkipUntil('\n'); // skip the revision
waitResponse(); // wait for the OK
return factory + String(" ") + model;
}
// This is extracted from the modem info
String getModemManufacturerImpl() {
sendAT(GF("I"));
String factory = stream.readStringUntil('\n'); // read the factory
factory.trim();
streamSkipUntil('\n'); // skip the model
streamSkipUntil('\n'); // skip the revision
if (waitResponse() == 1) { return factory; }
return MODEM_MANUFACTURER;
}
// This is extracted from the modem info
String getModemModelImpl() {
sendAT(GF("I"));
streamSkipUntil('\n'); // skip the factory
String model = stream.readStringUntil('\n'); // read the model
model.trim();
streamSkipUntil('\n'); // skip the revision
if (waitResponse() == 1) { return model; }
return MODEM_MODEL;
}
// Gets the modem firmware version
// This is extracted from the modem info
String getModemRevisionImpl() {
sendAT(GF("I"));
streamSkipUntil('\n'); // skip the factory
streamSkipUntil('\n'); // skip the model
String res = stream.readStringUntil('\n'); // read the revision
res.trim();
waitResponse(); // wait for the OK
return res;
}
// Extra stuff here - pwr save, internal stack
bool factoryDefaultImpl() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
waitResponse();
sendAT(GF("+ICF=3,1")); // 8 data 0 parity 1 stop
waitResponse();
sendAT(GF("+ENPWRSAVE=0")); // Disable PWR save
waitResponse();
sendAT(GF("+XISP=0")); // Use internal stack
waitResponse();
sendAT(GF("&W")); // Write configuration
return waitResponse() == 1;
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
if (!setPhoneFunctionality(15)) { return false; }
// MODEM:STARTUP
waitResponse(60000L, GF(AT_NL "+PBREADY" AT_NL));
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+CPWROFF"));
return waitResponse(3000L) == 1;
}
bool sleepEnableImpl(bool enable = true) {
sendAT(GF("+ENPWRSAVE="), enable);
return waitResponse() == 1;
}
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
return waitResponse(10000L) == 1;
}
/*
* Generic network functions
*/
public:
M590RegStatus getRegistrationStatus() {
return (M590RegStatus)getRegistrationStatusXREG("CREG");
}
protected:
bool isNetworkConnectedImpl() {
M590RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
String getLocalIPImpl() {
sendAT(GF("+XIIC?"));
if (waitResponse(GF(AT_NL "+XIIC:")) != 1) { return ""; }
streamSkipUntil(',');
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Secure socket layer (SSL) functions
*/
// No functions of this type supported
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect();
sendAT(GF("+XISP=0"));
waitResponse();
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
waitResponse();
if (!user) user = "";
if (!pwd) pwd = "";
sendAT(GF("+XGAUTH=1,1,\""), user, GF("\",\""), pwd, GF("\""));
waitResponse();
sendAT(GF("+XIIC=1"));
waitResponse();
const uint32_t timeout_ms = 60000L;
for (uint32_t start = millis(); millis() - start < timeout_ms;) {
if (isGprsConnected()) {
// goto set_dns; // TODO
return true;
}
delay(500);
}
return false;
// set_dns: // TODO
// sendAT(GF("+DNSSERVER=1,8.8.8.8"));
// waitResponse();
//
// sendAT(GF("+DNSSERVER=2,8.8.4.4"));
// waitResponse();
return true;
}
bool gprsDisconnectImpl() {
// TODO(?): There is no command in AT command set
// XIIC=0 does not work
return true;
}
bool isGprsConnectedImpl() {
sendAT(GF("+XIIC?"));
if (waitResponse(GF(AT_NL "+XIIC:")) != 1) { return false; }
int8_t res = streamGetIntBefore(',');
waitResponse();
return res == 1;
}
/*
* SIM card functions
*/
protected:
// Able to follow all SIM card functions as inherited from TinyGsmGPRS.tpp
/*
* Phone Call functions
*/
// No functions of this type supported
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
protected:
bool sendSMS_UTF16Impl(const String& number, const void* text,
size_t len) TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* GSM Location functions
*/
// No functions of this type supported
/*
* GPS/GNSS/GLONASS location functions
*/
// No functions of this type supported
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
// Follows all NTP server functions as inherited from TinyGsmNTP.tpp
/*
* BLE functions
*/
// No functions of this type supported
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
// No functions of this type supported
/*
* Temperature functions
*/
// No functions of this type supported
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool,
int timeout_s = 75) {
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
for (int i = 0; i < 3; i++) { // TODO(?): no need for loop?
String ip = dnsIpQuery(host);
sendAT(GF("+TCPSETUP="), mux, GF(","), ip, GF(","), port);
int8_t rsp = waitResponse(timeout_ms, GF(",OK" AT_NL), GF(",FAIL" AT_NL),
GF("+TCPSETUP:Error" AT_NL));
if (1 == rsp) {
return true;
} else if (3 == rsp) {
sendAT(GF("+TCPCLOSE="), mux);
waitResponse();
}
delay(1000);
}
return false;
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+TCPSEND="), mux, ',', (uint16_t)len);
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.write(static_cast<char>(0x0D));
stream.flush();
if (waitResponse(30000L, GF(AT_NL "+TCPSEND:")) != 1) { return 0; }
streamSkipUntil('\n');
return len;
}
bool modemGetConnected(uint8_t mux) {
sendAT(GF("+CIPSTATUS="), mux);
int8_t res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""),
GF(",\"CLOSING\""), GF(",\"INITIAL\""));
waitResponse();
return 1 == res;
}
String dnsIpQuery(const char* host) {
sendAT(GF("+DNS=\""), host, GF("\""));
if (waitResponse(10000L, GF(AT_NL "+DNS:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse(GF("+DNS:OK" AT_NL));
res.trim();
return res;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF("+TCPRECV:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore(',');
int16_t len_orig = len;
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
if (len > sockets[mux]->rx.free()) {
DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
// reset the len to read to the amount free
len = sockets[mux]->rx.free();
}
while (len--) { moveCharFromStreamToFifo(mux); }
// TODO(?): Handle lost characters
if (len_orig != sockets[mux]->available()) {
DBG("### Different number of characters received than expected: ",
sockets[mux]->available(), " vs ", len_orig);
}
}
data = "";
DBG("### Got Data: ", len_orig, "on", mux);
return true;
} else if (data.endsWith(GF("+TCPCLOSE:"))) {
int8_t mux = streamGetIntBefore(',');
streamSkipUntil('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientM590* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTM590_H_

View file

@ -0,0 +1,592 @@
/**
* @file TinyGsmClientM95.h
* @author Volodymyr Shymanskyy, Pacman Pereira, and Replicade Ltd.
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy, (c)2017 Replicade Ltd.
* <http://www.replicade.com>
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTM95_H_
#define SRC_TINYGSMCLIENTM95_H_
// #pragma message("TinyGSM: TinyGsmClientM95")
// #define TINY_GSM_DEBUG Serial
#define TINY_GSM_MUX_COUNT 6
#define TINY_GSM_BUFFER_READ_NO_CHECK
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "Quectel"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#define MODEM_MODEL "M95"
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmCalling.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmBattery.tpp"
#include "TinyGsmTemperature.tpp"
enum M95RegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmM95 : public TinyGsmModem<TinyGsmM95>,
public TinyGsmGPRS<TinyGsmM95>,
public TinyGsmTCP<TinyGsmM95, TINY_GSM_MUX_COUNT>,
public TinyGsmCalling<TinyGsmM95>,
public TinyGsmSMS<TinyGsmM95>,
public TinyGsmTime<TinyGsmM95>,
public TinyGsmBattery<TinyGsmM95>,
public TinyGsmTemperature<TinyGsmM95> {
friend class TinyGsmModem<TinyGsmM95>;
friend class TinyGsmGPRS<TinyGsmM95>;
friend class TinyGsmTCP<TinyGsmM95, TINY_GSM_MUX_COUNT>;
friend class TinyGsmCalling<TinyGsmM95>;
friend class TinyGsmSMS<TinyGsmM95>;
friend class TinyGsmTime<TinyGsmM95>;
friend class TinyGsmBattery<TinyGsmM95>;
friend class TinyGsmTemperature<TinyGsmM95>;
/*
* Inner Client
*/
public:
class GsmClientM95 : public GsmClient {
friend class TinyGsmM95;
public:
GsmClientM95() {}
explicit GsmClientM95(TinyGsmM95& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmM95* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
sock_connected = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
uint32_t startMillis = millis();
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+QICLOSE="), mux);
sock_connected = false;
at->waitResponse((maxWaitMs - (millis() - startMillis)), GF("CLOSED"),
GF("CLOSE OK"), GF("ERROR"));
}
void stop() override {
stop(75000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
// NOT SUPPORTED
/*
* Constructor
*/
public:
explicit TinyGsmM95(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientM95"));
if (!testAT()) { return false; }
// sendAT(GF("&FZ")); // Factory + Reset
// waitResponse();
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) { return false; }
#ifdef TINY_GSM_DEBUG
sendAT(GF("+CMEE=2")); // turn on verbose error codes
#else
sendAT(GF("+CMEE=0")); // turn off error codes
#endif
waitResponse();
DBG(GF("### Modem:"), getModemName());
// Enable network time synchronization
sendAT(GF("+QNITZ=1"));
if (waitResponse(10000L) != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
sendAT(GF("+CFUN=0"));
if (waitResponse(10000L, GF("NORMAL POWER DOWN"), GF("OK"), GF("FAIL")) ==
3) {
return false;
}
sendAT(GF("+CFUN=1"));
if (waitResponse(10000L, GF("Call Ready"), GF("OK"), GF("FAIL")) == 3) {
return false;
}
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+QPOWD=1"));
return waitResponse(300, GF("NORMAL POWER DOWN")) == 1;
}
// When entering into sleep mode is enabled, DTR is pulled up, and WAKEUP_IN
// is pulled up, the module can directly enter into sleep mode.If entering
// into sleep mode is enabled, DTR is pulled down, and WAKEUP_IN is pulled
// down, there is a need to pull the DTR pin and the WAKEUP_IN pin up first,
// and then the module can enter into sleep mode.
bool sleepEnableImpl(bool enable = true) {
sendAT(GF("+QSCLK="), enable);
return waitResponse() == 1;
}
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false)
TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Generic network functions
*/
public:
M95RegStatus getRegistrationStatus() {
return (M95RegStatus)getRegistrationStatusXREG("CREG");
}
protected:
bool isNetworkConnectedImpl() {
M95RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
void setHostFormat(bool useDottedQuad) {
if (useDottedQuad) {
sendAT(GF("+QIDNSIP=0"));
} else {
sendAT(GF("+QIDNSIP=1"));
}
waitResponse();
}
String getLocalIPImpl() {
sendAT(GF("+QILOCIP"));
streamSkipUntil('\n');
String res = stream.readStringUntil('\n');
res.trim();
return res;
}
/*
* Secure socket layer (SSL) functions
*/
// No functions of this type supported
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect();
// select foreground context 0 = VIRTUAL_UART_1
sendAT(GF("+QIFGCNT=0"));
if (waitResponse() != 1) { return false; }
// Select GPRS (=1) as the Bearer
sendAT(GF("+QICSGP=1,\""), apn, GF("\",\""), user, GF("\",\""), pwd,
GF("\""));
if (waitResponse() != 1) { return false; }
// Select TCP/IP transfer mode - NOT transparent mode
sendAT(GF("+QIMODE=0"));
if (waitResponse() != 1) { return false; }
// Enable multiple TCP/IP connections
sendAT(GF("+QIMUX=1"));
if (waitResponse() != 1) { return false; }
// Start TCPIP Task and Set APN, User Name and Password
sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"");
if (waitResponse() != 1) { return false; }
// Activate GPRS/CSD Context
sendAT(GF("+QIACT"));
if (waitResponse(60000L) != 1) { return false; }
// Check that we have a local IP address
if (localIP() == IPAddress(0, 0, 0, 0)) { return false; }
// Set Method to Handle Received TCP/IP Data
// Mode = 1 - Output a notification when data is received
// +QIRDI: <id>,<sc>,<sid>
sendAT(GF("+QINDI=1"));
if (waitResponse() != 1) { return false; }
// // Request an IP header for received data
// // "IPD(data length):"
// sendAT(GF("+QIHEAD=1"));
// if (waitResponse() != 1) {
// return false;
// }
//
// // Do NOT show the IP address of the sender when receiving data
// // The format to show the address is: RECV FROM: <IP ADDRESS>:<PORT>
// sendAT(GF("+QISHOWRA=0"));
// if (waitResponse() != 1) {
// return false;
// }
//
// // Do NOT show the protocol type at the end of the header for received
// data
// // IPD(data length)(TCP/UDP):
// sendAT(GF("+QISHOWPT=0"));
// if (waitResponse() != 1) {
// return false;
// }
//
// // Do NOT show the destination address before receiving data
// // The format to show the address is: TO:<IP ADDRESS>
// sendAT(GF("+QISHOWLA=0"));
// if (waitResponse() != 1) {
// return false;
// }
return true;
}
bool gprsDisconnectImpl() {
sendAT(GF("+QIDEACT")); // Deactivate the bearer context
return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1;
}
String getProviderImpl() {
sendAT(GF("+QSPN?"));
if (waitResponse(GF("+QSPN:")) != 1) { return ""; }
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"'); // read the provider
waitResponse(); // skip anything else
return res;
}
/*
* SIM card functions
*/
protected:
String getSimCCIDImpl() {
sendAT(GF("+QCCID"));
if (waitResponse(GF(AT_NL "+QCCID:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Phone Call functions
*/
// Follows all phone call functions as inherited from TinyGsmCalling.tpp
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
public:
/** Delete all SMS */
bool deleteAllSMS() {
sendAT(GF("+QMGDA=6"));
if (waitResponse(waitResponse(60000L, GF("OK"), GF("ERROR")) == 1)) {
return true;
}
return false;
}
/*
* GSM Location functions
*/
// No functions of this type supported
/*
* GPS/GNSS/GLONASS location functions
*/
// No functions of this type supported
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
// Follows all NTP server functions as inherited from TinyGsmNTP.tpp
/*
* BLE functions
*/
// No functions of this type supported
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
// Follows all battery functions as inherited from TinyGsmBattery.tpp
/*
* Temperature functions
*/
protected:
float getTemperatureImpl() {
sendAT(GF("+QTEMP?"));
if (waitResponse(GF(AT_NL "+QTEMP:")) != 1) {
return static_cast<float>(-9999);
}
streamSkipUntil(','); // Skip mode
// Read charge of thermistor
// milliVolts = streamGetIntBefore(',');
streamSkipUntil(','); // Skip thermistor charge
float temp = streamGetFloatBefore('\n');
// 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, int timeout_s = 75) {
if (ssl) { DBG("SSL not yet supported on this module!"); }
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
sendAT(GF("+QIOPEN="), mux, GF(",\""), GF("TCP"), GF("\",\""), host,
GF("\","), port);
int8_t rsp = waitResponse(timeout_ms, GF("CONNECT OK" AT_NL),
GF("CONNECT FAIL" AT_NL),
GF("ALREADY CONNECT" AT_NL));
return (1 == rsp);
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+QISEND="), mux, ',', (uint16_t)len);
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(GF(AT_NL "SEND OK")) != 1) { return 0; }
// bool allAcknowledged = false;
// // bool failed = false;
// while ( !allAcknowledged ) {
// sendAT( GF("+QISACK"));
// if (waitResponse(5000L, GF(AT_NL "+QISACK:")) != 1) {
// return -1;
// } else {
// streamSkipUntil(','); // Skip total length sent on connection
// streamSkipUntil(','); // Skip length already acknowledged by remote
// // Make sure the total length un-acknowledged is 0
// if ( streamGetIntBefore('\n') == 0 ) {
// allAcknowledged = true;
// }
// }
// }
// waitResponse(5000L);
return len; // TODO(?): get len/ack properly
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) return 0;
// TODO(?): Does this work????
// AT+QIRD=<id>,<sc>,<sid>,<len>
// id = GPRS context number = 0, set in GPRS connect
// sc = role in connection = 1, client of connection
// sid = index of connection = mux
// len = maximum length of data to retrieve
sendAT(GF("+QIRD=0,1,"), mux, ',', (uint16_t)size);
// If it replies only OK for the write command, it means there is no
// received data in the buffer of the connection.
int8_t res = waitResponse(GF("+QIRD:"), GFP(GSM_OK), GFP(GSM_ERROR));
if (res == 1) {
streamSkipUntil(':'); // skip IP address
streamSkipUntil(','); // skip port
streamSkipUntil(','); // skip connection type (TCP/UDP)
// read the real length of the retrieved data
uint16_t len = streamGetIntBefore('\n');
// We have no way of knowing in advance how much data will be in the
// buffer so when data is received we always assume the buffer is
// completely full. Chances are, this is not true and there's really not
// that much there. In that case, make sure we make sure we re-set the
// amount of data available.
if (len < size) { sockets[mux]->sock_available = len; }
for (uint16_t i = 0; i < len; i++) {
moveCharFromStreamToFifo(mux);
sockets[mux]->sock_available--;
// ^^ One less character available after moving from modem's FIFO to our
// FIFO
}
waitResponse(); // ends with an OK
// DBG("### READ:", len, "from", mux);
return len;
} else {
sockets[mux]->sock_available = 0;
return 0;
}
}
// Not possible to check the number of characters remaining in buffer
size_t modemGetAvailable(uint8_t) {
return 0;
}
bool modemGetConnected(uint8_t mux) {
sendAT(GF("+QISTATE=1,"), mux);
// +QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1"
if (waitResponse(GF("+QISTATE:")) != 1) { return false; }
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip socket type
streamSkipUntil(','); // Skip remote ip
streamSkipUntil(','); // Skip remote port
streamSkipUntil(','); // Skip local port
int8_t res = streamGetIntBefore(','); // socket state
waitResponse();
// 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
return 2 == res;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF(AT_NL "+QIRDI:"))) {
streamSkipUntil(','); // Skip the context
streamSkipUntil(','); // Skip the role
int8_t mux = streamGetIntBefore('\n');
// DBG("### Got Data:", mux);
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
// We have no way of knowing how much data actually came in, so
// we set the value to 1500, the maximum possible size.
sockets[mux]->sock_available = 1500;
}
data = "";
return true;
} else if (data.endsWith(GF("CLOSED" AT_NL))) {
int8_t nl = data.lastIndexOf(AT_NL, data.length() - 8);
int8_t coma = data.indexOf(',', nl + 2);
int8_t mux = data.substring(nl + 2, coma).toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
return true;
} else if (data.endsWith(GF("+QNITZ:"))) {
streamSkipUntil('\n'); // URC for time sync
data = "";
DBG("### Network time updated.");
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientM95* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTM95_H_

View file

@ -0,0 +1,581 @@
/**
* @file TinyGsmClientMC60.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*
* @MC60 support added by Tamas Dajka 2017.10.15 - with fixes by Sara Damiano
*
*/
#ifndef SRC_TINYGSMCLIENTMC60_H_
#define SRC_TINYGSMCLIENTMC60_H_
// #pragma message("TinyGSM: TinyGsmClientMC60")
// #define TINY_GSM_DEBUG Serial
#define TINY_GSM_MUX_COUNT 6
#define TINY_GSM_BUFFER_READ_NO_CHECK
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "Quectel"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#if defined(TINY_GSM_MODEM_MC60E)
#define MODEM_MODEL "MC60E"
#else
#define MODEM_MODEL "MC60"
#endif
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmCalling.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmBattery.tpp"
enum MC60RegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmMC60 : public TinyGsmModem<TinyGsmMC60>,
public TinyGsmGPRS<TinyGsmMC60>,
public TinyGsmTCP<TinyGsmMC60, TINY_GSM_MUX_COUNT>,
public TinyGsmCalling<TinyGsmMC60>,
public TinyGsmSMS<TinyGsmMC60>,
public TinyGsmTime<TinyGsmMC60>,
public TinyGsmBattery<TinyGsmMC60> {
friend class TinyGsmModem<TinyGsmMC60>;
friend class TinyGsmGPRS<TinyGsmMC60>;
friend class TinyGsmTCP<TinyGsmMC60, TINY_GSM_MUX_COUNT>;
friend class TinyGsmCalling<TinyGsmMC60>;
friend class TinyGsmSMS<TinyGsmMC60>;
friend class TinyGsmTime<TinyGsmMC60>;
friend class TinyGsmBattery<TinyGsmMC60>;
/*
* Inner Client
*/
public:
class GsmClientMC60 : public GsmClient {
friend class TinyGsmMC60;
public:
GsmClientMC60() {}
explicit GsmClientMC60(TinyGsmMC60& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmMC60* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
sock_connected = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
uint32_t startMillis = millis();
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+QICLOSE="), mux);
sock_connected = false;
at->waitResponse((maxWaitMs - (millis() - startMillis)), GF("CLOSED"),
GF("CLOSE OK"), GF("ERROR"));
}
void stop() override {
stop(75000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
// NOT SUPPORTED
/*
* Constructor
*/
public:
explicit TinyGsmMC60(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientMC60"));
if (!testAT()) { return false; }
// sendAT(GF("&FZ")); // Factory + Reset
// waitResponse();
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) { return false; }
#ifdef TINY_GSM_DEBUG
sendAT(GF("+CMEE=2")); // turn on verbose error codes
#else
sendAT(GF("+CMEE=0")); // turn off error codes
#endif
waitResponse();
DBG(GF("### Modem:"), getModemName());
// Enable network time synchronization
sendAT(GF("+QNITZ=1"));
if (waitResponse(10000L) != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
if (!setPhoneFunctionality(0)) { return false; }
if (!setPhoneFunctionality(1, true)) { return false; }
delay(3000);
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+QPOWD=1"));
return waitResponse(GF("NORMAL POWER DOWN")) == 1;
}
// When entering into sleep mode is enabled, DTR is pulled up, and WAKEUP_IN
// is pulled up, the module can directly enter into sleep mode.If entering
// into sleep mode is enabled, DTR is pulled down, and WAKEUP_IN is pulled
// down, there is a need to pull the DTR pin and the WAKEUP_IN pin up first,
// and then the module can enter into sleep mode.
bool sleepEnableImpl(bool enable = true) {
sendAT(GF("+QSCLK="), enable);
return waitResponse() == 1;
}
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
return waitResponse(10000L) == 1;
}
/*
* Generic network functions
*/
public:
MC60RegStatus getRegistrationStatus() {
return (MC60RegStatus)getRegistrationStatusXREG("CREG");
}
protected:
bool isNetworkConnectedImpl() {
MC60RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
String getLocalIPImpl() {
sendAT(GF("+QILOCIP"));
streamSkipUntil('\n');
String res = stream.readStringUntil('\n');
res.trim();
return res;
}
/*
* Secure socket layer (SSL) functions
*/
// No functions of this type supported
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect();
// select foreground context 0 = VIRTUAL_UART_1
sendAT(GF("+QIFGCNT=0"));
if (waitResponse() != 1) { return false; }
// Select GPRS (=1) as the Bearer
sendAT(GF("+QICSGP=1,\""), apn, GF("\",\""), user, GF("\",\""), pwd,
GF("\""));
if (waitResponse() != 1) { return false; }
// Define PDP context - is this necessary?
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
waitResponse();
// Activate PDP context - is this necessary?
sendAT(GF("+CGACT=1,1"));
waitResponse(60000L);
// Select TCP/IP transfer mode - NOT transparent mode
sendAT(GF("+QIMODE=0"));
if (waitResponse() != 1) { return false; }
// Enable multiple TCP/IP connections
sendAT(GF("+QIMUX=1"));
if (waitResponse() != 1) { return false; }
// Modem is used as a client
sendAT(GF("+QISRVC=1"));
if (waitResponse() != 1) { return false; }
// Start TCPIP Task and Set APN, User Name and Password
sendAT("+QIREGAPP=\"", apn, "\",\"", user, "\",\"", pwd, "\"");
if (waitResponse() != 1) { return false; }
// Activate GPRS/CSD Context
sendAT(GF("+QIACT"));
if (waitResponse(60000L) != 1) { return false; }
// Check that we have a local IP address
if (localIP() == IPAddress(0, 0, 0, 0)) { return false; }
// Set Method to Handle Received TCP/IP Data
// Mode=2 - Output a notification statement:
// +QIRDI: <id>,<sc>,<sid>,<num>,<len>,< tlen>
sendAT(GF("+QINDI=2"));
if (waitResponse() != 1) { return false; }
return true;
}
bool gprsDisconnectImpl() {
sendAT(GF("+QIDEACT")); // Deactivate the bearer context
return waitResponse(60000L, GF("DEACT OK"), GF("ERROR")) == 1;
}
String getProviderImpl() {
sendAT(GF("+QSPN?"));
if (waitResponse(GF("+QSPN:")) != 1) { return ""; }
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"'); // read the provider
waitResponse(); // skip anything else
return res;
}
/*
* SIM card functions
*/
protected:
SimStatus getSimStatusImpl(uint32_t timeout_ms = 10000L) {
for (uint32_t start = millis(); millis() - start < timeout_ms;) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(AT_NL "+CPIN:")) != 1) {
delay(1000);
continue;
}
int8_t status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"),
GF("NOT INSERTED"), GF("PH_SIM PIN"),
GF("PH_SIM PUK"));
waitResponse();
switch (status) {
case 2:
case 3: return SIM_LOCKED;
case 5:
case 6: return SIM_ANTITHEFT_LOCKED;
case 1: return SIM_READY;
default: return SIM_ERROR;
}
}
return SIM_ERROR;
}
/*
* Phone Call functions
*/
// Follows all phone call functions as inherited from TinyGsmCalling.tpp
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
public:
/** Delete all SMS */
bool deleteAllSMS() {
sendAT(GF("+QMGDA=6"));
if (waitResponse(waitResponse(60000L, GF("OK"), GF("ERROR")) == 1)) {
return true;
}
return false;
}
/*
* GSM Location functions
*/
// No functions of this type supported
/*
* GPS/GNSS/GLONASS location functions
*/
// No functions of this type supported
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
// Follows all battery functions as inherited from TinyGsmBattery.tpp
/*
* Temperature functions
*/
// No functions of this type supported
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75) {
if (ssl) { DBG("SSL not yet supported on this module!"); }
// By default, MC60 expects IP address as 'host' parameter.
// If it is a domain name, "AT+QIDNSIP=1" should be executed.
// "AT+QIDNSIP=0" is for dotted decimal IP address.
IPAddress addr;
sendAT(GF("+QIDNSIP="),
(TinyGsmIpFromString(host) == IPAddress(0, 0, 0, 0) ? 0 : 1));
if (waitResponse() != 1) { return false; }
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
sendAT(GF("+QIOPEN="), mux, GF(",\""), GF("TCP"), GF("\",\""), host,
GF("\","), port);
int8_t rsp = waitResponse(timeout_ms, GF("CONNECT OK" AT_NL),
GF("CONNECT FAIL" AT_NL),
GF("ALREADY CONNECT" AT_NL));
return (1 == rsp);
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+QISEND="), mux, ',', (uint16_t)len);
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(GF(AT_NL "SEND OK")) != 1) { return 0; }
bool allAcknowledged = false;
// bool failed = false;
while (!allAcknowledged) {
sendAT(GF("+QISACK="), mux); // If 'mux' is not specified, MC60 returns
// 'ERRROR' (for QIMUX == 1)
if (waitResponse(5000L, GF(AT_NL "+QISACK:")) != 1) {
return -1;
} else {
streamSkipUntil(','); /** Skip total */
streamSkipUntil(','); /** Skip acknowledged data size */
if (streamGetIntBefore('\n') == 0) { allAcknowledged = true; }
}
}
waitResponse(5000L);
// streamSkipUntil(','); // Skip mux
// return streamGetIntBefore('\n');
return len; // TODO(?): verify len/ack
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) return 0;
// TODO(?): Does this even work????
// AT+QIRD=<id>,<sc>,<sid>,<len>
// id = GPRS context number = 0, set in GPRS connect
// sc = role in connection = 1, client of connection
// sid = index of connection = mux
// len = maximum length of data to retrieve
sendAT(GF("+QIRD=0,1,"), mux, ',', (uint16_t)size);
// If it replies only OK for the write command, it means there is no
// received data in the buffer of the connection.
int8_t res = waitResponse(GF("+QIRD:"), GFP(GSM_OK), GFP(GSM_ERROR));
if (res == 1) {
streamSkipUntil(':'); // skip IP address
streamSkipUntil(','); // skip port
streamSkipUntil(','); // skip connection type (TCP/UDP)
// read the real length of the retrieved data
uint16_t len = streamGetIntBefore('\n');
// It's possible that the real length available is less than expected
// This is quite likely if the buffer is broken into packets - which may
// be different sizes.
// If so, make sure we make sure we re-set the amount of data available.
if (len < size) { sockets[mux]->sock_available = len; }
for (uint16_t i = 0; i < len; i++) {
moveCharFromStreamToFifo(mux);
sockets[mux]->sock_available--;
// ^^ One less character available after moving from modem's FIFO to our
// FIFO
}
waitResponse(); // ends with an OK
// DBG("### READ:", len, "from", mux);
return len;
} else {
sockets[mux]->sock_available = 0;
return 0;
}
}
// Not possible to check the number of characters remaining in buffer
size_t modemGetAvailable(uint8_t) {
return 0;
}
bool modemGetConnected(uint8_t mux) {
sendAT(GF("+QISTATE=1,"), mux);
// +QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1"
if (waitResponse(GF("+QISTATE:")) != 1) { return false; }
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip socket type
streamSkipUntil(','); // Skip remote ip
streamSkipUntil(','); // Skip remote port
streamSkipUntil(','); // Skip local port
int8_t res = streamGetIntBefore(','); // socket state
waitResponse();
// 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
return 2 == res;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF(AT_NL "+QIRDI:"))) { // TODO(?): QIRD? or QIRDI?
// +QIRDI: <id>,<sc>,<sid>,<num>,<len>,< tlen>
streamSkipUntil(','); // Skip the context
streamSkipUntil(','); // Skip the role
// read the connection id
int8_t mux = streamGetIntBefore(',');
// read the number of packets in the buffer
int8_t num_packets = streamGetIntBefore(',');
// read the length of the current packet
streamSkipUntil(
','); // Skip the length of the current package in the buffer
int16_t len_total =
streamGetIntBefore('\n'); // Total length of all packages
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux] &&
num_packets >= 0 && len_total >= 0) {
sockets[mux]->sock_available = len_total;
}
data = "";
// DBG("### Got Data:", len_total, "on", mux);
return true;
} else if (data.endsWith(GF("CLOSED" AT_NL))) {
int8_t nl = data.lastIndexOf(AT_NL, data.length() - 8);
int8_t coma = data.indexOf(',', nl + 2);
int8_t mux = data.substring(nl + 2, coma).toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
return true;
} else if (data.endsWith(GF("+QNITZ:"))) {
streamSkipUntil('\n'); // URC for time sync
DBG("### Network time updated.");
data = "";
}
return false;
}
public:
Stream& stream;
protected:
GsmClientMC60* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTMC60_H_

View file

@ -0,0 +1,774 @@
/**
* @file TinyGsmClientSIM5360.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTSIM5360_H_
#define SRC_TINYGSMCLIENTSIM5360_H_
// #define TINY_GSM_DEBUG Serial
// #define TINY_GSM_USE_HEX
#define TINY_GSM_MUX_COUNT 10
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "SIMCom"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#if defined(TINY_GSM_MODEM_SIM5320)
#define MODEM_MODEL "SIM5320";
#elif defined(TINY_GSM_MODEM_SIM5300)
#define MODEM_MODEL "SIM5300";
#elif defined(TINY_GSM_MODEM_SIM7100)
#define MODEM_MODEL "SIM7100";
#else
#define MODEM_MODEL "SIM5360";
#endif
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmGSMLocation.tpp"
#include "TinyGsmGPS.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmNTP.tpp"
#include "TinyGsmBattery.tpp"
#include "TinyGsmTemperature.tpp"
enum SIM5360RegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmSim5360 : public TinyGsmModem<TinyGsmSim5360>,
public TinyGsmGPRS<TinyGsmSim5360>,
public TinyGsmTCP<TinyGsmSim5360, TINY_GSM_MUX_COUNT>,
public TinyGsmSMS<TinyGsmSim5360>,
public TinyGsmGSMLocation<TinyGsmSim5360>,
public TinyGsmGPS<TinyGsmSim5360>,
public TinyGsmTime<TinyGsmSim5360>,
public TinyGsmNTP<TinyGsmSim5360>,
public TinyGsmBattery<TinyGsmSim5360>,
public TinyGsmTemperature<TinyGsmSim5360> {
friend class TinyGsmModem<TinyGsmSim5360>;
friend class TinyGsmGPRS<TinyGsmSim5360>;
friend class TinyGsmTCP<TinyGsmSim5360, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSMS<TinyGsmSim5360>;
friend class TinyGsmGSMLocation<TinyGsmSim5360>;
friend class TinyGsmGPS<TinyGsmSim5360>;
friend class TinyGsmTime<TinyGsmSim5360>;
friend class TinyGsmNTP<TinyGsmSim5360>;
friend class TinyGsmBattery<TinyGsmSim5360>;
friend class TinyGsmTemperature<TinyGsmSim5360>;
/*
* Inner Client
*/
public:
class GsmClientSim5360 : public GsmClient {
friend class TinyGsmSim5360;
public:
GsmClientSim5360() {}
explicit GsmClientSim5360(TinyGsmSim5360& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmSim5360* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+CIPCLOSE="), mux);
sock_connected = false;
at->waitResponse();
}
void stop() override {
stop(15000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
// NOT SUPPORTED
/*
* Constructor
*/
public:
explicit TinyGsmSim5360(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM5360"));
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();
DBG(GF("### Modem:"), getModemName());
// Disable time and time zone URC's
sendAT(GF("+CTZR=0"));
if (waitResponse(10000L) != 1) { return false; }
// Enable automatic time zome update
sendAT(GF("+CTZU=1"));
if (waitResponse(10000L) != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
bool factoryDefaultImpl() { // these commands aren't supported
return false;
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
sendAT(GF("+REBOOT"));
// Should return an 'OK' after reboot command is sent
if (waitResponse(10000L) != 1) { return false; }
// After booting, modem sends out messages as each of its
// internal modules loads. The final message is "PB DONE".
if (waitResponse(40000L, GF(AT_NL "PB DONE")) != 1) { return false; }
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+CPOF"));
return waitResponse() == 1;
}
bool radioOffImpl() {
if (!setPhoneFunctionality(4)) { return false; }
delay(3000);
return true;
}
bool sleepEnableImpl(bool enable = true) {
sendAT(GF("+CSCLK="), enable);
return waitResponse() == 1;
}
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
return waitResponse(10000L) == 1;
}
/*
* Generic network functions
*/
public:
SIM5360RegStatus getRegistrationStatus() {
return (SIM5360RegStatus)getRegistrationStatusXREG("CGREG");
}
protected:
bool isNetworkConnectedImpl() {
SIM5360RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
public:
String getNetworkModes() {
sendAT(GF("+CNMP=?"));
if (waitResponse(GF(AT_NL "+CNMP:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
return res;
}
int16_t getNetworkMode() {
sendAT(GF("+CNMP?"));
if (waitResponse(GF(AT_NL "+CNMP:")) != 1) { return false; }
int16_t mode = streamGetIntBefore('\n');
waitResponse();
return mode;
}
bool setNetworkMode(uint8_t mode) {
sendAT(GF("+CNMP="), mode);
return waitResponse() == 1;
}
String getLocalIPImpl() {
sendAT(GF("+IPADDR")); // Inquire Socket PDP address
// sendAT(GF("+CGPADDR=1")); // Show PDP address
String res;
if (waitResponse(10000L, res) != 1) { return ""; }
cleanResponseString(res);
return res;
}
/*
* Secure socket layer (SSL) functions
*/
// No functions of this type supported
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect(); // Make sure we're not connected first
// Define the PDP context
// The CGDCONT commands set up the "external" PDP context
// Set the external authentication
if (user && strlen(user) > 0) {
sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"');
waitResponse();
}
// Define external PDP context 1
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"', ",\"0.0.0.0\",0,0");
waitResponse();
// The CGSOCKCONT commands define the "embedded" PDP context for TCP/IP
// Define the socket PDP context
sendAT(GF("+CGSOCKCONT=1,\"IP\",\""), apn, '"');
waitResponse();
// Set the embedded authentication
if (user && strlen(user) > 0) {
sendAT(GF("+CSOCKAUTH=1,1,\""), user, "\",\"", pwd, '"');
waitResponse();
}
// Set active PDP context's profile number
// This ties the embedded TCP/IP application to the external PDP context
sendAT(GF("+CSOCKSETPN=1"));
waitResponse();
// Configure TCP parameters
// Select TCP/IP application mode (command mode)
sendAT(GF("+CIPMODE=0"));
waitResponse();
// Set Sending Mode - send without waiting for peer TCP ACK
sendAT(GF("+CIPSENDMODE=0"));
waitResponse();
// Configure socket parameters
// AT+CIPCCFG= <NmRetry>, <DelayTm>, <Ack>, <errMode>, <HeaderType>,
// <AsyncMode>, <TimeoutVal>
// NmRetry = number of retransmission to be made for an IP packet
// = 10 (default)
// DelayTm = number of milliseconds to delay before outputting received data
// = 0 (default)
// Ack = sets whether reporting a string "Send ok" = 0 (don't report)
// errMode = mode of reporting error result code = 0 (numberic values)
// HeaderType = which data header of receiving data in multi-client mode
// = 1 (+RECEIVE,<link num>,<data length>)
// AsyncMode = sets mode of executing commands
// = 0 (synchronous command executing)
// TimeoutVal = minimum retransmission timeout in milliseconds = 75000
sendAT(GF("+CIPCCFG=10,0,0,0,1,0,75000"));
if (waitResponse() != 1) { return false; }
// Configure timeouts for opening and closing sockets
// AT+CIPTIMEOUT=<netopen_timeout>, <cipopen_timeout>, <cipsend_timeout>
sendAT(GF("+CIPTIMEOUT="), 75000, ',', 15000, ',', 15000);
waitResponse();
// Start the socket service
// This activates and attaches to the external PDP context that is tied
// to the embedded context for TCP/IP (ie AT+CGACT=1,1 and AT+CGATT=1)
// Response may be an immediate "OK" followed later by "+NETOPEN: 0".
// We to ignore any immediate response and wait for the
// URC to show it's really connected.
sendAT(GF("+NETOPEN"));
if (waitResponse(75000L, GF(AT_NL "+NETOPEN: 0")) != 1) { return false; }
return true;
}
bool gprsDisconnectImpl() {
// Close any open sockets
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClientSim5360* sock = sockets[mux];
if (sock) { sock->stop(); }
}
// Stop the socket service
// Note: all sockets should be closed first - on 3G/4G models the sockets
// must be closed manually
sendAT(GF("+NETCLOSE"));
if (waitResponse(60000L, GF(AT_NL "+NETCLOSE: 0")) != 1) { return false; }
return true;
}
bool isGprsConnectedImpl() {
sendAT(GF("+NETOPEN?"));
// May return +NETOPEN: 1, 0. We just confirm that the first number is 1
if (waitResponse(GF(AT_NL "+NETOPEN: 1")) != 1) { return false; }
waitResponse();
sendAT(GF("+IPADDR")); // Inquire Socket PDP address
// sendAT(GF("+CGPADDR=1")); // Show PDP address
if (waitResponse() != 1) { return false; }
return true;
}
String getProviderImpl() {
sendAT(GF("+CSPN?"));
if (waitResponse(GF("+CSPN:")) != 1) { return ""; }
streamSkipUntil('"'); /* Skip mode and format */
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
/*
* SIM card functions
*/
protected:
// Gets the CCID of a sim card via AT+CCID
String getSimCCIDImpl() {
sendAT(GF("+CICCID"));
if (waitResponse(GF(AT_NL "+ICCID:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Phone Call functions
*/
// No functions of this type supported
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
/*
* GSM Location functions
*/
// SIM5360 and SIM7100 can return a GSM-based location from CLBS as per the
// template; SIM5320 doesn't not appear to be able to
/*
* GPS/GNSS/GLONASS location functions
*/
protected:
// enable GPS
bool enableGPSImpl() {
sendAT(GF("+CGPS=1"));
if (waitResponse() != 1) { return false; }
return true;
}
bool disableGPSImpl() {
sendAT(GF("+CGPS=0"));
if (waitResponse() != 1) { return false; }
return true;
}
// get the RAW GPS output
String getGPSrawImpl() {
sendAT(GF("+CGPSINFO"));
if (waitResponse(GF(AT_NL "+CGPSINFO:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
// get GPS informations
bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0,
int* vsat = 0, int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
int* minute = 0, int* second = 0) {
sendAT(GF("+CGPSINFO"));
if (waitResponse(GF(AT_NL "+CGPSINFO:")) != 1) { return false; }
delay(30);
float ilat = 0;
char north;
float ilon = 0;
char east;
float ispeed = 0;
float ialt = 0;
int ivsat = 0;
int iusat = 0;
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
float secondWithSS = 0;
ilat = streamGetFloatBefore(','); // Latitude in ddmm.mmmmmm
north = stream.read(); // N/S Indicator, N=north or S=south
streamSkipUntil(','); // BEIDOU satellite valid numbers
ilon = streamGetFloatBefore(','); // Longitude in dddmm.mmmmmm
east = stream.read(); // E/W Indicator, E=east or W=west
streamSkipUntil(','); // BEIDOU satellite valid numbers
// Date. Output format is ddmmyy
iday = streamGetIntLength(2); // Two digit day
imonth = streamGetIntLength(2); // Two digit month
iyear = streamGetIntBefore(','); // Two digit year
// UTC Time. Output format is hhmmss.s
ihour = streamGetIntLength(2); // Two digit hour
imin = streamGetIntLength(2); // Two digit minute
secondWithSS = streamGetFloatBefore(','); // 4 digit second with subseconds
ialt = streamGetFloatBefore(','); // MSL Altitude. Unit is meters
ispeed = streamGetFloatBefore(','); // Speed Over Ground. Unit is knots.
if (ilat != -9999.0F) {
if (lat != nullptr)
*lat = (floor(ilat / 100) + fmod(ilat, 100.) / 60) *
(north == 'N' ? 1 : -1);
if (lon != nullptr)
*lon = (floor(ilon / 100) + fmod(ilon, 100.) / 60) *
(east == 'E' ? 1 : -1);
if (speed != nullptr) *speed = ispeed;
if (alt != nullptr) *alt = ialt;
if (vsat != nullptr) *vsat = ivsat;
if (usat != nullptr) *usat = iusat;
if (accuracy != nullptr) *accuracy = -9999;
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = static_cast<int>(secondWithSS);
waitResponse();
return true;
}
waitResponse();
return false;
}
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
// Follows all NTP server functions as inherited from TinyGsmNTP.tpp
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
protected:
// SRGD Note: Returns voltage in VOLTS instead of millivolts
int16_t getBattVoltageImpl() {
sendAT(GF("+CBC"));
if (waitResponse(GF(AT_NL "+CBC:")) != 1) { return 0; }
streamSkipUntil(','); // Skip battery charge status
streamSkipUntil(','); // Skip battery charge level
// get voltage in VOLTS
float voltage = streamGetFloatBefore('\n');
// Wait for final OK
waitResponse();
// Return millivolts
uint16_t res = voltage * 1000;
return res;
}
// SRGD Note: Returns voltage in VOLTS instead of millivolts
bool getBattStatsImpl(int8_t& chargeState, int8_t& percent,
int16_t& milliVolts) {
sendAT(GF("+CBC"));
if (waitResponse(GF(AT_NL "+CBC:")) != 1) { return false; }
chargeState = streamGetIntBefore(',');
percent = streamGetIntBefore(',');
// get voltage in VOLTS
float voltage = streamGetFloatBefore('\n');
milliVolts = voltage * 1000;
// Wait for final OK
waitResponse();
return true;
}
/*
* Temperature functions
*/
protected:
// get temperature in degree celsius
float getTemperatureImpl() {
// Enable Temparature Reading
sendAT(GF("+CMTE=1"));
if (waitResponse() != 1) { return 0; }
// Get Temparature Value
sendAT(GF("+CMTE?"));
if (waitResponse(GF(AT_NL "+CMTE:")) != 1) { return false; }
float res = streamGetFloatBefore('\n');
// Wait for final OK
waitResponse();
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 15) {
if (ssl) { DBG("SSL not yet supported on this module!"); }
// Make sure we'll be getting data manually on this connection
sendAT(GF("+CIPRXGET=1"));
if (waitResponse() != 1) { return false; }
// Establish a connection in multi-socket mode
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
sendAT(GF("+CIPOPEN="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","),
port);
// The reply is +CIPOPEN: ## of socket created
if (waitResponse(timeout_ms, GF(AT_NL "+CIPOPEN:")) != 1) { return false; }
return true;
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(GF(AT_NL "+CIPSEND:")) != 1) { return 0; }
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip requested bytes to send
// TODO(?): make sure requested and confirmed bytes match
return streamGetIntBefore('\n');
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) return 0;
#ifdef TINY_GSM_USE_HEX
sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
#else
sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
#endif
streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX
streamSkipUntil(','); // Skip mux/cid (connecion id)
int16_t len_requested = streamGetIntBefore(',');
// ^^ Requested number of data bytes (1-1460 bytes)to be read
int16_t len_confirmed = streamGetIntBefore('\n');
// ^^ The data length which not read in the buffer
for (int i = 0; i < len_requested; i++) {
uint32_t startMillis = millis();
#ifdef TINY_GSM_USE_HEX
while (stream.available() < 2 &&
(millis() - startMillis < sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char buf[4] = {
0,
};
buf[0] = stream.read();
buf[1] = stream.read();
char c = strtol(buf, nullptr, 16);
#else
while (!stream.available() &&
(millis() - startMillis < sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char c = stream.read();
#endif
sockets[mux]->rx.put(c);
}
// DBG("### READ:", len_requested, "from", mux);
// sockets[mux]->sock_available = modemGetAvailable(mux);
sockets[mux]->sock_available = len_confirmed;
waitResponse();
return len_requested;
}
size_t modemGetAvailable(uint8_t mux) {
if (!sockets[mux]) return 0;
sendAT(GF("+CIPRXGET=4,"), mux);
size_t result = 0;
if (waitResponse(GF("+CIPRXGET:")) == 1) {
streamSkipUntil(','); // Skip mode 4
streamSkipUntil(','); // Skip mux
result = streamGetIntBefore('\n');
waitResponse();
}
// DBG("### Available:", result, "on", mux);
if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
return result;
}
bool modemGetConnected(uint8_t mux) {
// Read the status of all sockets at once
sendAT(GF("+CIPCLOSE?"));
if (waitResponse(GF("+CIPCLOSE:")) != 1) { return false; }
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
// +CIPCLOSE:<link0_state>,<link1_state>,...,<link9_state>
bool muxState = stream.parseInt();
if (sockets[muxNo]) { sockets[muxNo]->sock_connected = muxState; }
}
waitResponse(); // Should be an OK at the end
if (!sockets[mux]) return false;
return sockets[mux]->sock_connected;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF(AT_NL "+CIPRXGET:"))) {
int8_t mode = streamGetIntBefore(',');
if (mode == 1) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
}
data = "";
// DBG("### Got Data:", mux);
return true;
} else {
data += mode;
return false;
}
} else if (data.endsWith(GF(AT_NL "+RECEIVE:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
}
data = "";
// DBG("### Got Data:", len, "on", mux);
return true;
} else if (data.endsWith(GF("+IPCLOSE:"))) {
int8_t mux = streamGetIntBefore(',');
streamSkipUntil('\n'); // Skip the reason code
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
return true;
} else if (data.endsWith(GF("+CIPEVENT:"))) {
// Need to close all open sockets and release the network library.
// User will then need to reconnect.
DBG("### Network error!");
if (!isGprsConnected()) { gprsDisconnect(); }
data = "";
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientSim5360* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTSIM5360_H_

View file

@ -0,0 +1,508 @@
/**
* @file TinyGsmClientSIM7000.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTSIM7000_H_
#define SRC_TINYGSMCLIENTSIM7000_H_
// #define TINY_GSM_DEBUG Serial
// #define TINY_GSM_USE_HEX
#define TINY_GSM_MUX_COUNT 8
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#include "TinyGsmClientSIM70xx.h"
#include "TinyGsmTCP.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmGSMLocation.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmNTP.tpp"
#include "TinyGsmBattery.tpp"
class TinyGsmSim7000 : public TinyGsmSim70xx<TinyGsmSim7000>,
public TinyGsmTCP<TinyGsmSim7000, TINY_GSM_MUX_COUNT>,
public TinyGsmSMS<TinyGsmSim7000>,
public TinyGsmTime<TinyGsmSim7000>,
public TinyGsmNTP<TinyGsmSim7000>,
public TinyGsmGSMLocation<TinyGsmSim7000>,
public TinyGsmBattery<TinyGsmSim7000> {
friend class TinyGsmSim70xx<TinyGsmSim7000>;
friend class TinyGsmModem<TinyGsmSim7000>;
friend class TinyGsmGPRS<TinyGsmSim7000>;
friend class TinyGsmTCP<TinyGsmSim7000, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSMS<TinyGsmSim7000>;
friend class TinyGsmGSMLocation<TinyGsmSim7000>;
friend class TinyGsmGPS<TinyGsmSim7000>;
friend class TinyGsmTime<TinyGsmSim7000>;
friend class TinyGsmNTP<TinyGsmSim7000>;
friend class TinyGsmBattery<TinyGsmSim7000>;
/*
* Inner Client
*/
public:
class GsmClientSim7000 : public GsmClient {
friend class TinyGsmSim7000;
public:
GsmClientSim7000() {}
explicit GsmClientSim7000(TinyGsmSim7000& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmSim7000* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+CIPCLOSE="), mux);
sock_connected = false;
at->waitResponse(3000);
}
void stop() override {
stop(15000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
// NOTE: Use modem TinyGsmSim7000SSL for a secure client!
/*
* Constructor
*/
public:
explicit TinyGsmSim7000(Stream& stream)
: TinyGsmSim70xx<TinyGsmSim7000>(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM7000"));
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();
DBG(GF("### Modem:"), getModemName());
// Enable Local Time Stamp for getting network time
sendAT(GF("+CLTS=1"));
if (waitResponse(10000L) != 1) { return false; }
// Enable battery checks
sendAT(GF("+CBATCHK=1"));
if (waitResponse() != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
/*
* Power functions
*/
// Follows functions as inherited from TinyGsmClientSIM70xx.h
/*
* Generic network functions
*/
protected:
String getLocalIPImpl() {
sendAT(GF("+CIFSR;E0"));
String res;
if (waitResponse(10000L, res) != 1) { return ""; }
cleanResponseString(res);
return res;
}
/*
* Secure socket layer (SSL) functions
*/
// NOTE: Use modem TinyGsmSim7000SSL for a secure client!
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect();
// Bearer settings for applications based on IP
// Set the connection type to GPRS
sendAT(GF("+SAPBR=3,1,\"Contype\",\"GPRS\""));
waitResponse();
// Set the APN
sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"');
waitResponse();
// Set the user name
if (user && strlen(user) > 0) {
sendAT(GF("+SAPBR=3,1,\"USER\",\""), user, '"');
waitResponse();
}
// Set the password
if (pwd && strlen(pwd) > 0) {
sendAT(GF("+SAPBR=3,1,\"PWD\",\""), pwd, '"');
waitResponse();
}
// Define the PDP context
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
waitResponse();
// Attach to GPRS
sendAT(GF("+CGATT=1"));
if (waitResponse(60000L) != 1) { return false; }
// Activate the PDP context
sendAT(GF("+CGACT=1,1"));
waitResponse(60000L);
// Open the definied GPRS bearer context
sendAT(GF("+SAPBR=1,1"));
waitResponse(85000L);
// Query the GPRS bearer context status
sendAT(GF("+SAPBR=2,1"));
if (waitResponse(30000L) != 1) { return false; }
// Set the TCP application toolkit to multi-IP
sendAT(GF("+CIPMUX=1"));
if (waitResponse() != 1) { return false; }
// Put the TCP application toolkit in "quick send" mode
// (thus no extra "Send OK")
sendAT(GF("+CIPQSEND=1"));
if (waitResponse() != 1) { return false; }
// Set the TCP application toolkit to get data manually
sendAT(GF("+CIPRXGET=1"));
if (waitResponse() != 1) { return false; }
// Start the TCP application toolkit task and set APN, USER NAME, PASSWORD
sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
if (waitResponse(60000L) != 1) { return false; }
// Bring up the TCP application toolkit wireless connection with GPRS or CSD
sendAT(GF("+CIICR"));
if (waitResponse(60000L) != 1) { return false; }
// Get local IP address for the TCP application toolkit
// only assigned after connection
sendAT(GF("+CIFSR;E0"));
if (waitResponse(10000L) != 1) { return false; }
return true;
}
bool gprsDisconnectImpl() {
// Shut the TCP application toolkit connection
// CIPSHUT will close *all* open TCP application toolkit connections
sendAT(GF("+CIPSHUT"));
if (waitResponse(60000L) != 1) { return false; }
sendAT(GF("+CGATT=0")); // Deactivate the bearer context
if (waitResponse(60000L) != 1) { return false; }
return true;
}
/*
* SIM card functions
*/
// Follows functions as inherited from TinyGsmClientSIM70xx.h
/*
* Phone Call functions
*/
// No functions of this type supported
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
/*
* GSM Location functions
*/
// Follows all GSM-based location functions as inherited from
// TinyGsmGSMLocation.tpp
/*
* GPS/GNSS/GLONASS location functions
*/
// Follows functions as inherited from TinyGsmClientSIM70xx.h
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
// Follows all NTP server functions as inherited from TinyGsmNTP.tpp
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
// Follows all battery functions as inherited from TinyGsmBattery.tpp
/*
* Temperature functions
*/
// No functions of this type supported
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75) {
if (ssl) { DBG("SSL only supported using application on SIM7000!"); }
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
// when not using SSL, the TCP application toolkit is more stable
sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host,
GF("\","), port);
return (1 ==
waitResponse(timeout_ms, GF("CONNECT OK" AT_NL),
GF("CONNECT FAIL" AT_NL), GF("ALREADY CONNECT" AT_NL),
GF("ERROR" AT_NL), GF("CLOSE OK" AT_NL)));
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(GF(AT_NL "DATA ACCEPT:"), GF("SEND FAIL")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip mux
return streamGetIntBefore('\n');
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) return 0;
#ifdef TINY_GSM_USE_HEX
sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
#else
sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
#endif
streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX
streamSkipUntil(','); // Skip mux
int16_t len_requested = streamGetIntBefore(',');
// ^^ Requested number of data bytes (1-1460 bytes)to be read
int16_t len_confirmed = streamGetIntBefore('\n');
// ^^ Confirmed number of data bytes to be read, which may be less than
// requested. 0 indicates that no data can be read.
// SRGD NOTE: Contrary to above (which is copied from AT command manual)
// this is actually be the number of bytes that will be remaining in the
// buffer after the read.
for (int i = 0; i < len_requested; i++) {
uint32_t startMillis = millis();
#ifdef TINY_GSM_USE_HEX
while (stream.available() < 2 &&
(millis() - startMillis < sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char buf[4] = {
0,
};
buf[0] = stream.read();
buf[1] = stream.read();
char c = strtol(buf, nullptr, 16);
#else
while (!stream.available() &&
(millis() - startMillis < sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char c = stream.read();
#endif
sockets[mux]->rx.put(c);
}
// DBG("### READ:", len_requested, "from", mux);
// sockets[mux]->sock_available = modemGetAvailable(mux);
sockets[mux]->sock_available = len_confirmed;
waitResponse();
return len_requested;
}
size_t modemGetAvailable(uint8_t mux) {
if (!sockets[mux]) return 0;
sendAT(GF("+CIPRXGET=4,"), mux);
size_t result = 0;
if (waitResponse(GF("+CIPRXGET:")) == 1) {
streamSkipUntil(','); // Skip mode 4
streamSkipUntil(','); // Skip mux
result = streamGetIntBefore('\n');
waitResponse();
}
// DBG("### Available:", result, "on", mux);
if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
return result;
}
bool modemGetConnected(uint8_t mux) {
sendAT(GF("+CIPSTATUS="), mux);
waitResponse(GF("+CIPSTATUS"));
int8_t res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""),
GF(",\"CLOSING\""), GF(",\"REMOTE CLOSING\""),
GF(",\"INITIAL\""));
waitResponse();
return 1 == res;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF(AT_NL "+CIPRXGET:"))) {
int8_t mode = streamGetIntBefore(',');
if (mode == 1) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
}
data = "";
// DBG("### Got Data:", mux);
return true;
} else {
data += mode;
return false;
}
} else if (data.endsWith(GF(AT_NL "+RECEIVE:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
}
data = "";
// DBG("### Got Data:", len, "on", mux);
return true;
} else if (data.endsWith(GF("CLOSED" AT_NL))) {
int8_t nl = data.lastIndexOf(AT_NL, data.length() - 8);
int8_t coma = data.indexOf(',', nl + 2);
int8_t mux = data.substring(nl + 2, coma).toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
return true;
} else if (data.endsWith(GF("*PSNWID:"))) {
streamSkipUntil('\n'); // Refresh network name by network
data = "";
DBG("### Network name updated.");
return true;
} else if (data.endsWith(GF("*PSUTTZ:"))) {
streamSkipUntil('\n'); // Refresh time and time zone by network
data = "";
DBG("### Network time and time zone updated.");
return true;
} else if (data.endsWith(GF("+CTZV:"))) {
streamSkipUntil('\n'); // Refresh network time zone by network
data = "";
DBG("### Network time zone updated.");
return true;
} else if (data.endsWith(GF("DST: "))) {
streamSkipUntil('\n'); // Refresh Network Daylight Saving Time by network
data = "";
DBG("### Daylight savings time state updated.");
return true;
} else if (data.endsWith(GF(AT_NL "SMS Ready" AT_NL))) {
data = "";
DBG("### Unexpected module reset!");
init();
return true;
}
return false;
}
protected:
GsmClientSim7000* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTSIM7000_H_

View file

@ -0,0 +1,699 @@
/**
* @file TinyGsmClientSim7000SSL.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTSIM7000SSL_H_
#define SRC_TINYGSMCLIENTSIM7000SSL_H_
// #define TINY_GSM_DEBUG Serial
// #define TINY_GSM_USE_HEX
#define TINY_GSM_MUX_COUNT 2
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#include "TinyGsmClientSIM70xx.h"
#include "TinyGsmTCP.tpp"
#include "TinyGsmSSL.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmGSMLocation.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmNTP.tpp"
#include "TinyGsmBattery.tpp"
class TinyGsmSim7000SSL
: public TinyGsmSim70xx<TinyGsmSim7000SSL>,
public TinyGsmTCP<TinyGsmSim7000SSL, TINY_GSM_MUX_COUNT>,
public TinyGsmSSL<TinyGsmSim7000SSL, TINY_GSM_MUX_COUNT>,
public TinyGsmSMS<TinyGsmSim7000SSL>,
public TinyGsmGSMLocation<TinyGsmSim7000SSL>,
public TinyGsmTime<TinyGsmSim7000SSL>,
public TinyGsmNTP<TinyGsmSim7000SSL>,
public TinyGsmBattery<TinyGsmSim7000SSL> {
friend class TinyGsmSim70xx<TinyGsmSim7000SSL>;
friend class TinyGsmTCP<TinyGsmSim7000SSL, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSSL<TinyGsmSim7000SSL, TINY_GSM_MUX_COUNT>;
friend class TinyGsmModem<TinyGsmSim7000SSL>;
friend class TinyGsmGPRS<TinyGsmSim7000SSL>;
friend class TinyGsmSMS<TinyGsmSim7000SSL>;
friend class TinyGsmGSMLocation<TinyGsmSim7000SSL>;
friend class TinyGsmGPS<TinyGsmSim7000SSL>;
friend class TinyGsmNTP<TinyGsmSim7000SSL>;
friend class TinyGsmTime<TinyGsmSim7000SSL>;
friend class TinyGsmBattery<TinyGsmSim7000SSL>;
/*
* Inner Client
*/
public:
class GsmClientSim7000SSL : public GsmClient {
friend class TinyGsmSim7000SSL;
public:
GsmClientSim7000SSL() {}
explicit GsmClientSim7000SSL(TinyGsmSim7000SSL& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmSim7000SSL* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+CACLOSE="), mux);
sock_connected = false;
at->waitResponse(3000);
}
void stop() override {
stop(15000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
public:
class GsmClientSecureSIM7000SSL : public GsmClientSim7000SSL {
public:
GsmClientSecureSIM7000SSL() {}
explicit GsmClientSecureSIM7000SSL(TinyGsmSim7000SSL& modem,
uint8_t mux = 0)
: GsmClientSim7000SSL(modem, mux) {}
bool setCertificate(const String& certificateName) {
return at->setCertificate(certificateName, mux);
}
virtual int connect(const char* host, uint16_t port,
int timeout_s) override {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
};
/*
* Constructor
*/
public:
explicit TinyGsmSim7000SSL(Stream& stream)
: TinyGsmSim70xx<TinyGsmSim7000SSL>(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM7000SSL"));
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();
DBG(GF("### Modem:"), getModemName());
// Enable Local Time Stamp for getting network time
sendAT(GF("+CLTS=1"));
if (waitResponse(10000L) != 1) { return false; }
// Enable battery checks
sendAT(GF("+CBATCHK=1"));
if (waitResponse() != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
void maintainImpl() {
// Keep listening for modem URC's and proactively iterate through
// sockets asking if any data is avaiable
bool check_socks = false;
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClientSim7000SSL* sock = sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
check_socks = true;
}
}
// modemGetAvailable checks all socks, so we only want to do it once
// modemGetAvailable calls modemGetConnected(), which also checks allf
if (check_socks) { modemGetAvailable(0); }
while (stream.available()) { waitResponse(15, nullptr, nullptr); }
}
/*
* Power functions
*/
// Follows functions as inherited from TinyGsmClientSIM70xx.h
/*
* Generic network functions
*/
protected:
String getLocalIPImpl() {
sendAT(GF("+CNACT?"));
if (waitResponse(GF(AT_NL "+CNACT:")) != 1) { return ""; }
streamSkipUntil('\"');
String res = stream.readStringUntil('\"');
waitResponse();
return res;
}
/*
* Secure socket layer (SSL) functions
*/
// Follows functions as inherited from TinyGsmSSL.tpp
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect();
// Define the PDP context
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
waitResponse();
// Attach to GPRS
sendAT(GF("+CGATT=1"));
if (waitResponse(60000L) != 1) { return false; }
// NOTE: **DO NOT** activate the PDP context
// For who only knows what reason, doing so screws up the rest of the
// process
// Bearer settings for applications based on IP
// Set the user name and password
// AT+CNCFG=<ip_type>[,<APN>[,<usename>,<password>[,<authentication>]]]
//<ip_type> 0: Dual PDN Stack
// 1: Internet Protocol Version 4
// 2: Internet Protocol Version 6
//<authentication> 0: NONE
// 1: PAP
// 2: CHAP
// 3: PAP or CHAP
if (pwd && strlen(pwd) > 0 && user && strlen(user) > 0) {
sendAT(GF("+CNCFG=1,\""), apn, "\",\"", "\",\"", user, pwd, "\",3");
waitResponse();
} else if (user && strlen(user) > 0) {
// Set the user name only
sendAT(GF("+CNCFG=1,\""), apn, "\",\"", user, '"');
waitResponse();
} else {
// Set the APN only
sendAT(GF("+CNCFG=1,\""), apn, '"');
waitResponse();
}
// Activate application network connection
// This is for most other supported applications outside of the
// TCP application toolkit (ie, SSL)
// AT+CNACT=<mode>,<action>
// <mode> 0: Deactive
// 1: Active
// 2: Auto Active
bool res = false;
int ntries = 0;
while (!res && ntries < 5) {
sendAT(GF("+CNACT=1,\""), apn, GF("\""));
res = waitResponse(60000L, GF(AT_NL "+APP PDP: ACTIVE"),
GF(AT_NL "+APP PDP: DEACTIVE")) == 1;
waitResponse();
ntries++;
}
return res;
}
bool gprsDisconnectImpl() {
// Shut down the general application TCP/IP connection
// CNACT will close *all* open application connections
sendAT(GF("+CNACT=0"));
if (waitResponse(60000L) != 1) { return false; }
sendAT(GF("+CGATT=0")); // Deactivate the bearer context
if (waitResponse(60000L) != 1) { return false; }
return true;
}
/*
* SIM card functions
*/
// Follows functions as inherited from TinyGsmClientSIM70xx.h
/*
* Phone Call functions
*/
// No functions of this type supported
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
/*
* GSM Location functions
*/
// Follows all GSM-based location functions as inherited from
// TinyGsmGSMLocation.tpp
/*
* GPS/GNSS/GLONASS location functions
*/
// Follows functions as inherited from TinyGsmClientSIM70xx.h
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
// Follows all NTP server functions as inherited from TinyGsmNTP.tpp
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
// Follows all battery functions as inherited from TinyGsmBattery.tpp
/*
* Temperature functions
*/
// No functions of this type supported
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75) {
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
// set the connection (mux) identifier to use
sendAT(GF("+CACID="), mux);
if (waitResponse(timeout_ms) != 1) return false;
if (ssl) {
// set the ssl version
// AT+CSSLCFG="SSLVERSION",<ctxindex>,<sslversion>
// <ctxindex> PDP context identifier - for reasons not understood by me,
// use PDP context identifier of 0 for what we defined as 1 in
// the gprsConnect function
// <sslversion> 0: QAPI_NET_SSL_PROTOCOL_UNKNOWN
// 1: QAPI_NET_SSL_PROTOCOL_TLS_1_0
// 2: QAPI_NET_SSL_PROTOCOL_TLS_1_1
// 3: QAPI_NET_SSL_PROTOCOL_TLS_1_2
// 4: QAPI_NET_SSL_PROTOCOL_DTLS_1_0
// 5: QAPI_NET_SSL_PROTOCOL_DTLS_1_2
// NOTE: despite docs using caps, "sslversion" must be in lower case
sendAT(GF("+CSSLCFG=\"sslversion\",0,3")); // TLS 1.2
if (waitResponse(5000L) != 1) return false;
}
// enable or disable ssl
// AT+CASSLCFG=<cid>,"SSL",<sslFlag>
// <cid> Application connection ID (set with AT+CACID above)
// <sslFlag> 0: Not support SSL
// 1: Support SSL
sendAT(GF("+CASSLCFG="), mux, ',', GF("ssl,"), ssl);
waitResponse();
if (ssl) {
// set the PDP context to apply SSL to
// AT+CSSLCFG="CTXINDEX",<ctxindex>
// <ctxindex> PDP context identifier - for reasons not understood by me,
// use PDP context identifier of 0 for what we defined as 1 in
// the gprsConnect function
// NOTE: despite docs using "CRINDEX" in all caps, the module only
// accepts the command "ctxindex" and it must be in lower case
sendAT(GF("+CSSLCFG=\"ctxindex\",0"));
if (waitResponse(5000L, GF("+CSSLCFG:")) != 1) return false;
streamSkipUntil('\n'); // read out the certificate information
waitResponse();
if (certificates[mux] != "") {
// apply the correct certificate to the connection
// AT+CASSLCFG=<cid>,"CACERT",<caname>
// <cid> Application connection ID (set with AT+CACID above)
// <certname> certificate name
sendAT(GF("+CASSLCFG="), mux, ",CACERT,\"", certificates[mux].c_str(),
"\"");
if (waitResponse(5000L) != 1) return false;
}
// set the protocol
// 0: TCP; 1: UDP
sendAT(GF("+CASSLCFG="), mux, ',', GF("protocol,0"));
waitResponse();
// set the SSL SNI (server name indication)
// AT+CSSLCFG="SNI",<ctxindex>,<servername>
// <ctxindex> PDP context identifier - for reasons not understood by me,
// use PDP context identifier of 0 for what we defined as 1 in
// the gprsConnect function
// NOTE: despite docs using caps, "sni" must be in lower case
sendAT(GF("+CSSLCFG=\"sni\",0,"), GF("\""), host, GF("\""));
waitResponse();
}
// actually open the connection
// AT+CAOPEN=<cid>[,<conn_type>],<server>,<port>
// <cid> TCP/UDP identifier
// <conn_type> "TCP" or "UDP"
// NOTE: the "TCP" can't be included
sendAT(GF("+CAOPEN="), mux, GF(",\""), host, GF("\","), port);
if (waitResponse(timeout_ms, GF(AT_NL "+CAOPEN:")) != 1) { return 0; }
// returns OK/r/n/r/n+CAOPEN: <cid>,<result>
// <result> 0: Success
// 1: Socket error
// 2: No memory
// 3: Connection limit
// 4: Parameter invalid
// 6: Invalid IP address
// 7: Not support the function
// 12: Can’t bind the port
// 13: Can’t listen the port
// 20: Can’t resolve the host
// 21: Network not active
// 23: Remote refuse
// 24: Certificate’s time expired
// 25: Certificate’s common name does not match
// 26: Certificate’s common name does not match and time expired
// 27: Connect failed
streamSkipUntil(','); // Skip mux
// make sure the connection really opened
int8_t res = streamGetIntBefore('\n');
waitResponse();
return 0 == res;
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
// send data on prompt
sendAT(GF("+CASEND="), mux, ',', (uint16_t)len);
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
// after posting data, module responds with:
//+CASEND: <cid>,<result>,<sendlen>
if (waitResponse(GF(AT_NL "+CASEND:")) != 1) { return 0; }
streamSkipUntil(','); // Skip mux
if (streamGetIntBefore(',') != 0) { return 0; } // If result != success
return streamGetIntBefore('\n');
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) { return 0; }
sendAT(GF("+CARECV="), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CARECV:")) != 1) { return 0; }
// uint8_t ret_mux = stream.parseInt();
// streamSkipUntil(',');
// const int16_t len_confirmed = streamGetIntBefore('\n');
// DBG("### READING:", len_confirmed, "from", ret_mux);
// if (ret_mux != mux) {
// DBG("### Data from wrong mux! Got", ret_mux, "expected", mux);
// waitResponse();
// sockets[mux]->sock_available = modemGetAvailable(mux);
// return 0;
// }
// NOTE: manual says the mux number is returned before the number of
// characters available, but in tests only the number is returned
int16_t len_confirmed = stream.parseInt();
streamSkipUntil(','); // skip the comma
if (len_confirmed <= 0) {
waitResponse();
sockets[mux]->sock_available = modemGetAvailable(mux);
return 0;
}
for (int i = 0; i < len_confirmed; i++) {
uint32_t startMillis = millis();
while (!stream.available() &&
(millis() - startMillis < sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char c = stream.read();
sockets[mux]->rx.put(c);
}
waitResponse();
// DBG("### READ:", len_confirmed, "from", mux);
// make sure the sock available number is accurate again
// the module is **EXTREMELY** testy about being asked to read more from
// the buffer than exits; it will freeze until a hard reset or power cycle!
sockets[mux]->sock_available = modemGetAvailable(mux);
return len_confirmed;
}
size_t modemGetAvailable(uint8_t mux) {
// If the socket doesn't exist, just return
if (!sockets[mux]) { return 0; }
// We need to check if there are any connections open *before* checking for
// available characters. The SIM7000 *will crash* if you ask about data
// when there are no open connections.
if (!modemGetConnected(mux)) { return 0; }
// NOTE: This gets how many characters are available on all connections that
// have data. It does not return all the connections, just those with data.
sendAT(GF("+CARECV?"));
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
// after the last connection, there's an ok, so we catch it right away
int res = waitResponse(3000, GF("+CARECV:"), GFP(GSM_OK), GFP(GSM_ERROR));
// if we get the +CARECV: response, read the mux number and the number of
// characters available
if (res == 1) {
int ret_mux = streamGetIntBefore(',');
size_t result = streamGetIntBefore('\n');
GsmClientSim7000SSL* sock = sockets[ret_mux];
if (sock) { sock->sock_available = result; }
// if the first returned mux isn't 0 (or is higher than expected)
// we need to fill in the missing muxes
if (ret_mux > muxNo) {
for (int extra_mux = muxNo; extra_mux < ret_mux; extra_mux++) {
GsmClientSim7000SSL* isock = sockets[extra_mux];
if (isock) { isock->sock_available = 0; }
}
muxNo = ret_mux;
}
} else if (res == 2) {
// if we get an OK, we've reached the last socket with available data
// so we set any we haven't gotten to yet to 0
for (int extra_mux = muxNo; extra_mux < TINY_GSM_MUX_COUNT;
extra_mux++) {
GsmClientSim7000SSL* isock = sockets[extra_mux];
if (isock) { isock->sock_available = 0; }
}
break;
} else {
// if we got an error, give up
break;
}
// Should be a final OK at the end.
// If every connection was returned, catch the OK here.
// If only a portion were returned, catch it above.
if (muxNo == TINY_GSM_MUX_COUNT - 1) { waitResponse(); }
}
modemGetConnected(mux); // check the state of all connections
if (!sockets[mux]) { return 0; }
return sockets[mux]->sock_available;
}
bool modemGetConnected(uint8_t mux) {
// NOTE: This gets the state of all connections that have been opened
// since the last connection
sendAT(GF("+CASTATE?"));
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
// after the last connection, there's an ok, so we catch it right away
int res = waitResponse(3000, GF("+CASTATE:"), GFP(GSM_OK),
GFP(GSM_ERROR));
// if we get the +CASTATE: response, read the mux number and the status
if (res == 1) {
int ret_mux = streamGetIntBefore(',');
size_t status = streamGetIntBefore('\n');
// 0: Closed by remote server or internal error
// 1: Connected to remote server
// 2: Listening (server mode)
GsmClientSim7000SSL* sock = sockets[ret_mux];
if (sock) { sock->sock_connected = (status == 1); }
// if the first returned mux isn't 0 (or is higher than expected)
// we need to fill in the missing muxes
if (ret_mux > muxNo) {
for (int extra_mux = muxNo; extra_mux < ret_mux; extra_mux++) {
GsmClientSim7000SSL* isock = sockets[extra_mux];
if (isock) { isock->sock_connected = false; }
}
muxNo = ret_mux;
}
} else if (res == 2) {
// if we get an OK, we've reached the last socket with available data
// so we set any we haven't gotten to yet to 0
for (int extra_mux = muxNo; extra_mux < TINY_GSM_MUX_COUNT;
extra_mux++) {
GsmClientSim7000SSL* isock = sockets[extra_mux];
if (isock) { isock->sock_connected = false; }
}
break;
} else {
// if we got an error, give up
break;
}
// Should be a final OK at the end.
// If every connection was returned, catch the OK here.
// If only a portion were returned, catch it above.
if (muxNo == TINY_GSM_MUX_COUNT - 1) { waitResponse(); }
}
return sockets[mux]->sock_connected;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF("+CARECV:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
}
data = "";
DBG("### Got Data:", len, "on", mux);
return true;
} else if (data.endsWith(GF("+CADATAIND:"))) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
}
data = "";
DBG("### Got Data:", mux);
return true;
} else if (data.endsWith(GF("+CASTATE:"))) {
int8_t mux = streamGetIntBefore(',');
int8_t state = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
if (state != 1) {
sockets[mux]->sock_connected = false;
DBG("### Closed: ", mux);
}
}
data = "";
return true;
} else if (data.endsWith(GF("*PSNWID:"))) {
streamSkipUntil('\n'); // Refresh network name by network
data = "";
DBG("### Network name updated.");
return true;
} else if (data.endsWith(GF("*PSUTTZ:"))) {
streamSkipUntil('\n'); // Refresh time and time zone by network
data = "";
DBG("### Network time and time zone updated.");
return true;
} else if (data.endsWith(GF("+CTZV:"))) {
streamSkipUntil('\n'); // Refresh network time zone by network
data = "";
DBG("### Network time zone updated.");
return true;
} else if (data.endsWith(GF("DST: "))) {
streamSkipUntil('\n'); // Refresh Network Daylight Saving Time by network
data = "";
DBG("### Daylight savings time state updated.");
return true;
} else if (data.endsWith(GF(AT_NL "SMS Ready" AT_NL))) {
data = "";
DBG("### Unexpected module reset!");
init();
return true;
}
return false;
}
protected:
GsmClientSim7000SSL* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTSIM7000SSL_H_

View file

@ -0,0 +1,775 @@
/**
* @file TinyGsmClientSim7080.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTSIM7080_H_
#define SRC_TINYGSMCLIENTSIM7080_H_
// #define TINY_GSM_DEBUG Serial
// #define TINY_GSM_USE_HEX
#define TINY_GSM_MUX_COUNT 12
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#include "TinyGsmClientSIM70xx.h"
#include "TinyGsmTCP.tpp"
#include "TinyGsmSSL.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmGSMLocation.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmNTP.tpp"
#include "TinyGsmBattery.tpp"
class TinyGsmSim7080 : public TinyGsmSim70xx<TinyGsmSim7080>,
public TinyGsmTCP<TinyGsmSim7080, TINY_GSM_MUX_COUNT>,
public TinyGsmSSL<TinyGsmSim7080, TINY_GSM_MUX_COUNT>,
public TinyGsmSMS<TinyGsmSim7080>,
public TinyGsmGSMLocation<TinyGsmSim7080>,
public TinyGsmTime<TinyGsmSim7080>,
public TinyGsmNTP<TinyGsmSim7080>,
public TinyGsmBattery<TinyGsmSim7080> {
friend class TinyGsmSim70xx<TinyGsmSim7080>;
friend class TinyGsmModem<TinyGsmSim7080>;
friend class TinyGsmGPRS<TinyGsmSim7080>;
friend class TinyGsmTCP<TinyGsmSim7080, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSSL<TinyGsmSim7080, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSMS<TinyGsmSim7080>;
friend class TinyGsmGSMLocation<TinyGsmSim7080>;
friend class TinyGsmGPS<TinyGsmSim7080>;
friend class TinyGsmTime<TinyGsmSim7080>;
friend class TinyGsmNTP<TinyGsmSim7080>;
friend class TinyGsmBattery<TinyGsmSim7080>;
/*
* Inner Client
*/
public:
class GsmClientSim7080 : public GsmClient {
friend class TinyGsmSim7080;
public:
GsmClientSim7080() {}
explicit GsmClientSim7080(TinyGsmSim7080& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmSim7080* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+CACLOSE="), mux);
sock_connected = false;
at->waitResponse(3000);
}
void stop() override {
stop(15000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
public:
class GsmClientSecureSIM7080 : public GsmClientSim7080 {
public:
GsmClientSecureSIM7080() {}
explicit GsmClientSecureSIM7080(TinyGsmSim7080& modem, uint8_t mux = 0)
: GsmClientSim7080(modem, mux) {}
bool setCertificate(const String& certificateName) {
return at->setCertificate(certificateName, mux);
}
int connect(const char* host, uint16_t port, int timeout_s) override {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
};
/*
* Constructor
*/
public:
explicit TinyGsmSim7080(Stream& stream)
: TinyGsmSim70xx<TinyGsmSim7080>(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM7080"));
bool gotATOK = false;
for (uint32_t start = millis(); millis() - start < 10000L;) {
sendAT(GF(""));
int8_t resp = waitResponse(200L, GFP(GSM_OK), GFP(GSM_ERROR), GF("AT"));
if (resp == 1) {
gotATOK = true;
break;
} else if (resp == 3) {
waitResponse(200L); // get the OK
sendAT(GF("E0")); // Echo Off
DBG(GF("## Turning off echo!"));
waitResponse(2000L);
}
delay(100);
}
if (!gotATOK) { return false; }
#ifdef TINY_GSM_DEBUG
sendAT(GF("+CMEE=2")); // turn on verbose error codes
#else
sendAT(GF("+CMEE=0")); // turn off error codes
#endif
waitResponse();
DBG(GF("### Modem:"), getModemName());
// Enable Local Time Stamp for getting network time
sendAT(GF("+CLTS=1"));
if (waitResponse(10000L) != 1) { return false; }
// Enable battery checks
sendAT(GF("+CBATCHK=1"));
if (waitResponse() != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
void maintainImpl() {
// Keep listening for modem URC's and proactively iterate through
// sockets asking if any data is avaiable
bool check_socks = false;
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClientSim7080* sock = sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
check_socks = true;
}
}
// modemGetAvailable checks all socks, so we only want to do it once
// modemGetAvailable calls modemGetConnected(), which also checks allf
if (check_socks) { modemGetAvailable(0); }
while (stream.available()) { waitResponse(15, nullptr, nullptr); }
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
bool success = true;
bool gotATOK = false;
for (uint32_t start = millis(); millis() - start < 10000L;) {
sendAT(GF(""));
int8_t resp = waitResponse(200L, GFP(GSM_OK), GFP(GSM_ERROR), GF("AT"));
if (resp == 1) {
gotATOK = true;
break;
} else if (resp == 3) {
waitResponse(200L); // get the OK
DBG(GF("## Turning off echo!"));
sendAT(GF("E0")); // Echo Off
waitResponse(2000L);
}
delay(100);
}
if (!gotATOK) { return false; }
sendAT(GF("+CREBOOT")); // Reboot
success &= waitResponse() == 1;
waitResponse(30000L, GF("SMS Ready"));
success &= initImpl(pin);
return success;
}
/*
* Generic network functions
*/
protected:
String getLocalIPImpl() {
sendAT(GF("+CNACT?"));
if (waitResponse(GF(AT_NL "+CNACT:")) != 1) { return ""; }
streamSkipUntil('\"');
String res = stream.readStringUntil('\"');
waitResponse();
return res;
}
/*
* Secure socket layer (SSL) functions
*/
// Follows functions as inherited from TinyGsmSSL.tpp
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect();
// Define the PDP context
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
waitResponse();
// Attach to GPRS
sendAT(GF("+CGATT=1"));
if (waitResponse(60000L) != 1) { return false; }
// NOTE: **DO NOT** activate the PDP context
// For who only knows what reason, doing so screws up the rest of the
// process
// Check the APN returned by the server
// not sure why, but the connection is more consistent with this
sendAT(GF("+CGNAPN"));
waitResponse();
// Bearer settings for applications based on IP
// Set the user name and password
// AT+CNCFG=<pdpidx>,<ip_type>,[<APN>,[<usename>,<password>,[<authentication>]]]
// <pdpidx> PDP Context Identifier - for reasons not understood by me,
// use PDP context identifier of 0 for what we defined as 1 above
// <ip_type> 0: Dual PDN Stack
// 1: Internet Protocol Version 4
// 2: Internet Protocol Version 6
// <authentication> 0: NONE
// 1: PAP
// 2: CHAP
// 3: PAP or CHAP
if (pwd && strlen(pwd) > 0 && user && strlen(user) > 0) {
sendAT(GF("+CNCFG=0,1,\""), apn, "\",\"", user, "\",\"", pwd, "\",3");
waitResponse();
} else if (user && strlen(user) > 0) {
// Set the user name only
sendAT(GF("+CNCFG=0,1,\""), apn, "\",\"", user, '"');
waitResponse();
} else {
// Set the APN only
sendAT(GF("+CNCFG=0,1,\""), apn, '"');
waitResponse();
}
// Activate application network connection
// AT+CNACT=<pdpidx>,<action>
// <pdpidx> PDP Context Identifier - for reasons not understood by me,
// use PDP context identifier of 0 for what we defined as 1 above
// <action> 0: Deactive
// 1: Active
// 2: Auto Active
bool res = false;
int ntries = 0;
while (!res && ntries < 5) {
sendAT(GF("+CNACT=0,1"));
res = waitResponse(60000L, GF(AT_NL "+APP PDP: 0,ACTIVE"),
GF(AT_NL "+APP PDP: 0,DEACTIVE"));
waitResponse();
ntries++;
}
return res;
}
bool gprsDisconnectImpl() {
// Shut down the general application TCP/IP connection
// CNACT will close *all* open application connections
sendAT(GF("+CNACT=0,0"));
if (waitResponse(60000L) != 1) { return false; }
sendAT(GF("+CGATT=0")); // Deactivate the bearer context
if (waitResponse(60000L) != 1) { return false; }
return true;
}
/*
* SIM card functions
*/
// Follows functions as inherited from TinyGsmClientSIM70xx.h
/*
* Phone Call functions
*/
// No functions of this type supported
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
/*
* GSM Location functions
*/
// Follows all GSM-based location functions as inherited from
// TinyGsmGSMLocation.tpp
/*
* GPS/GNSS/GLONASS location functions
*/
// Follows functions as inherited from TinyGsmClientSIM70xx.h
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
protected:
byte NTPServerSyncImpl(String server = "pool.ntp.org", int TimeZone = 0) {
// Set GPRS bearer profile to associate with NTP sync
// this may fail, it's not supported by all modules
sendAT(GF("+CNTPCID=0")); // CID must be 0. With 1 (like other modules)
// does not work!
waitResponse(10000L);
// Set NTP server and timezone
sendAT(GF("+CNTP=\""), server, "\",", String(TimeZone));
if (waitResponse(10000L) != 1) { return -1; }
// Request network synchronization
sendAT(GF("+CNTP"));
if (waitResponse(10000L, GF("+CNTP:"))) {
String result = stream.readStringUntil('\n');
// Check for ',' in case the module appends the time next to the return
// code. Eg: +CNTP: <code>[,<time>]
int index = result.indexOf(',');
if (index > 0) { result.remove(index); }
result.trim();
if (TinyGsmIsValidNumber(result)) { return result.toInt(); }
} else {
return -1;
}
return -1;
}
String ShowNTPErrorImpl(byte error) {
switch (error) {
case 1: return "Network time synchronization is successful";
case 61: return "Network error";
case 62: return "DNS resolution error";
case 63: return "Connection error";
case 64: return "Service response error";
case 65: return "Service response timeout";
default: return "Unknown error: " + String(error);
}
}
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
// Follows all battery functions as inherited from TinyGsmBattery.tpp
/*
* Temperature functions
*/
// No functions of this type supported
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75) {
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
// set the connection (mux) identifier to use
sendAT(GF("+CACID="), mux);
if (waitResponse(timeout_ms) != 1) return false;
if (ssl) {
// set the ssl version
// AT+CSSLCFG="SSLVERSION",<ctxindex>,<sslversion>
// <ctxindex> PDP context identifier - for reasons not understood by me,
// use PDP context identifier of 0 for what we defined as 1 in
// the gprsConnect function
// <sslversion> 0: QAPI_NET_SSL_PROTOCOL_UNKNOWN
// 1: QAPI_NET_SSL_PROTOCOL_TLS_1_0
// 2: QAPI_NET_SSL_PROTOCOL_TLS_1_1
// 3: QAPI_NET_SSL_PROTOCOL_TLS_1_2
// 4: QAPI_NET_SSL_PROTOCOL_DTLS_1_0
// 5: QAPI_NET_SSL_PROTOCOL_DTLS_1_2
// NOTE: despite docs using caps, "sslversion" must be in lower case
sendAT(GF("+CSSLCFG=\"sslversion\",0,3")); // TLS 1.2
if (waitResponse(5000L) != 1) return false;
}
// enable or disable ssl
// AT+CASSLCFG=<cid>,"SSL",<sslFlag>
// <cid> Application connection ID (set with AT+CACID above)
// <sslFlag> 0: Not support SSL
// 1: Support SSL
sendAT(GF("+CASSLCFG="), mux, ',', GF("SSL,"), ssl);
waitResponse();
if (ssl) {
// set the PDP context to apply SSL to
// AT+CSSLCFG="CTXINDEX",<ctxindex>
// <ctxindex> PDP context identifier - for reasons not understood by me,
// use PDP context identifier of 0 for what we defined as 1 in
// the gprsConnect function
// NOTE: despite docs using "CRINDEX" in all caps, the module only
// accepts the command "ctxindex" and it must be in lower case
sendAT(GF("+CSSLCFG=\"ctxindex\",0"));
if (waitResponse(5000L, GF("+CSSLCFG:")) != 1) return false;
streamSkipUntil('\n'); // read out the certificate information
waitResponse();
if (certificates[mux] != "") {
// apply the correct certificate to the connection
// AT+CASSLCFG=<cid>,"CACERT",<caname>
// <cid> Application connection ID (set with AT+CACID above)
// <certname> certificate name
sendAT(GF("+CASSLCFG="), mux, ",CACERT,\"", certificates[mux].c_str(),
"\"");
if (waitResponse(5000L) != 1) return false;
}
// set the SSL SNI (server name indication)
// AT+CSSLCFG="SNI",<ctxindex>,<servername>
// <ctxindex> PDP context identifier - for reasons not understood by me,
// use PDP context identifier of 0 for what we defined as 1 in
// the gprsConnect function
// NOTE: despite docs using caps, "sni" must be in lower case
sendAT(GF("+CSSLCFG=\"sni\",0,"), GF("\""), host, GF("\""));
waitResponse();
}
// actually open the connection
// AT+CAOPEN=<cid>,<pdp_index>,<conn_type>,<server>,<port>[,<recv_mode>]
// <cid> TCP/UDP identifier
// <pdp_index> Index of PDP connection; we set up PCP context 1 above
// <conn_type> "TCP" or "UDP"
// <recv_mode> 0: The received data can only be read manually using
// AT+CARECV=<cid>
// 1: After receiving the data, it will automatically report
// URC:
// +CAURC:
// "recv",<id>,<length>,<remoteIP>,<remote_port><CR><LF><data>
// NOTE: including the <recv_mode> fails
sendAT(GF("+CAOPEN="), mux, GF(",0,\"TCP\",\""), host, GF("\","), port);
if (waitResponse(timeout_ms, GF(AT_NL "+CAOPEN:")) != 1) { return 0; }
// returns OK/r/n/r/n+CAOPEN: <cid>,<result>
// <result> 0: Success
// 1: Socket error
// 2: No memory
// 3: Connection limit
// 4: Parameter invalid
// 6: Invalid IP address
// 7: Not support the function
// 12: Can’t bind the port
// 13: Can’t listen the port
// 20: Can’t resolve the host
// 21: Network not active
// 23: Remote refuse
// 24: Certificate’s time expired
// 25: Certificate’s common name does not match
// 26: Certificate’s common name does not match and time expired
// 27: Connect failed
streamSkipUntil(','); // Skip mux
// make sure the connection really opened
int8_t res = streamGetIntBefore('\n');
waitResponse();
return 0 == res;
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
// send data on prompt
sendAT(GF("+CASEND="), mux, ',', (uint16_t)len);
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
// OK after posting data
if (waitResponse() != 1) { return 0; }
return len;
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) { return 0; }
sendAT(GF("+CARECV="), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CARECV:")) != 1) { return 0; }
// uint8_t ret_mux = stream.parseInt();
// streamSkipUntil(',');
// const int16_t len_confirmed = streamGetIntBefore('\n');
// DBG("### READING:", len_confirmed, "from", ret_mux);
// if (ret_mux != mux) {
// DBG("### Data from wrong mux! Got", ret_mux, "expected", mux);
// waitResponse();
// sockets[mux]->sock_available = modemGetAvailable(mux);
// return 0;
// }
// NOTE: manual says the mux number is returned before the number of
// characters available, but in tests only the number is returned
int16_t len_confirmed = stream.parseInt();
streamSkipUntil(','); // skip the comma
if (len_confirmed <= 0) {
waitResponse();
sockets[mux]->sock_available = modemGetAvailable(mux);
return 0;
}
for (int i = 0; i < len_confirmed; i++) {
uint32_t startMillis = millis();
while (!stream.available() &&
(millis() - startMillis < sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char c = stream.read();
sockets[mux]->rx.put(c);
}
waitResponse();
// make sure the sock available number is accurate again
sockets[mux]->sock_available = modemGetAvailable(mux);
return len_confirmed;
}
size_t modemGetAvailable(uint8_t mux) {
// If the socket doesn't exist, just return
if (!sockets[mux]) { return 0; }
// NOTE: This gets how many characters are available on all connections that
// have data. It does not return all the connections, just those with data.
sendAT(GF("+CARECV?"));
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
// after the last connection, there's an ok, so we catch it right away
int res = waitResponse(3000, GF("+CARECV:"), GFP(GSM_OK), GFP(GSM_ERROR));
// if we get the +CARECV: response, read the mux number and the number of
// characters available
if (res == 1) {
int ret_mux = streamGetIntBefore(',');
size_t result = streamGetIntBefore('\n');
GsmClientSim7080* sock = sockets[ret_mux];
if (sock) { sock->sock_available = result; }
// if the first returned mux isn't 0 (or is higher than expected)
// we need to fill in the missing muxes
if (ret_mux > muxNo) {
for (int extra_mux = muxNo; extra_mux < ret_mux; extra_mux++) {
GsmClientSim7080* isock = sockets[extra_mux];
if (isock) { isock->sock_available = 0; }
}
muxNo = ret_mux;
}
} else if (res == 2) {
// if we get an OK, we've reached the last socket with available data
// so we set any we haven't gotten to yet to 0
for (int extra_mux = muxNo; extra_mux < TINY_GSM_MUX_COUNT;
extra_mux++) {
GsmClientSim7080* isock = sockets[extra_mux];
if (isock) { isock->sock_available = 0; }
}
break;
} else {
// if we got an error, give up
break;
}
// Should be a final OK at the end.
// If every connection was returned, catch the OK here.
// If only a portion were returned, catch it above.
if (muxNo == TINY_GSM_MUX_COUNT - 1) { waitResponse(); }
}
modemGetConnected(mux); // check the state of all connections
if (!sockets[mux]) { return 0; }
return sockets[mux]->sock_available;
}
bool modemGetConnected(uint8_t mux) {
// NOTE: This gets the state of all connections that have been opened
// since the last connection
sendAT(GF("+CASTATE?"));
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
// after the last connection, there's an ok, so we catch it right away
int res = waitResponse(3000, GF("+CASTATE:"), GFP(GSM_OK),
GFP(GSM_ERROR));
// if we get the +CASTATE: response, read the mux number and the status
if (res == 1) {
int ret_mux = streamGetIntBefore(',');
size_t status = streamGetIntBefore('\n');
// 0: Closed by remote server or internal error
// 1: Connected to remote server
// 2: Listening (server mode)
GsmClientSim7080* sock = sockets[ret_mux];
if (sock) { sock->sock_connected = (status == 1); }
// if the first returned mux isn't 0 (or is higher than expected)
// we need to fill in the missing muxes
if (ret_mux > muxNo) {
for (int extra_mux = muxNo; extra_mux < ret_mux; extra_mux++) {
GsmClientSim7080* isock = sockets[extra_mux];
if (isock) { isock->sock_connected = false; }
}
muxNo = ret_mux;
}
} else if (res == 2) {
// if we get an OK, we've reached the last socket with available data
// so we set any we haven't gotten to yet to 0
for (int extra_mux = muxNo; extra_mux < TINY_GSM_MUX_COUNT;
extra_mux++) {
GsmClientSim7080* isock = sockets[extra_mux];
if (isock) { isock->sock_connected = false; }
}
break;
} else {
// if we got an error, give up
break;
}
// Should be a final OK at the end.
// If every connection was returned, catch the OK here.
// If only a portion were returned, catch it above.
if (muxNo == TINY_GSM_MUX_COUNT - 1) { waitResponse(); }
}
return sockets[mux]->sock_connected;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF("+CARECV:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
}
data = "";
DBG("### Got Data:", len, "on", mux);
return true;
} else if (data.endsWith(GF("+CADATAIND:"))) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
}
data = "";
DBG("### Got Data:", mux);
return true;
} else if (data.endsWith(GF("+CASTATE:"))) {
int8_t mux = streamGetIntBefore(',');
int8_t state = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
if (state != 1) {
sockets[mux]->sock_connected = false;
DBG("### Closed: ", mux);
}
}
data = "";
return true;
} else if (data.endsWith(GF("*PSNWID:"))) {
streamSkipUntil('\n'); // Refresh network name by network
data = "";
DBG("### Network name updated.");
return true;
} else if (data.endsWith(GF("*PSUTTZ:"))) {
streamSkipUntil('\n'); // Refresh time and time zone by network
data = "";
DBG("### Network time and time zone updated.");
return true;
} else if (data.endsWith(GF("+CTZV:"))) {
streamSkipUntil('\n'); // Refresh network time zone by network
data = "";
DBG("### Network time zone updated.");
return true;
} else if (data.endsWith(GF("DST: "))) {
streamSkipUntil('\n'); // Refresh Network Daylight Saving Time by network
data = "";
DBG("### Daylight savings time state updated.");
return true;
} else if (data.endsWith(GF(AT_NL "SMS Ready" AT_NL))) {
data = "";
DBG("### Unexpected module reset!");
init();
data = "";
return true;
}
return false;
}
protected:
GsmClientSim7080* sockets[TINY_GSM_MUX_COUNT];
String certificates[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTSIM7080_H_

View file

@ -0,0 +1,350 @@
/**
* @file TinyGsmClientSIM70xx.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTSIM70XX_H_
#define SRC_TINYGSMCLIENTSIM70XX_H_
// #define TINY_GSM_DEBUG Serial
// #define TINY_GSM_USE_HEX
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "SIMCom"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#if defined(TINY_GSM_MODEM_SIM7070)
#define MODEM_MODEL "SIM7070";
#elif defined(TINY_GSM_MODEM_SIM7080)
#define MODEM_MODEL "SIM7080";
#elif defined(TINY_GSM_MODEM_SIM7090)
#define MODEM_MODEL "SIM7090";
#elif defined(TINY_GSM_MODEM_SIM7000) || defined(TINY_GSM_MODEM_SIM7000SSL)
#define MODEM_MODEL "SIM7000";
#else
#define MODEM_MODEL "SIM70xx";
#endif
#include "TinyGsmModem.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmGPS.tpp"
enum SIM70xxRegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
template <class SIM70xxType>
class TinyGsmSim70xx : public TinyGsmModem<SIM70xxType>,
public TinyGsmGPRS<SIM70xxType>,
public TinyGsmGPS<SIM70xxType> {
friend class TinyGsmModem<SIM70xxType>;
friend class TinyGsmGPRS<SIM70xxType>;
friend class TinyGsmGPS<SIM70xxType>;
/*
* CRTP Helper
*/
protected:
inline const SIM70xxType& thisModem() const {
return static_cast<const SIM70xxType&>(*this);
}
inline SIM70xxType& thisModem() {
return static_cast<SIM70xxType&>(*this);
}
~TinyGsmSim70xx() {}
/*
* Constructor
*/
public:
explicit TinyGsmSim70xx(Stream& stream) : stream(stream) {}
/*
* Basic functions
*/
protected:
bool factoryDefaultImpl() {
return false;
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
thisModem().sendAT(GF("E0")); // Echo Off
thisModem().waitResponse();
if (!thisModem().setPhoneFunctionality(0)) { return false; }
if (!thisModem().setPhoneFunctionality(1, true)) { return false; }
thisModem().waitResponse(30000L, GF("SMS Ready"));
return thisModem().initImpl(pin);
}
bool powerOffImpl() {
thisModem().sendAT(GF("+CPOWD=1"));
return thisModem().waitResponse(GF("NORMAL POWER DOWN")) == 1;
}
// During sleep, the SIM70xx module has its serial communication disabled.
// In order to reestablish communication pull the DRT-pin of the SIM70xx
// module LOW for at least 50ms. Then use this function to disable sleep
// mode. The DTR-pin can then be released again.
bool sleepEnableImpl(bool enable = true) {
thisModem().sendAT(GF("+CSCLK="), enable);
return thisModem().waitResponse() == 1;
}
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
thisModem().sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
return thisModem().waitResponse(10000L) == 1;
}
/*
* Generic network functions
*/
public:
SIM70xxRegStatus getRegistrationStatus() {
SIM70xxRegStatus epsStatus =
(SIM70xxRegStatus)thisModem().getRegistrationStatusXREG("CEREG");
// If we're connected on EPS, great!
if (epsStatus == REG_OK_HOME || epsStatus == REG_OK_ROAMING) {
return epsStatus;
} else {
// Otherwise, check GPRS network status
// We could be using GPRS fall-back or the board could be being moody
return (SIM70xxRegStatus)thisModem().getRegistrationStatusXREG("CGREG");
}
}
protected:
bool isNetworkConnectedImpl() {
SIM70xxRegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
public:
String getNetworkModes() {
// Get the help string, not the setting value
thisModem().sendAT(GF("+CNMP=?"));
if (thisModem().waitResponse(GF(AT_NL "+CNMP:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
thisModem().waitResponse();
return res;
}
int16_t getNetworkMode() {
thisModem().sendAT(GF("+CNMP?"));
if (thisModem().waitResponse(GF(AT_NL "+CNMP:")) != 1) { return false; }
int16_t mode = thisModem().streamGetIntBefore('\n');
thisModem().waitResponse();
return mode;
}
bool setNetworkMode(uint8_t mode) {
// 2 Automatic
// 13 GSM only
// 38 LTE only
// 51 GSM and LTE only
thisModem().sendAT(GF("+CNMP="), mode);
return thisModem().waitResponse() == 1;
}
String getPreferredModes() {
// Get the help string, not the setting value
thisModem().sendAT(GF("+CMNB=?"));
if (thisModem().waitResponse(GF(AT_NL "+CMNB:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
thisModem().waitResponse();
return res;
}
int16_t getPreferredMode() {
thisModem().sendAT(GF("+CMNB?"));
if (thisModem().waitResponse(GF(AT_NL "+CMNB:")) != 1) { return false; }
int16_t mode = thisModem().streamGetIntBefore('\n');
thisModem().waitResponse();
return mode;
}
bool setPreferredMode(uint8_t mode) {
// 1 CAT-M
// 2 NB-IoT
// 3 CAT-M and NB-IoT
thisModem().sendAT(GF("+CMNB="), mode);
return thisModem().waitResponse() == 1;
}
bool getNetworkSystemMode(bool& n, int16_t& stat) {
// n: whether to automatically report the system mode info
// stat: the current service. 0 if it not connected
thisModem().sendAT(GF("+CNSMOD?"));
if (thisModem().waitResponse(GF(AT_NL "+CNSMOD:")) != 1) { return false; }
n = thisModem().streamGetIntBefore(',') != 0;
stat = thisModem().streamGetIntBefore('\n');
thisModem().waitResponse();
return true;
}
bool setNetworkSystemMode(bool n) {
// n: whether to automatically report the system mode info
thisModem().sendAT(GF("+CNSMOD="), int8_t(n));
return thisModem().waitResponse() == 1;
}
/*
* GPRS functions
*/
protected:
// should implement in sub-classes
/*
* SIM card functions
*/
protected:
// Doesn't return the "+CCID" before the number
String getSimCCIDImpl() {
thisModem().sendAT(GF("+CCID"));
if (thisModem().waitResponse(GF(AT_NL)) != 1) { return ""; }
String res = stream.readStringUntil('\n');
thisModem().waitResponse();
res.trim();
return res;
}
/*
* GPS/GNSS/GLONASS location functions
*/
protected:
// enable GPS
bool enableGPSImpl() {
thisModem().sendAT(GF("+CGNSPWR=1"));
if (thisModem().waitResponse() != 1) { return false; }
return true;
}
bool disableGPSImpl() {
thisModem().sendAT(GF("+CGNSPWR=0"));
if (thisModem().waitResponse() != 1) { return false; }
return true;
}
// get the RAW GPS output
String getGPSrawImpl() {
thisModem().sendAT(GF("+CGNSINF"));
if (thisModem().waitResponse(10000L, GF(AT_NL "+CGNSINF:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
thisModem().waitResponse();
res.trim();
return res;
}
// get GPS informations
bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0,
int* vsat = 0, int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
int* minute = 0, int* second = 0) {
thisModem().sendAT(GF("+CGNSINF"));
if (thisModem().waitResponse(10000L, GF(AT_NL "+CGNSINF:")) != 1) {
return false;
}
thisModem().streamSkipUntil(','); // GNSS run status
if (thisModem().streamGetIntBefore(',') == 1) { // fix status
// init variables
float ilat = 0;
float ilon = 0;
float ispeed = 0;
float ialt = 0;
int ivsat = 0;
int iusat = 0;
float iaccuracy = 0;
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
float secondWithSS = 0;
// UTC date & Time
iyear = thisModem().streamGetIntLength(4); // Four digit year
imonth = thisModem().streamGetIntLength(2); // Two digit month
iday = thisModem().streamGetIntLength(2); // Two digit day
ihour = thisModem().streamGetIntLength(2); // Two digit hour
imin = thisModem().streamGetIntLength(2); // Two digit minute
secondWithSS = thisModem().streamGetFloatBefore(
','); // 6 digit second with subseconds
ilat = thisModem().streamGetFloatBefore(','); // Latitude
ilon = thisModem().streamGetFloatBefore(','); // Longitude
ialt = thisModem().streamGetFloatBefore(
','); // MSL Altitude. Unit is meters
ispeed = thisModem().streamGetFloatBefore(
','); // Speed Over Ground. Unit is knots.
thisModem().streamSkipUntil(','); // Course Over Ground. Degrees.
thisModem().streamSkipUntil(','); // Fix Mode
thisModem().streamSkipUntil(','); // Reserved1
iaccuracy = thisModem().streamGetFloatBefore(
','); // Horizontal Dilution Of Precision
thisModem().streamSkipUntil(','); // Position Dilution Of Precision
thisModem().streamSkipUntil(','); // Vertical Dilution Of Precision
thisModem().streamSkipUntil(','); // Reserved2
ivsat = thisModem().streamGetIntBefore(','); // GNSS Satellites in View
iusat = thisModem().streamGetIntBefore(','); // GNSS Satellites Used
thisModem().streamSkipUntil(','); // GLONASS Satellites Used
thisModem().streamSkipUntil(','); // Reserved3
thisModem().streamSkipUntil(','); // C/N0 max
thisModem().streamSkipUntil(','); // HPA
thisModem().streamSkipUntil('\n'); // VPA
// Set pointers
if (lat != nullptr) *lat = ilat;
if (lon != nullptr) *lon = ilon;
if (speed != nullptr) *speed = ispeed;
if (alt != nullptr) *alt = ialt;
if (vsat != nullptr) *vsat = ivsat;
if (usat != nullptr) *usat = iusat;
if (accuracy != nullptr) *accuracy = iaccuracy;
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = static_cast<int>(secondWithSS);
thisModem().waitResponse();
return true;
}
thisModem().streamSkipUntil('\n'); // toss the row of commas
thisModem().waitResponse();
return false;
}
bool handleURCs(String& data) {
return thisModem().handleURCs(data);
}
public:
Stream& stream;
};
#endif // SRC_TINYGSMCLIENTSIM70XX_H_

View file

@ -0,0 +1,800 @@
/**
* @file TinyGsmClientSIM7600.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTSIM7600_H_
#define SRC_TINYGSMCLIENTSIM7600_H_
// #define TINY_GSM_DEBUG Serial
// #define TINY_GSM_USE_HEX
#define TINY_GSM_MUX_COUNT 10
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "SIMCom"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#if defined(TINY_GSM_MODEM_SIM7500)
#define MODEM_MODEL "SIM7500";
#elif defined(TINY_GSM_MODEM_SIM7800)
#define MODEM_MODEL "SIM7800";
#else
#define MODEM_MODEL "SIM7600";
#endif
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmCalling.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmGSMLocation.tpp"
#include "TinyGsmGPS.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmNTP.tpp"
#include "TinyGsmBattery.tpp"
#include "TinyGsmTemperature.tpp"
enum SIM7600RegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmSim7600 : public TinyGsmModem<TinyGsmSim7600>,
public TinyGsmGPRS<TinyGsmSim7600>,
public TinyGsmTCP<TinyGsmSim7600, TINY_GSM_MUX_COUNT>,
public TinyGsmSMS<TinyGsmSim7600>,
public TinyGsmGSMLocation<TinyGsmSim7600>,
public TinyGsmGPS<TinyGsmSim7600>,
public TinyGsmTime<TinyGsmSim7600>,
public TinyGsmNTP<TinyGsmSim7600>,
public TinyGsmBattery<TinyGsmSim7600>,
public TinyGsmTemperature<TinyGsmSim7600>,
public TinyGsmCalling<TinyGsmSim7600> {
friend class TinyGsmModem<TinyGsmSim7600>;
friend class TinyGsmGPRS<TinyGsmSim7600>;
friend class TinyGsmTCP<TinyGsmSim7600, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSMS<TinyGsmSim7600>;
friend class TinyGsmGPS<TinyGsmSim7600>;
friend class TinyGsmGSMLocation<TinyGsmSim7600>;
friend class TinyGsmTime<TinyGsmSim7600>;
friend class TinyGsmNTP<TinyGsmSim7600>;
friend class TinyGsmBattery<TinyGsmSim7600>;
friend class TinyGsmTemperature<TinyGsmSim7600>;
friend class TinyGsmCalling<TinyGsmSim7600>;
/*
* Inner Client
*/
public:
class GsmClientSim7600 : public GsmClient {
friend class TinyGsmSim7600;
public:
GsmClientSim7600() {}
explicit GsmClientSim7600(TinyGsmSim7600& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmSim7600* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+CIPCLOSE="), mux);
sock_connected = false;
at->waitResponse();
}
void stop() override {
stop(15000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
// NOT SUPPORTED
/*
* Constructor
*/
public:
explicit TinyGsmSim7600(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM7600"));
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();
DBG(GF("### Modem:"), getModemName());
// Disable time and time zone URC's
sendAT(GF("+CTZR=0"));
if (waitResponse(10000L) != 1) { return false; }
// Enable automatic time zome update
sendAT(GF("+CTZU=1"));
if (waitResponse(10000L) != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
bool factoryDefaultImpl() { // these commands aren't supported
return false;
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
sendAT(GF("+CRESET"));
if (waitResponse(10000L) != 1) { return false; }
delay(24000L);
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+CPOF"));
return waitResponse() == 1;
}
bool radioOffImpl() {
if (!setPhoneFunctionality(4)) { return false; }
delay(3000);
return true;
}
bool sleepEnableImpl(bool enable = true) {
sendAT(GF("+CSCLK="), enable);
return waitResponse() == 1;
}
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
return waitResponse(10000L) == 1;
}
/*
* Generic network functions
*/
public:
SIM7600RegStatus getRegistrationStatus() {
return (SIM7600RegStatus)getRegistrationStatusXREG("CGREG");
}
protected:
bool isNetworkConnectedImpl() {
SIM7600RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
public:
String getNetworkModes() {
// Get the help string, not the setting value
sendAT(GF("+CNMP=?"));
if (waitResponse(GF(AT_NL "+CNMP:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
return res;
}
int16_t getNetworkMode() {
sendAT(GF("+CNMP?"));
if (waitResponse(GF(AT_NL "+CNMP:")) != 1) { return false; }
int16_t mode = streamGetIntBefore('\n');
waitResponse();
return mode;
}
bool setNetworkMode(uint8_t mode) {
sendAT(GF("+CNMP="), mode);
return waitResponse() == 1;
}
bool getNetworkSystemMode(bool& n, int16_t& stat) {
// n: whether to automatically report the system mode info
// stat: the current service. 0 if it not connected
sendAT(GF("+CNSMOD?"));
if (waitResponse(GF(AT_NL "+CNSMOD:")) != 1) { return false; }
n = streamGetIntBefore(',') != 0;
stat = streamGetIntBefore('\n');
waitResponse();
return true;
}
String getLocalIPImpl() {
sendAT(GF("+IPADDR")); // Inquire Socket PDP address
// sendAT(GF("+CGPADDR=1")); // Show PDP address
String res;
if (waitResponse(10000L, res) != 1) { return ""; }
cleanResponseString(res);
res.trim();
return res;
}
/*
* Secure socket layer (SSL) functions
*/
// No functions of this type supported
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect(); // Make sure we're not connected first
// Define the PDP context
// The CGDCONT commands set up the "external" PDP context
// Set the external authentication
if (user && strlen(user) > 0) {
sendAT(GF("+CGAUTH=1,0,\""), pwd, GF("\",\""), user, '"');
waitResponse();
}
// Define external PDP context 1
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"', ",\"0.0.0.0\",0,0");
waitResponse();
// Configure TCP parameters
// Select TCP/IP application mode (command mode)
sendAT(GF("+CIPMODE=0"));
waitResponse();
// Set Sending Mode - send without waiting for peer TCP ACK
sendAT(GF("+CIPSENDMODE=0"));
waitResponse();
// Configure socket parameters
// AT+CIPCCFG= <NmRetry>, <DelayTm>, <Ack>, <errMode>, <HeaderType>,
// <AsyncMode>, <TimeoutVal>
// NmRetry = number of retransmission to be made for an IP packet
// = 10 (default)
// DelayTm = number of milliseconds to delay before outputting received data
// = 0 (default)
// Ack = sets whether reporting a string "Send ok" = 0 (don't report)
// errMode = mode of reporting error result code = 0 (numberic values)
// HeaderType = which data header of receiving data in multi-client mode
// = 1 (+RECEIVE,<link num>,<data length>)
// AsyncMode = sets mode of executing commands
// = 0 (synchronous command executing)
// TimeoutVal = minimum retransmission timeout in milliseconds = 75000
sendAT(GF("+CIPCCFG=10,0,0,0,1,0,75000"));
if (waitResponse() != 1) { return false; }
// Configure timeouts for opening and closing sockets
// AT+CIPTIMEOUT=<netopen_timeout> <cipopen_timeout>, <cipsend_timeout>
sendAT(GF("+CIPTIMEOUT="), 75000, ',', 15000, ',', 15000);
waitResponse();
// Start the socket service
// This activates and attaches to the external PDP context that is tied
// to the embedded context for TCP/IP (ie AT+CGACT=1,1 and AT+CGATT=1)
// Response may be an immediate "OK" followed later by "+NETOPEN: 0".
// We to ignore any immediate response and wait for the
// URC to show it's really connected.
sendAT(GF("+NETOPEN"));
if (waitResponse(75000L, GF(AT_NL "+NETOPEN: 0")) != 1) { return false; }
return true;
}
bool gprsDisconnectImpl() {
// Close all sockets and stop the socket service
// Note: On the LTE models, this single command closes all sockets and the
// service
sendAT(GF("+NETCLOSE"));
if (waitResponse(60000L, GF(AT_NL "+NETCLOSE: 0")) != 1) { return false; }
return true;
}
bool isGprsConnectedImpl() {
sendAT(GF("+NETOPEN?"));
// May return +NETOPEN: 1, 0. We just confirm that the first number is 1
if (waitResponse(GF(AT_NL "+NETOPEN: 1")) != 1) { return false; }
waitResponse();
sendAT(GF("+IPADDR")); // Inquire Socket PDP address
// sendAT(GF("+CGPADDR=1")); // Show PDP address
if (waitResponse() != 1) { return false; }
return true;
}
String getProviderImpl() {
sendAT(GF("+CSPN?"));
if (waitResponse(GF("+CSPN:")) != 1) { return ""; }
streamSkipUntil('"'); /* Skip mode and format */
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
/*
* SIM card functions
*/
protected:
// Gets the CCID of a sim card via AT+CCID
String getSimCCIDImpl() {
sendAT(GF("+CICCID"));
if (waitResponse(GF(AT_NL "+ICCID:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Phone Call functions
*/
protected:
bool callHangupImpl() {
sendAT(GF("+CHUP"));
return waitResponse() == 1;
}
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
/*
* GSM Location functions
*/
// Follows all GSM-based location functions as inherited from
// TinyGsmGSMLocation.tpp
/*
* GPS/GNSS/GLONASS location functions
*/
protected:
// enable GPS
bool enableGPSImpl() {
sendAT(GF("+CGPS=1"));
if (waitResponse() != 1) { return false; }
return true;
}
bool disableGPSImpl() {
sendAT(GF("+CGPS=0"));
if (waitResponse() != 1) { return false; }
return true;
}
// get the RAW GPS output
String getGPSrawImpl() {
sendAT(GF("+CGNSSINFO"));
if (waitResponse(GF(AT_NL "+CGNSSINFO:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
// get GPS informations
bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0,
int* vsat = 0, int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
int* minute = 0, int* second = 0) {
sendAT(GF("+CGNSSINFO"));
if (waitResponse(GF(AT_NL "+CGNSSINFO:")) != 1) { return false; }
uint8_t fixMode = streamGetIntBefore(','); // mode 2=2D Fix or 3=3DFix
// TODO(?) Can 1 be returned
if (fixMode == 1 || fixMode == 2 || fixMode == 3) {
// init variables
float ilat = 0;
char north;
float ilon = 0;
char east;
float ispeed = 0;
float ialt = 0;
int ivsat = 0;
int iusat = 0;
float iaccuracy = 0;
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
float secondWithSS = 0;
streamSkipUntil(','); // GPS satellite valid numbers
streamSkipUntil(','); // GLONASS satellite valid numbers
streamSkipUntil(','); // BEIDOU satellite valid numbers
ilat = streamGetFloatBefore(','); // Latitude in ddmm.mmmmmm
north = stream.readStringUntil(',').charAt(
0); // N/S Indicator, N=north or S=south
ilon = streamGetFloatBefore(','); // Longitude in ddmm.mmmmmm
east = stream.readStringUntil(',').charAt(
0); // E/W Indicator, E=east or W=west
// Date. Output format is ddmmyy
iday = streamGetIntLength(2); // Two digit day
imonth = streamGetIntLength(2); // Two digit month
iyear = streamGetIntBefore(','); // Two digit year
// UTC Time. Output format is hhmmss.s
ihour = streamGetIntLength(2); // Two digit hour
imin = streamGetIntLength(2); // Two digit minute
secondWithSS =
streamGetFloatBefore(','); // 4 digit second with subseconds
ialt = streamGetFloatBefore(','); // MSL Altitude. Unit is meters
ispeed = streamGetFloatBefore(','); // Speed Over Ground. Unit is knots.
streamSkipUntil(','); // Course Over Ground. Degrees.
iaccuracy = streamGetFloatBefore(','); // Position Dilution Of Precision
streamSkipUntil(','); // Horizontal Dilution Of Precision
streamSkipUntil('\n'); // Vertical Dilution Of Precision
// Set pointers
if (lat != nullptr)
*lat = (floor(ilat / 100) + fmod(ilat, 100.) / 60) *
(north == 'N' ? 1 : -1);
if (lon != nullptr)
*lon = (floor(ilon / 100) + fmod(ilon, 100.) / 60) *
(east == 'E' ? 1 : -1);
if (speed != nullptr) *speed = ispeed;
if (alt != nullptr) *alt = ialt;
if (vsat != nullptr) *vsat = ivsat;
if (usat != nullptr) *usat = iusat;
if (accuracy != nullptr) *accuracy = iaccuracy;
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = static_cast<int>(secondWithSS);
waitResponse();
return true;
}
waitResponse();
return false;
}
/**
* CGNSSMODE: <gnss_mode>,<dpo_mode>
* This command is used to configure GPS, GLONASS, BEIDOU and QZSS support
* mode. 0 : GLONASS 1 : BEIDOU 2 : GALILEO 3 : QZSS dpo_mode: 1 enable , 0
* disable
*/
String setGNSSModeImpl(uint8_t mode, bool dpo) {
String res;
sendAT(GF("+CGNSSMODE="), mode, ",", dpo);
if (waitResponse(10000L, res) != 1) { return ""; }
res.replace(AT_NL, "");
res.trim();
return res;
}
uint8_t getGNSSModeImpl() {
sendAT(GF("+CGNSSMODE?"));
if (waitResponse(GF(AT_NL "+CGNSSMODE:")) != 1) { return 0; }
return stream.readStringUntil(',').toInt();
}
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
// Follows all NTP server functions as inherited from TinyGsmNTP.tpp
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
protected:
// returns volts, multiply by 1000 to get mV
int16_t getBattVoltageImpl() {
sendAT(GF("+CBC"));
if (waitResponse(GF(AT_NL "+CBC:")) != 1) { return 0; }
// get voltage in VOLTS
float voltage = streamGetFloatBefore('\n');
// Wait for final OK
waitResponse();
// Return millivolts
uint16_t res = voltage * 1000;
return res;
}
int8_t getBattPercentImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
int8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
bool getBattStatsImpl(int8_t& chargeState, int8_t& percent,
int16_t& milliVolts) {
chargeState = 0;
percent = 0;
milliVolts = getBattVoltage();
return true;
}
/*
* Temperature functions
*/
protected:
// get temperature in degree celsius
uint16_t getTemperatureImpl() {
sendAT(GF("+CPMUTEMP"));
if (waitResponse(GF(AT_NL "+CPMUTEMP:")) != 1) { return 0; }
// return temperature in C
uint16_t res = streamGetIntBefore('\n');
// Wait for final OK
waitResponse();
return res;
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 15) {
if (ssl) { DBG("SSL not yet supported on this module!"); }
// Make sure we'll be getting data manually on this connection
sendAT(GF("+CIPRXGET=1"));
if (waitResponse() != 1) { return false; }
// Establish a connection in multi-socket mode
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
sendAT(GF("+CIPOPEN="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","),
port);
// The reply is OK followed by +CIPOPEN: <link_num>,<err> where <link_num>
// is the mux number and <err> should be 0 if there's no error
if (waitResponse(timeout_ms, GF(AT_NL "+CIPOPEN:")) != 1) { return false; }
uint8_t opened_mux = streamGetIntBefore(',');
uint8_t opened_result = streamGetIntBefore('\n');
if (opened_mux != mux || opened_result != 0) return false;
return true;
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(GF(AT_NL "+CIPSEND:")) != 1) { return 0; }
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip requested bytes to send
// TODO(?): make sure requested and confirmed bytes match
return streamGetIntBefore('\n');
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) return 0;
#ifdef TINY_GSM_USE_HEX
sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
#else
sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
#endif
streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX
streamSkipUntil(','); // Skip mux/cid (connecion id)
int16_t len_requested = streamGetIntBefore(',');
// ^^ Requested number of data bytes (1-1460 bytes)to be read
int16_t len_confirmed = streamGetIntBefore('\n');
// ^^ The data length which not read in the buffer
for (int i = 0; i < len_requested; i++) {
uint32_t startMillis = millis();
#ifdef TINY_GSM_USE_HEX
while (stream.available() < 2 &&
(millis() - startMillis < sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char buf[4] = {
0,
};
buf[0] = stream.read();
buf[1] = stream.read();
char c = strtol(buf, nullptr, 16);
#else
while (!stream.available() &&
(millis() - startMillis < sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char c = stream.read();
#endif
sockets[mux]->rx.put(c);
}
// DBG("### READ:", len_requested, "from", mux);
// sockets[mux]->sock_available = modemGetAvailable(mux);
sockets[mux]->sock_available = len_confirmed;
waitResponse();
return len_requested;
}
size_t modemGetAvailable(uint8_t mux) {
if (!sockets[mux]) return 0;
sendAT(GF("+CIPRXGET=4,"), mux);
size_t result = 0;
if (waitResponse(GF("+CIPRXGET:")) == 1) {
streamSkipUntil(','); // Skip mode 4
streamSkipUntil(','); // Skip mux
result = streamGetIntBefore('\n');
waitResponse();
}
// DBG("### Available:", result, "on", mux);
if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
return result;
}
bool modemGetConnected(uint8_t mux) {
// Read the status of all sockets at once
sendAT(GF("+CIPCLOSE?"));
if (waitResponse(GF("+CIPCLOSE:")) != 1) {
// return false; // TODO: Why does this not read correctly?
}
for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
// +CIPCLOSE:<link0_state>,<link1_state>,...,<link9_state>
bool muxState = stream.parseInt();
if (sockets[muxNo]) { sockets[muxNo]->sock_connected = muxState; }
}
waitResponse(); // Should be an OK at the end
if (!sockets[mux]) return false;
return sockets[mux]->sock_connected;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF(AT_NL "+CIPRXGET:"))) {
int8_t mode = streamGetIntBefore(',');
if (mode == 1) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
}
data = "";
// DBG("### Got Data:", mux);
return true;
} else {
data += mode;
return false;
}
} else if (data.endsWith(GF(AT_NL "+RECEIVE:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
}
data = "";
// DBG("### Got Data:", len, "on", mux);
return true;
} else if (data.endsWith(GF("+IPCLOSE:"))) {
int8_t mux = streamGetIntBefore(',');
streamSkipUntil('\n'); // Skip the reason code
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
return true;
} else if (data.endsWith(GF("+CIPEVENT:"))) {
// Need to close all open sockets and release the network library.
// User will then need to reconnect.
DBG("### Network error!");
if (!isGprsConnected()) { gprsDisconnect(); }
data = "";
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientSim7600* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTSIM7600_H_

View file

@ -0,0 +1,702 @@
/**
* @file TinyGsmClientSIM800.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTSIM800_H_
#define SRC_TINYGSMCLIENTSIM800_H_
// #pragma message("TinyGSM: TinyGsmClientSIM800")
// #define TINY_GSM_DEBUG Serial
// #define TINY_GSM_USE_HEX
#define TINY_GSM_MUX_COUNT 5
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "unknown"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#if defined(TINY_GSM_MODEM_SIM808)
#define MODEM_MODEL "SIM808";
#elif defined(TINY_GSM_MODEM_SIM868)
#define MODEM_MODEL "SIM868";
#elif defined(TINY_GSM_MODEM_SIM900)
#define MODEM_MODEL "SIM900";
#else
#define MODEM_MODEL "SIM800";
#endif
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmSSL.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmCalling.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmGSMLocation.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmNTP.tpp"
#include "TinyGsmBattery.tpp"
enum SIM800RegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmSim800 : public TinyGsmModem<TinyGsmSim800>,
public TinyGsmGPRS<TinyGsmSim800>,
public TinyGsmTCP<TinyGsmSim800, TINY_GSM_MUX_COUNT>,
public TinyGsmSSL<TinyGsmSim800, TINY_GSM_MUX_COUNT>,
public TinyGsmCalling<TinyGsmSim800>,
public TinyGsmSMS<TinyGsmSim800>,
public TinyGsmGSMLocation<TinyGsmSim800>,
public TinyGsmTime<TinyGsmSim800>,
public TinyGsmNTP<TinyGsmSim800>,
public TinyGsmBattery<TinyGsmSim800> {
friend class TinyGsmModem<TinyGsmSim800>;
friend class TinyGsmGPRS<TinyGsmSim800>;
friend class TinyGsmTCP<TinyGsmSim800, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSSL<TinyGsmSim800, TINY_GSM_MUX_COUNT>;
friend class TinyGsmCalling<TinyGsmSim800>;
friend class TinyGsmSMS<TinyGsmSim800>;
friend class TinyGsmGSMLocation<TinyGsmSim800>;
friend class TinyGsmTime<TinyGsmSim800>;
friend class TinyGsmNTP<TinyGsmSim800>;
friend class TinyGsmBattery<TinyGsmSim800>;
/*
* Inner Client
*/
public:
class GsmClientSim800 : public GsmClient {
friend class TinyGsmSim800;
public:
GsmClientSim800() {}
explicit GsmClientSim800(TinyGsmSim800& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmSim800* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+CIPCLOSE="), mux, GF(",1")); // Quick close
sock_connected = false;
at->waitResponse();
}
void stop() override {
stop(15000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
public:
class GsmClientSecureSim800 : public GsmClientSim800 {
public:
GsmClientSecureSim800() {}
explicit GsmClientSecureSim800(TinyGsmSim800& modem, uint8_t mux = 0)
: GsmClientSim800(modem, mux) {}
int connect(const char* host, uint16_t port, int timeout_s) override {
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
};
/*
* Constructor
*/
public:
explicit TinyGsmSim800(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSIM800"));
if (!testAT()) { return false; }
// sendAT(GF("&FZ")); // Factory + Reset
// waitResponse();
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) { return false; }
#ifdef TINY_GSM_DEBUG
sendAT(GF("+CMEE=2")); // turn on verbose error codes
#else
sendAT(GF("+CMEE=0")); // turn off error codes
#endif
waitResponse();
DBG(GF("### Modem:"), getModemName());
// Enable Local Time Stamp for getting network time
sendAT(GF("+CLTS=1"));
if (waitResponse(10000L) != 1) { return false; }
// Enable battery checks
sendAT(GF("+CBATCHK=1"));
waitResponse();
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
bool factoryDefaultImpl() {
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
waitResponse();
sendAT(GF("+IPR=0")); // Auto-baud
waitResponse();
sendAT(GF("+IFC=0,0")); // No Flow Control
waitResponse();
sendAT(GF("+ICF=3,3")); // 8 data 0 parity 1 stop
waitResponse();
sendAT(GF("+CSCLK=0")); // Disable Slow Clock
waitResponse();
sendAT(GF("&W")); // Write configuration
return waitResponse() == 1;
}
/*
bool thisHasSSL() {
#if defined(TINY_GSM_MODEM_SIM900)
return false;
#else
sendAT(GF("+CIPSSL=?"));
if (waitResponse(GF(AT_NL "+CIPSSL:")) != 1) { return false; }
return waitResponse() == 1;
#endif
}
*/
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
sendAT(GF("&W"));
waitResponse();
if (!setPhoneFunctionality(0)) { return false; }
if (!setPhoneFunctionality(1, true)) { return false; }
delay(3000);
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+CPOWD=1"));
return waitResponse(10000L, GF("NORMAL POWER DOWN")) == 1;
}
// During sleep, the SIM800 module has its serial communication disabled. In
// order to reestablish communication pull the DRT-pin of the SIM800 module
// LOW for at least 50ms. Then use this function to disable sleep mode. The
// DTR-pin can then be released again.
bool sleepEnableImpl(bool enable = true) {
sendAT(GF("+CSCLK="), enable);
return waitResponse() == 1;
}
// <fun> 0 Minimum functionality
// <fun> 1 Full functionality (Default)
// <fun> 4 Disable phone both transmit and receive RF circuits.
// <rst> Reset the MT before setting it to <fun> power level.
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
return waitResponse(10000L) == 1;
}
/*
* Generic network functions
*/
public:
SIM800RegStatus getRegistrationStatus() {
return (SIM800RegStatus)getRegistrationStatusXREG("CREG");
}
protected:
bool isNetworkConnectedImpl() {
SIM800RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
String getLocalIPImpl() {
sendAT(GF("+CIFSR;E0"));
String res;
if (waitResponse(10000L, res) != 1) { return ""; }
cleanResponseString(res);
return res;
}
/*
* Secure socket layer (SSL) functions
*/
// Follows functions as inherited from TinyGsmSSL.tpp
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect();
// Bearer settings for applications based on IP
// Set the connection type to GPRS
sendAT(GF("+SAPBR=3,1,\"Contype\",\"GPRS\""));
waitResponse();
// Set the APN
sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"');
waitResponse();
// Set the user name
if (user && strlen(user) > 0) {
sendAT(GF("+SAPBR=3,1,\"USER\",\""), user, '"');
waitResponse();
}
// Set the password
if (pwd && strlen(pwd) > 0) {
sendAT(GF("+SAPBR=3,1,\"PWD\",\""), pwd, '"');
waitResponse();
}
// Define the PDP context
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
waitResponse();
// Activate the PDP context
sendAT(GF("+CGACT=1,1"));
waitResponse(60000L);
// Open the definied GPRS bearer context
sendAT(GF("+SAPBR=1,1"));
waitResponse(85000L);
// Query the GPRS bearer context status
sendAT(GF("+SAPBR=2,1"));
if (waitResponse(30000L) != 1) { return false; }
// Attach to GPRS
sendAT(GF("+CGATT=1"));
if (waitResponse(60000L) != 1) { return false; }
// Set to multi-IP
sendAT(GF("+CIPMUX=1"));
if (waitResponse() != 1) { return false; }
// Put in "quick send" mode (thus no extra "Send OK")
sendAT(GF("+CIPQSEND=1"));
if (waitResponse() != 1) { return false; }
// Set to get data manually
sendAT(GF("+CIPRXGET=1"));
if (waitResponse() != 1) { return false; }
// Start Task and Set APN, USER NAME, PASSWORD
sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
if (waitResponse(60000L) != 1) { return false; }
// Bring Up Wireless Connection with GPRS or CSD
sendAT(GF("+CIICR"));
if (waitResponse(60000L) != 1) { return false; }
// Get Local IP Address, only assigned after connection
sendAT(GF("+CIFSR;E0"));
if (waitResponse(10000L) != 1) { return false; }
// Configure Domain Name Server (DNS)
sendAT(GF("+CDNSCFG=\"8.8.8.8\",\"8.8.4.4\""));
if (waitResponse() != 1) { return false; }
return true;
}
bool gprsDisconnectImpl() {
// Shut the TCP/IP connection
// CIPSHUT will close *all* open connections
sendAT(GF("+CIPSHUT"));
if (waitResponse(60000L) != 1) { return false; }
sendAT(GF("+CGATT=0")); // Detach from GPRS
if (waitResponse(60000L) != 1) { return false; }
return true;
}
String getProviderImpl() {
sendAT(GF("+CSPN?"));
if (waitResponse(GF("+CSPN:")) != 1) { return ""; }
streamSkipUntil('"'); /* Skip mode and format */
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
/*
* SIM card functions
*/
protected:
// May not return the "+CCID" before the number
String getSimCCIDImpl() {
sendAT(GF("+CCID"));
if (waitResponse(GF(AT_NL)) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
// Trim out the CCID header in case it is there
res.replace("CCID:", "");
res.trim();
return res;
}
/*
* Phone Call functions
*/
public:
bool setGsmBusy(bool busy = true) {
sendAT(GF("+GSMBUSY="), busy ? 1 : 0);
return waitResponse() == 1;
}
/*
* Audio functions
*/
public:
bool setVolume(uint8_t volume = 50) {
// Set speaker volume
sendAT(GF("+CLVL="), volume);
return waitResponse() == 1;
}
uint8_t getVolume() {
// Get speaker volume
sendAT(GF("+CLVL?"));
if (waitResponse(GF(AT_NL)) != 1) { return 0; }
String res = stream.readStringUntil('\n');
waitResponse();
res.replace("+CLVL:", "");
res.trim();
return res.toInt();
}
bool setMicVolume(uint8_t channel, uint8_t level) {
if (channel > 4) { return 0; }
sendAT(GF("+CMIC="), level);
return waitResponse() == 1;
}
bool setAudioChannel(uint8_t channel) {
sendAT(GF("+CHFA="), channel);
return waitResponse() == 1;
}
bool playToolkitTone(uint8_t tone, uint32_t duration) {
sendAT(GF("STTONE="), 1, tone);
delay(duration);
sendAT(GF("STTONE="), 0);
return waitResponse();
}
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
/*
* GSM Location functions
*/
// Depending on the exacty model and firmware revision, should return a
// GSM-based location from CLBS as as inherited from TinyGsmGSMLocation.tpp
// TODO(?): Check number of digits in year (2 or 4)
/*
* GPS/GNSS/GLONASS location functions
*/
// No functions of this type supported
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
// Follows all NTP server functions as inherited from TinyGsmNTP.tpp
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
// Follows all battery functions as inherited from TinyGsmBattery.tpp
/*
* Temperature functions
*/
// No functions of this type supported
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75) {
int8_t rsp;
uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
#if !defined(TINY_GSM_MODEM_SIM900)
sendAT(GF("+CIPSSL="), ssl);
rsp = waitResponse();
if (ssl && rsp != 1) { return false; }
#ifdef TINY_GSM_SSL_CLIENT_AUTHENTICATION
// set SSL options
// +SSLOPT=<opt>,<enable>
// <opt>
// 0 (default) ignore invalid certificate
// 1 client authentication
// <enable>
// 0 (default) close
// 1 open
sendAT(GF("+CIPSSL=1,1"));
if (waitResponse() != 1) return false;
#endif
#endif
sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host,
GF("\","), port);
rsp = waitResponse(
timeout_ms, GF("CONNECT OK" AT_NL), GF("CONNECT FAIL" AT_NL),
GF("ALREADY CONNECT" AT_NL), GF("ERROR" AT_NL),
GF("CLOSE OK" AT_NL)); // Happens when HTTPS handshake fails
return (1 == rsp);
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
if (waitResponse(GF(">")) != 1) { return 0; }
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(GF(AT_NL "DATA ACCEPT:")) != 1) { return 0; }
streamSkipUntil(','); // Skip mux
return streamGetIntBefore('\n');
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) return 0;
#ifdef TINY_GSM_USE_HEX
sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
#else
sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size);
if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
#endif
streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX
streamSkipUntil(','); // Skip mux
int16_t len_requested = streamGetIntBefore(',');
// ^^ Requested number of data bytes (1-1460 bytes)to be read
int16_t len_confirmed = streamGetIntBefore('\n');
// ^^ Confirmed number of data bytes to be read, which may be less than
// requested. 0 indicates that no data can be read.
// SRGD NOTE: Contrary to above (which is copied from AT command manual)
// this is actually be the number of bytes that will be remaining in the
// buffer after the read.
for (int i = 0; i < len_requested; i++) {
uint32_t startMillis = millis();
#ifdef TINY_GSM_USE_HEX
while (stream.available() < 2 &&
(millis() - startMillis < sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char buf[4] = {
0,
};
buf[0] = stream.read();
buf[1] = stream.read();
char c = strtol(buf, nullptr, 16);
#else
while (!stream.available() &&
(millis() - startMillis < sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char c = stream.read();
#endif
sockets[mux]->rx.put(c);
}
// DBG("### READ:", len_requested, "from", mux);
// sockets[mux]->sock_available = modemGetAvailable(mux);
sockets[mux]->sock_available = len_confirmed;
waitResponse();
return len_requested;
}
size_t modemGetAvailable(uint8_t mux) {
if (!sockets[mux]) return 0;
sendAT(GF("+CIPRXGET=4,"), mux);
size_t result = 0;
if (waitResponse(GF("+CIPRXGET:")) == 1) {
streamSkipUntil(','); // Skip mode 4
streamSkipUntil(','); // Skip mux
result = streamGetIntBefore('\n');
waitResponse();
}
// DBG("### Available:", result, "on", mux);
if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
return result;
}
bool modemGetConnected(uint8_t mux) {
sendAT(GF("+CIPSTATUS="), mux);
waitResponse(GF("+CIPSTATUS"));
int8_t res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""),
GF(",\"CLOSING\""), GF(",\"REMOTE CLOSING\""),
GF(",\"INITIAL\""));
waitResponse();
return 1 == res;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF(AT_NL "+CIPRXGET:"))) {
int8_t mode = streamGetIntBefore(',');
if (mode == 1) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
}
data = "";
// DBG("### Got Data:", mux);
return true;
} else {
data += mode;
return false;
}
} else if (data.endsWith(GF(AT_NL "+RECEIVE:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
}
data = "";
// DBG("### Got Data:", len, "on", mux);
return true;
} else if (data.endsWith(GF("CLOSED" AT_NL))) {
int8_t nl = data.lastIndexOf(AT_NL, data.length() - 8);
int8_t coma = data.indexOf(',', nl + 2);
int8_t mux = data.substring(nl + 2, coma).toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
return true;
} else if (data.endsWith(GF("*PSNWID:"))) {
streamSkipUntil('\n'); // Refresh network name by network
data = "";
DBG("### Network name updated.");
return true;
} else if (data.endsWith(GF("*PSUTTZ:"))) {
streamSkipUntil('\n'); // Refresh time and time zone by network
data = "";
DBG("### Network time and time zone updated.");
return true;
} else if (data.endsWith(GF("+CTZV:"))) {
streamSkipUntil('\n'); // Refresh network time zone by network
data = "";
DBG("### Network time zone updated.");
return true;
} else if (data.endsWith(GF("DST:"))) {
streamSkipUntil('\n'); // Refresh Network Daylight Saving Time by network
data = "";
DBG("### Daylight savings time state updated.");
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientSim800* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTSIM800_H_

View file

@ -0,0 +1,166 @@
/**
* @file TinyGsmClientSIM808.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTSIM808_H_
#define SRC_TINYGSMCLIENTSIM808_H_
// #pragma message("TinyGSM: TinyGsmClientSIM808")
#include "TinyGsmClientSIM800.h"
#include "TinyGsmGPS.tpp"
#include "TinyGsmBluetooth.tpp"
class TinyGsmSim808 : public TinyGsmSim800,
public TinyGsmGPS<TinyGsmSim808>,
public TinyGsmBluetooth<TinyGsmSim808> {
friend class TinyGsmGPS<TinyGsmSim808>;
friend class TinyGsmBluetooth<TinyGsmSim808>;
public:
explicit TinyGsmSim808(Stream& stream) : TinyGsmSim800(stream) {}
/*
* GPS/GNSS/GLONASS location functions
*/
protected:
// enable GPS
bool enableGPSImpl() {
sendAT(GF("+CGNSPWR=1"));
if (waitResponse() != 1) { return false; }
return true;
}
bool disableGPSImpl() {
sendAT(GF("+CGNSPWR=0"));
if (waitResponse() != 1) { return false; }
return true;
}
// get the RAW GPS output
// works only with ans SIM808 V2
String getGPSrawImpl() {
sendAT(GF("+CGNSINF"));
if (waitResponse(10000L, GF(AT_NL "+CGNSINF:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
// get GPS informations
// works only with ans SIM808 V2
bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0,
int* vsat = 0, int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
int* minute = 0, int* second = 0) {
sendAT(GF("+CGNSINF"));
if (waitResponse(10000L, GF(AT_NL "+CGNSINF:")) != 1) { return false; }
streamSkipUntil(','); // GNSS run status
if (streamGetIntBefore(',') == 1) { // fix status
// init variables
float ilat = 0;
float ilon = 0;
float ispeed = 0;
float ialt = 0;
int ivsat = 0;
int iusat = 0;
float iaccuracy = 0;
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
float secondWithSS = 0;
// UTC date & Time
iyear = streamGetIntLength(4); // Four digit year
imonth = streamGetIntLength(2); // Two digit month
iday = streamGetIntLength(2); // Two digit day
ihour = streamGetIntLength(2); // Two digit hour
imin = streamGetIntLength(2); // Two digit minute
secondWithSS =
streamGetFloatBefore(','); // 6 digit second with subseconds
ilat = streamGetFloatBefore(','); // Latitude
ilon = streamGetFloatBefore(','); // Longitude
ialt = streamGetFloatBefore(','); // MSL Altitude. Unit is meters
ispeed = streamGetFloatBefore(','); // Speed Over Ground. Unit is knots.
streamSkipUntil(','); // Course Over Ground. Degrees.
streamSkipUntil(','); // Fix Mode
streamSkipUntil(','); // Reserved1
iaccuracy =
streamGetFloatBefore(','); // Horizontal Dilution Of Precision
streamSkipUntil(','); // Position Dilution Of Precision
streamSkipUntil(','); // Vertical Dilution Of Precision
streamSkipUntil(','); // Reserved2
ivsat = streamGetIntBefore(','); // GNSS Satellites in View
iusat = streamGetIntBefore(','); // GNSS Satellites Used
streamSkipUntil(','); // GLONASS Satellites Used
streamSkipUntil(','); // Reserved3
streamSkipUntil(','); // C/N0 max
streamSkipUntil(','); // HPA
streamSkipUntil('\n'); // VPA
// Set pointers
if (lat != nullptr) *lat = ilat;
if (lon != nullptr) *lon = ilon;
if (speed != nullptr) *speed = ispeed;
if (alt != nullptr) *alt = ialt;
if (vsat != nullptr) *vsat = ivsat;
if (usat != nullptr) *usat = iusat;
if (accuracy != nullptr) *accuracy = iaccuracy;
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = static_cast<int>(secondWithSS);
waitResponse();
return true;
}
streamSkipUntil('\n'); // toss the row of commas
waitResponse();
return false;
}
/*
* Bluetooth functions
*/
bool enableBluetoothImpl() {
sendAT(GF("+BTPOWER=1"));
if (waitResponse() != 1) { return false; }
return true;
}
bool disableBluetoothImpl() {
sendAT(GF("+BTPOWER=0"));
if (waitResponse() != 1) { return false; }
return true;
}
bool setBluetoothVisibilityImpl(bool visible) {
sendAT(GF("+BTVIS="), visible);
if (waitResponse() != 1) { return false; }
return true;
}
bool setBluetoothHostNameImpl(const char* name) {
sendAT(GF("+BTHOST="), name);
if (waitResponse() != 1) { return false; }
return true;
}
};
#endif // SRC_TINYGSMCLIENTSIM808_H_

View file

@ -0,0 +1,857 @@
/**
* @file TinyGsmClientSaraR4.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTSARAR4_H_
#define SRC_TINYGSMCLIENTSARAR4_H_
// #pragma message("TinyGSM: TinyGsmClientSaraR4")
// #define TINY_GSM_DEBUG Serial
#define TINY_GSM_MUX_COUNT 7
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "u-blox"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#define MODEM_MODEL "SARA-R4"
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmSSL.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmGSMLocation.tpp"
#include "TinyGsmGPS.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmBattery.tpp"
#include "TinyGsmTemperature.tpp"
enum SaraR4RegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmSaraR4 : public TinyGsmModem<TinyGsmSaraR4>,
public TinyGsmGPRS<TinyGsmSaraR4>,
public TinyGsmTCP<TinyGsmSaraR4, TINY_GSM_MUX_COUNT>,
public TinyGsmSSL<TinyGsmSaraR4, TINY_GSM_MUX_COUNT>,
public TinyGsmSMS<TinyGsmSaraR4>,
public TinyGsmGSMLocation<TinyGsmSaraR4>,
public TinyGsmGPS<TinyGsmSaraR4>,
public TinyGsmTime<TinyGsmSaraR4>,
public TinyGsmBattery<TinyGsmSaraR4>,
public TinyGsmTemperature<TinyGsmSaraR4> {
friend class TinyGsmModem<TinyGsmSaraR4>;
friend class TinyGsmGPRS<TinyGsmSaraR4>;
friend class TinyGsmTCP<TinyGsmSaraR4, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSSL<TinyGsmSaraR4, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSMS<TinyGsmSaraR4>;
friend class TinyGsmGSMLocation<TinyGsmSaraR4>;
friend class TinyGsmGPS<TinyGsmSaraR4>;
friend class TinyGsmTime<TinyGsmSaraR4>;
friend class TinyGsmTemperature<TinyGsmSaraR4>;
friend class TinyGsmBattery<TinyGsmSaraR4>;
/*
* Inner Client
*/
public:
class GsmClientSaraR4 : public GsmClient {
friend class TinyGsmSaraR4;
public:
GsmClientSaraR4() {}
explicit GsmClientSaraR4(TinyGsmSaraR4& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmSaraR4* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
// stop(); // DON'T stop!
TINY_GSM_YIELD();
rx.clear();
uint8_t oldMux = mux;
sock_connected = at->modemConnect(host, port, &mux, false, timeout_s);
if (mux != oldMux) {
DBG("WARNING: Mux number changed from", oldMux, "to", mux);
at->sockets[oldMux] = nullptr;
}
at->sockets[mux] = this;
at->maintain();
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port, int timeout_s) {
return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s);
}
int connect(const char* host, uint16_t port) override {
return connect(host, port, 120);
}
int connect(IPAddress ip, uint16_t port) override {
return connect(ip, port, 120);
}
void stop(uint32_t maxWaitMs) {
uint32_t startMillis = millis();
dumpModemBuffer(maxWaitMs);
// We want to use an async socket close because the syncrhonous close of
// an open socket is INCREDIBLY SLOW and the modem can freeze up. But we
// only attempt the async close if we already KNOW the socket is open
// because calling the async close on a closed socket and then attempting
// opening a new socket causes the board to lock up for 2-3 minutes and
// then finally return with a "new" socket that is immediately closed.
// Attempting to close a socket that is already closed with a synchronous
// close quickly returns an error.
if (at->supportsAsyncSockets && sock_connected) {
DBG("### Closing socket asynchronously! Socket might remain open "
"until arrival of +UUSOCL:",
mux);
// faster asynchronous close
// NOT supported on SARA-R404M / SARA-R410M-01B
at->sendAT(GF("+USOCL="), mux, GF(",1"));
// NOTE: can take up to 120s to get a response
at->waitResponse((maxWaitMs - (millis() - startMillis)));
// We set the sock as disconnected right away because it can no longer
// be used
sock_connected = false;
} else {
// synchronous close
at->sendAT(GF("+USOCL="), mux);
// NOTE: can take up to 120s to get a response
at->waitResponse((maxWaitMs - (millis() - startMillis)));
sock_connected = false;
}
}
void stop() override {
stop(135000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
public:
class GsmClientSecureR4 : public GsmClientSaraR4 {
public:
GsmClientSecureR4() {}
explicit GsmClientSecureR4(TinyGsmSaraR4& modem, uint8_t mux = 0)
: GsmClientSaraR4(modem, mux) {}
public:
int connect(const char* host, uint16_t port, int timeout_s) override {
// stop(); // DON'T stop!
TINY_GSM_YIELD();
rx.clear();
uint8_t oldMux = mux;
sock_connected = at->modemConnect(host, port, &mux, true, timeout_s);
if (mux != oldMux) {
DBG("WARNING: Mux number changed from", oldMux, "to", mux);
at->sockets[oldMux] = nullptr;
}
at->sockets[mux] = this;
at->maintain();
return sock_connected;
}
int connect(IPAddress ip, uint16_t port, int timeout_s) override {
return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s);
}
int connect(const char* host, uint16_t port) override {
return connect(host, port, 120);
}
int connect(IPAddress ip, uint16_t port) override {
return connect(ip, port, 120);
}
};
/*
* Constructor
*/
public:
explicit TinyGsmSaraR4(Stream& stream)
: stream(stream),
has2GFallback(false),
supportsAsyncSockets(false) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSaraR4"));
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();
String modemName = getModemName();
DBG(GF("### Modem:"), modemName);
if (modemName.startsWith("u-blox SARA-R412")) {
has2GFallback = true;
} else {
has2GFallback = false;
}
if (modemName.startsWith("u-blox SARA-R404M") ||
modemName.startsWith("u-blox SARA-R410M-01B")) {
supportsAsyncSockets = false;
} else {
supportsAsyncSockets = true;
}
// Enable automatic time zome update
sendAT(GF("+CTZU=1"));
if (waitResponse(10000L) != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
// only difference in implementation is the warning on the wrong type
String getModemNameImpl() {
String manufacturer = getModemManufacturer();
String model = getModemModel();
String name = manufacturer + String(" ") + model;
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;
}
bool factoryDefaultImpl() {
sendAT(GF("&F")); // Resets the current profile, other NVM not affected
return waitResponse() == 1;
}
/*
* Power functions
*/
protected:
// using +CFUN=15 instead of the more common CFUN=1,1
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
if (!setPhoneFunctionality(15)) { return false; }
delay(3000); // TODO(?): Verify delay timing here
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+CPWROFF"));
return waitResponse(40000L) == 1;
}
bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
return waitResponse(10000L) == 1;
}
/*
* Generic network functions
*/
public:
SaraR4RegStatus getRegistrationStatus() {
// Check first for EPS registration
SaraR4RegStatus epsStatus =
(SaraR4RegStatus)getRegistrationStatusXREG("CEREG");
// If we're connected on EPS, great!
if (epsStatus == REG_OK_HOME || epsStatus == REG_OK_ROAMING) {
return epsStatus;
} else {
// Otherwise, check generic network status
return (SaraR4RegStatus)getRegistrationStatusXREG("CREG");
}
}
protected:
bool isNetworkConnectedImpl() {
SaraR4RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
public:
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();
}
/*
* Secure socket layer (SSL) functions
*/
// Follows functions as inherited from TinyGsmSSL.tpp
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
// gprsDisconnect();
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)
// Set the authentication
if (user && strlen(user) > 0) {
sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"');
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 gprsDisconnectImpl() {
// Mark all the sockets as closed
// This ensures that asynchronously closed sockets are marked closed
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClientSaraR4* sock = sockets[mux];
if (sock && sock->sock_connected) { sock->sock_connected = false; }
}
// sendAT(GF("+CGACT=0,1")); // Deactivate PDP context 1
sendAT(GF("+CGACT=0")); // Deactivate all contexts
if (waitResponse(40000L) != 1) {
// return false;
}
sendAT(GF("+CGATT=0")); // detach from GPRS
if (waitResponse(360000L) != 1) { return false; }
return true;
}
/*
* SIM card functions
*/
protected:
// This uses "CGSN" instead of "GSN"
String getIMEIImpl() {
sendAT(GF("+CGSN"));
if (waitResponse(GF(AT_NL)) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Phone Call functions
*/
// No functions of this type supported
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
protected:
String sendUSSDImpl(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sendSMS_UTF16Impl(const String& number, const void* text,
size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* GSM/GPS/GNSS/GLONASS Location functions
* NOTE: u-blox modules use the same function to get location data from both
* GSM tower triangulation and from dedicated GPS/GNSS/GLONASS receivers. The
* only difference in which sensor the data is requested from. If a GNSS
* location is requested from a modem without a GNSS receiver installed on the
* I2C port, the GSM-based "Cell Locate" location will be returned instead.
*/
protected:
bool enableGPSImpl() {
// AT+UGPS=<mode>[,<aid_mode>[,<GNSS_systems>]]
// <mode> - 0: GNSS receiver powered off, 1: on
// <aid_mode> - 0: no aiding (default)
// <GNSS_systems> - 3: GPS + SBAS (default)
sendAT(GF("+UGPS=1,0,3"));
if (waitResponse(10000L, GF(AT_NL "+UGPS:")) != 1) { return false; }
return waitResponse(10000L) == 1;
}
bool disableGPSImpl() {
sendAT(GF("+UGPS=0"));
if (waitResponse(10000L, GF(AT_NL "+UGPS:")) != 1) { return false; }
return waitResponse(10000L) == 1;
}
String inline getUbloxLocationRaw(int8_t sensor) {
// AT+ULOC=<mode>,<sensor>,<response_type>,<timeout>,<accuracy>
// <mode> - 2: single shot position
// <sensor> - 0: use the last fix in the internal database and stop the GNSS
// receiver
// - 1: use the GNSS receiver for localization
// - 2: use cellular CellLocate location information
// - 3: ?? use the combined GNSS receiver and CellLocate service
// information ?? - Docs show using sensor 3 and it's
// documented for the +UTIME command but not for +ULOC
// <response_type> - 0: standard (single-hypothesis) response
// <timeout> - Timeout period in seconds
// <accuracy> - Target accuracy in meters (1 - 999999)
sendAT(GF("+ULOC=2,"), sensor, GF(",0,120,1"));
// wait for first "OK"
if (waitResponse(10000L) != 1) { return ""; }
// wait for the final result - wait full timeout time
if (waitResponse(120000L, GF(AT_NL "+UULOC:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
String getGsmLocationRawImpl() {
return getUbloxLocationRaw(2);
}
String getGPSrawImpl() {
return getUbloxLocationRaw(1);
}
inline bool getUbloxLocation(int8_t sensor, float* lat, float* lon,
float* speed = 0, float* alt = 0, int* vsat = 0,
int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0,
int* hour = 0, int* minute = 0,
int* second = 0) {
// AT+ULOC=<mode>,<sensor>,<response_type>,<timeout>,<accuracy>
// <mode> - 2: single shot position
// <sensor> - 2: use cellular CellLocate location information
// - 0: use the last fix in the internal database and stop the GNSS
// receiver
// - 1: use the GNSS receiver for localization
// - 3: ?? use the combined GNSS receiver and CellLocate service
// information ?? - Docs show using sensor 3 and it's documented
// for the +UTIME command but not for +ULOC
// <response_type> - 0: standard (single-hypothesis) response
// <timeout> - Timeout period in seconds
// <accuracy> - Target accuracy in meters (1 - 999999)
sendAT(GF("+ULOC=2,"), sensor, GF(",0,120,1"));
// wait for first "OK"
if (waitResponse(10000L) != 1) { return false; }
// wait for the final result - wait full timeout time
if (waitResponse(120000L, GF(AT_NL "+UULOC: ")) != 1) { return false; }
// +UULOC: <date>, <time>, <lat>, <long>, <alt>, <uncertainty>, <speed>,
// <direction>, <vertical_acc>, <sensor_used>, <SV_used>, <antenna_status>,
// <jamming_status>
// init variables
float ilat = 0;
float ilon = 0;
float ispeed = 0;
float ialt = 0;
int iusat = 0;
float iaccuracy = 0;
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
float secondWithSS = 0;
// Date & Time
iday = streamGetIntBefore('/'); // Two digit day
imonth = streamGetIntBefore('/'); // Two digit month
iyear = streamGetIntBefore(','); // Four digit year
ihour = streamGetIntBefore(':'); // Two digit hour
imin = streamGetIntBefore(':'); // Two digit minute
secondWithSS = streamGetFloatBefore(','); // 6 digit second with subseconds
ilat = streamGetFloatBefore(','); // Estimated latitude, in degrees
ilon = streamGetFloatBefore(','); // Estimated longitude, in degrees
ialt = streamGetFloatBefore(
','); // Estimated altitude, in meters - only forGNSS
// positioning, 0 in case of CellLocate
if (ialt != 0) { // values not returned for CellLocate
iaccuracy =
streamGetFloatBefore(','); // Maximum possible error, in meters
ispeed = streamGetFloatBefore(','); // Speed over ground m/s3
streamSkipUntil(','); // Course over ground in degree (0 deg - 360 deg)
streamSkipUntil(','); // Vertical accuracy, in meters
streamSkipUntil(','); // Sensor used for the position calculation
iusat = streamGetIntBefore(','); // Number of satellite used
streamSkipUntil(','); // Antenna status
streamSkipUntil('\n'); // Jamming status
} else {
iaccuracy =
streamGetFloatBefore('\n'); // Maximum possible error, in meters
}
// Set pointers
if (lat != nullptr) *lat = ilat;
if (lon != nullptr) *lon = ilon;
if (speed != nullptr) *speed = ispeed;
if (alt != nullptr) *alt = ialt;
if (vsat != nullptr)
*vsat = 0; // Number of satellites viewed not reported;
if (usat != nullptr) *usat = iusat;
if (accuracy != nullptr) *accuracy = iaccuracy;
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = static_cast<int>(secondWithSS);
// final ok
waitResponse();
return true;
}
bool getGsmLocationImpl(float* lat, float* lon, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0,
int* hour = 0, int* minute = 0, int* second = 0) {
return getUbloxLocation(2, lat, lon, 0, 0, 0, 0, accuracy, year, month, day,
hour, minute, second);
}
bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0,
int* vsat = 0, int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
int* minute = 0, int* second = 0) {
return getUbloxLocation(1, lat, lon, speed, alt, vsat, usat, accuracy, year,
month, day, hour, minute, second);
}
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
protected:
int16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
int8_t getBattPercentImpl() {
sendAT(GF("+CIND?"));
if (waitResponse(GF(AT_NL "+CIND:")) != 1) { return 0; }
int8_t res = streamGetIntBefore(',');
int8_t percent = res * 20; // return is 0-5
// Wait for final OK
waitResponse();
return percent;
}
int8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
bool getBattStatsImpl(int8_t& chargeState, int8_t& percent,
int16_t& milliVolts) {
chargeState = 0;
percent = getBattPercent();
milliVolts = 0;
return true;
}
/*
* Temperature functions
*/
protected:
float getTemperatureImpl() {
// First make sure the temperature is set to be in celsius
sendAT(GF("+UTEMP=0")); // Would use 1 for Fahrenheit
if (waitResponse() != 1) { return static_cast<float>(-9999); }
sendAT(GF("+UTEMP?"));
if (waitResponse(GF(AT_NL "+UTEMP:")) != 1) {
return static_cast<float>(-9999);
}
int16_t res = streamGetIntBefore('\n');
float temp = -9999;
if (res != -1) { temp = (static_cast<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;
uint32_t startMillis = millis();
// create a socket
sendAT(GF("+USOCR=6"));
// reply is +USOCR: ## of socket created
if (waitResponse(GF(AT_NL "+USOCR:")) != 1) { return false; }
*mux = streamGetIntBefore('\n');
waitResponse();
if (ssl) {
sendAT(GF("+USOSEC="), *mux, ",1");
waitResponse();
}
// Enable NODELAY
// AT+USOSO=<socket>,<level>,<opt_name>,<opt_val>[,<opt_val2>]
// <level> - 0 for IP, 6 for TCP, 65535 for socket level options
// <opt_name> TCP/1 = no delay (do not delay send to coalesce packets)
// NOTE: Enabling this may increase data plan usage
// 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
// Use an asynchronous open to reduce the number of terminal freeze-ups
// This is still blocking until the URC arrives
// The SARA-R410M-02B with firmware revisions prior to L0.0.00.00.05.08
// has a nasty habit of locking up when opening a socket, especially if
// the cellular service is poor.
// NOT supported on SARA-R404M / SARA-R410M-01B
if (supportsAsyncSockets) {
DBG("### Opening socket asynchronously! Socket cannot be used until "
"the URC '+UUSOCO' appears.");
sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port, ",1");
if (waitResponse(timeout_ms - (millis() - startMillis),
GF(AT_NL "+UUSOCO:")) == 1) {
streamGetIntBefore(','); // skip repeated mux
int8_t connection_status = streamGetIntBefore('\n');
DBG("### Waited", millis() - startMillis, "ms for socket to open");
return (0 == connection_status);
} else {
DBG("### Waited", millis() - startMillis,
"but never got socket open notice");
return false;
}
} else {
// use synchronous open
sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port);
int8_t rsp = waitResponse(timeout_ms - (millis() - startMillis));
return (1 == rsp);
}
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+USOWR="), mux, ',', (uint16_t)len);
if (waitResponse(GF("@")) != 1) { return 0; }
// 50ms delay, see AT manual section 25.10.4
delay(50);
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(GF(AT_NL "+USOWR:")) != 1) { return 0; }
streamSkipUntil(','); // Skip mux
int16_t sent = streamGetIntBefore('\n');
waitResponse(); // sends back OK after the confirmation of number sent
return sent;
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) return 0;
sendAT(GF("+USORD="), mux, ',', (uint16_t)size);
if (waitResponse(GF(AT_NL "+USORD:")) != 1) { return 0; }
streamSkipUntil(','); // Skip mux
int16_t len = streamGetIntBefore(',');
streamSkipUntil('\"');
for (int i = 0; i < len; i++) { moveCharFromStreamToFifo(mux); }
streamSkipUntil('\"');
waitResponse();
// DBG("### READ:", len, "from", mux);
sockets[mux]->sock_available = modemGetAvailable(mux);
return len;
}
size_t modemGetAvailable(uint8_t mux) {
if (!sockets[mux]) return 0;
// 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(AT_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 = streamGetIntBefore('\n');
// if (result) DBG("### DATA AVAILABLE:", result, "on", mux);
waitResponse();
}
if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
// DBG("### Available:", result, "on", 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(AT_NL "+USOCTL:"));
if (res != 1) { return false; }
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip type
int8_t result = streamGetIntBefore('\n');
// 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);
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF("+UUSORD:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
// max size is 1024
if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
}
data = "";
DBG("### URC Data Received:", len, "on", mux);
return true;
} else if (data.endsWith(GF("+UUSOCL:"))) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### URC Sock Closed: ", mux);
return true;
} else if (data.endsWith(GF("+UUSOCO:"))) {
int8_t mux = streamGetIntBefore('\n');
int8_t socket_error = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux] &&
socket_error == 0) {
sockets[mux]->sock_connected = true;
}
data = "";
DBG("### URC Sock Opened: ", mux);
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientSaraR4* sockets[TINY_GSM_MUX_COUNT];
bool has2GFallback;
bool supportsAsyncSockets;
};
#endif // SRC_TINYGSMCLIENTSARAR4_H_

View file

@ -0,0 +1,823 @@
/**
* @file TinyGsmClientSaraR5.h
* @author Sebastian Bergner, Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Aug 2023
*/
#ifndef SRC_TINYGSMCLIENTSARAR5_H_
#define SRC_TINYGSMCLIENTSARAR5_H_
// #pragma message("TinyGSM: TinyGsmClientSaraR5")
// #define TINY_GSM_DEBUG Serial
#define TINY_GSM_MUX_COUNT 7
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "u-blox"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#define MODEM_MODEL "SARA-R5"
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmSSL.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmCalling.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmGSMLocation.tpp"
#include "TinyGsmGPS.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmBattery.tpp"
enum SaraR5RegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
REG_SMS_ONLY_HOME = 6,
REG_SMS_ONLY_ROAMING = 7,
REG_EMERGENCY_ONLY =
8, // blox AT command manual states: attached for emergency bearer
// services only (see 3GPP TS 24.008 [85] and 3GPP TS 24.301 [120]
// that specify the condition when the MS is considered as attached
// for emergency bearer services)
REG_NO_FALLBACK_LTE_HOME =
9, // not 100% certain, ublox AT command manual states: registered for
// "CSFB not preferred", home network (applicable only when
// <AcTStatus> indicates E-UTRAN)
REG_NO_FALLBACK_LTE_ROAMING =
10 // not 100% certain, ublox AT command manual states: registered for
// "CSFB not preferred", roaming (applicable only when <AcTStatus>
// indicates E-UTRAN)
};
class TinyGsmSaraR5 : public TinyGsmModem<TinyGsmSaraR5>,
public TinyGsmGPRS<TinyGsmSaraR5>,
public TinyGsmTCP<TinyGsmSaraR5, TINY_GSM_MUX_COUNT>,
public TinyGsmSSL<TinyGsmSaraR5, TINY_GSM_MUX_COUNT>,
public TinyGsmCalling<TinyGsmSaraR5>,
public TinyGsmSMS<TinyGsmSaraR5>,
public TinyGsmGSMLocation<TinyGsmSaraR5>,
public TinyGsmGPS<TinyGsmSaraR5>,
public TinyGsmTime<TinyGsmSaraR5>,
public TinyGsmBattery<TinyGsmSaraR5> {
friend class TinyGsmModem<TinyGsmSaraR5>;
friend class TinyGsmGPRS<TinyGsmSaraR5>;
friend class TinyGsmTCP<TinyGsmSaraR5, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSSL<TinyGsmSaraR5, TINY_GSM_MUX_COUNT>;
friend class TinyGsmCalling<TinyGsmSaraR5>;
friend class TinyGsmSMS<TinyGsmSaraR5>;
friend class TinyGsmGSMLocation<TinyGsmSaraR5>;
friend class TinyGsmGPS<TinyGsmSaraR5>;
friend class TinyGsmTime<TinyGsmSaraR5>;
friend class TinyGsmBattery<TinyGsmSaraR5>;
/*
* Inner Client
*/
public:
class GsmClientSaraR5 : public GsmClient {
friend class TinyGsmSaraR5;
public:
GsmClientSaraR5() {}
explicit GsmClientSaraR5(TinyGsmSaraR5& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmSaraR5* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
// stop(); // DON'T stop!
TINY_GSM_YIELD();
rx.clear();
uint8_t oldMux = mux;
sock_connected = at->modemConnect(host, port, &mux, false, timeout_s);
if (mux != oldMux) {
DBG("WARNING: Mux number changed from", oldMux, "to", mux);
at->sockets[oldMux] = nullptr;
}
at->sockets[mux] = this;
at->maintain();
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+USOCL="), mux);
at->waitResponse(); // should return within 1s
sock_connected = false;
}
void stop() override {
stop(15000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
public:
class GsmClientSecureR5 : public GsmClientSaraR5 {
public:
GsmClientSecureR5() {}
explicit GsmClientSecureR5(TinyGsmSaraR5& modem, uint8_t mux = 0)
: GsmClientSaraR5(modem, mux) {}
public:
int connect(const char* host, uint16_t port, int timeout_s) override {
// stop(); // DON'T stop!
TINY_GSM_YIELD();
rx.clear();
uint8_t oldMux = mux;
sock_connected = at->modemConnect(host, port, &mux, true, timeout_s);
if (mux != oldMux) {
DBG("WARNING: Mux number changed from", oldMux, "to", mux);
at->sockets[oldMux] = nullptr;
}
at->sockets[mux] = this;
at->maintain();
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
};
/*
* Constructor
*/
public:
explicit TinyGsmSaraR5(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSaraR5"));
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();
DBG(GF("### Modem:"), getModemName());
// Enable automatic time zome update
sendAT(GF("+CTZU=1"));
waitResponse(10000L);
// Ignore the response, in case the network doesn't support it.
// if (waitResponse(10000L) != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
// only difference in implementation is the warning on the wrong type
String getModemNameImpl() {
String manufacturer = getModemManufacturer();
String model = getModemModel();
String name = manufacturer + String(" ") + model;
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!");
}
return name;
}
bool factoryDefaultImpl() {
sendAT(GF("+UFACTORY=0,1")); // No factory restore, erase NVM
waitResponse();
return setPhoneFunctionality(16); // Reset
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
if (!setPhoneFunctionality(16)) { return false; }
delay(3000); // TODO(?): Verify delay timing here
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+CPWROFF"));
return waitResponse(40000L) == 1;
}
bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
return waitResponse(10000L) == 1;
}
/*
* Generic network functions
*/
public:
SaraR5RegStatus getRegistrationStatus() {
// Check first for EPS registration
SaraR5RegStatus epsStatus =
(SaraR5RegStatus)getRegistrationStatusXREG("CEREG");
// If we're connected on EPS, great!
if (epsStatus == REG_OK_HOME || epsStatus == REG_OK_ROAMING) {
return epsStatus;
} else {
// Otherwise, check generic network status
return (SaraR5RegStatus)getRegistrationStatusXREG("CREG");
}
}
bool setRadioAccessTechnology(int selected, int preferred) {
// selected:
// 0: GSM / GPRS / eGPRS (single mode)
// 1: GSM / UMTS (dual mode)
// 2: UMTS (single mode)
// 3: LTE (single mode)
// 4: GSM / UMTS / LTE (tri mode)
// 5: GSM / LTE (dual mode)
// 6: UMTS / LTE (dual mode)
// preferred:
// 0: GSM / GPRS / eGPRS
// 2: UTRAN
// 3: LTE
sendAT(GF("+URAT="), selected, GF(","), preferred);
if (waitResponse() != 1) { return false; }
return true;
}
bool getCurrentRadioAccessTechnology() {
sendAT(GF("+URAT?"));
String response;
if (waitResponse(10000L, response) != 1) { return false; }
return true;
}
protected:
bool isNetworkConnectedImpl() {
SaraR5RegStatus s = getRegistrationStatus();
if (s == REG_OK_HOME || s == REG_OK_ROAMING || s == REG_SMS_ONLY_ROAMING ||
s == REG_SMS_ONLY_HOME)
return true;
else if (s == REG_UNKNOWN) // for some reason, it can hang at unknown..
return isGprsConnected();
else
return false;
}
String getLocalIPImpl() {
sendAT(GF("+UPSND=0,0"));
if (waitResponse(GF(AT_NL "+UPSND:")) != 1) { return ""; }
streamSkipUntil(','); // Skip PSD profile
streamSkipUntil('\"'); // Skip request type
String res = stream.readStringUntil('\"');
if (waitResponse() != 1) { return ""; }
return res;
}
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
sendAT(GF("+CGATT=1")); // attach to GPRS
if (waitResponse(360000L) != 1) { return false; }
// Setting up the PSD profile/PDP context with the UPSD commands sets up an
// "internal" PDP context, i.e. a data connection using the internal IP
// stack and related AT commands for sockets.
// Packet switched data configuration
// AT+UPSD=<profile_id>,<param_tag>,<param_val>
// profile_id = 0 - PSD profile identifier, in range 0-6 (NOT PDP context)
// param_tag = 1: APN
// param_tag = 2: username -> not working for SARA-R5
// param_tag = 3: password -> not working for SARA-R5
// param_tag = 7: IP address Note: IP address set as "0.0.0.0" means
// dynamic IP address assigned during PDP context activation
// check all available PDP context identifiers
String response;
response.reserve(1024);
sendAT(GF("+CGDCONT?"));
waitResponseUntilEndStream(1000, response);
if (response.length() == 0) {
return false; // no apn at all found
}
// parse string & look for apn -> modified from SparkFun u-blox SARA-R5 lib
// Example:
// +CGDCONT: 0,"IP","payandgo.o2.co.uk","0.0.0.0",0,0,0,0,0,0,0,0,0,0
// +CGDCONT:
// 1,"IP","payandgo.o2.co.uk.mnc010.mcc234.gprs","10.160.182.234",0,0,0,2,0,0,0,0,0,0
// create search buffer where we can search
char* searchBuf = (char*)malloc(response.length() + 1);
response.toCharArray(searchBuf, response.length() + 1);
int rcid = -1;
char* searchPtr = searchBuf;
for (size_t index = 0; index <= response.length(); index++) {
int scanned = 0;
// Find the first/next occurrence of +CGDCONT:
searchPtr = strstr(searchPtr, "+CGDCONT:");
if (searchPtr != nullptr) {
char strPdpType[10];
char strApn[128];
int ipOct[4];
searchPtr += strlen("+CGDCONT:");
while (*searchPtr == ' ') searchPtr++; // skip spaces
scanned = sscanf(searchPtr, "%d,\"%[^\"]\",\"%[^\"]\",\"%d.%d.%d.%d",
&rcid, strPdpType, strApn, &ipOct[0], &ipOct[1],
&ipOct[2], &ipOct[3]);
if (!strcmp(strApn, apn)) {
// found the configuration that we want to connect to
break;
}
}
}
free(searchBuf);
sendAT(GF(
"+UPSDA=0,4")); // Deactivate the PDP context associated with profile 0
waitResponse(360000L); // Can return an error if previously not activated
sendAT(GF("+UPSD=0,100,"),
rcid); // Deactivate the PDP context associated with profile 0
waitResponse();
sendAT(GF(
"+UPSDA=0,3")); // Activate the PDP context associated with profile 0
if (waitResponse(360000L) != 1) { return false; }
sendAT(GF("+UPSD=0,0,2")); // Set protocol type to IPv4v6 with IPv4
// preferred for internal sockets
waitResponse();
return true;
}
bool gprsDisconnectImpl() {
sendAT(GF(
"+UPSDA=0,4")); // Deactivate the PDP context associated with profile 0
if (waitResponse(360000L) != 1) { return false; }
sendAT(GF("+CGATT=0")); // detach from GPRS
if (waitResponse(360000L) != 1) { return false; }
return true;
}
/*
* SIM card functions
*/
protected:
// This uses "CGSN" instead of "GSN"
String getIMEIImpl() {
sendAT(GF("+CGSN"));
if (waitResponse(GF(AT_NL)) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Phone Call functions
*/
// Follows all phone call functions as inherited from TinyGsmCalling.tpp
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
/*
* GSM/GPS/GNSS/GLONASS Location functions
* NOTE: u-blox modules use the same function to get location data from both
* GSM tower triangulation and from dedicated GPS/GNSS/GLONASS receivers. The
* only difference in which sensor the data is requested from. If a GNSS
* location is requested from a modem without a GNSS receiver installed on the
* I2C port, the GSM-based "Cell Locate" location will be returned instead.
*/
protected:
String inline getUbloxLocationRaw(int8_t sensor) {
// AT+ULOC=<mode>,<sensor>,<response_type>,<timeout>,<accuracy>
// <mode> - 2: single shot position
// <sensor> - 0: use the last fix in the internal database and stop the GNSS
// receiver
// - 1: use the GNSS receiver for localization
// - 2: use cellular CellLocate location information
// - 3: ?? use the combined GNSS receiver and CellLocate service
// information ?? - Docs show using sensor 3 and it's
// documented for the +UTIME command but not for +ULOC
// <response_type> - 0: standard (single-hypothesis) response
// <timeout> - Timeout period in seconds
// <accuracy> - Target accuracy in meters (1 - 999999)
sendAT(GF("+ULOC=2,"), sensor, GF(",0,120,1"));
// wait for first "OK"
// waitResponse(10000L) ;
if (waitResponse(10000L) != 1) { return ""; }
// wait for the final result - wait full timeout time
if (waitResponse(120000L, GF(AT_NL "+UULOC:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
String getGsmLocationRawImpl() {
return getUbloxLocationRaw(2);
}
String getGPSrawImpl() {
return getUbloxLocationRaw(1);
}
inline bool getUbloxLocation(int8_t sensor, float* lat, float* lon,
float* speed = 0, float* alt = 0, int* vsat = 0,
int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0,
int* hour = 0, int* minute = 0,
int* second = 0) {
// AT+ULOC=<mode>,<sensor>,<response_type>,<timeout>,<accuracy>
// <mode> - 2: single shot position
// <sensor> - 2: use cellular CellLocate location information
// - 0: use the last fix in the internal database and stop the GNSS
// receiver
// - 1: use the GNSS receiver for localization
// - 3: ?? use the combined GNSS receiver and CellLocate service
// information ?? - Docs show using sensor 3 and it's documented
// for the +UTIME command but not for +ULOC
// <response_type> - 0: standard (single-hypothesis) response -> +UULOC:
// <date>,<time>,<lat>,<long>,<alt>,<uncertainty>
// 1: detailed (single-hypothesis) response -> +UULOC:
// <date>,<time>,<lat>,<long>,<alt>,<uncertainty>,<speed>,<direction>,<vertical_acc>,<sensor_used>,<SV_used>,<antenna_status>,<jamming_status>
// <timeout> - Timeout period in seconds
// <accuracy> - Target accuracy in meters (1 - 999999)
sendAT(GF("+ULOC=2,"), sensor, GF(",1,120,1"));
// wait for first "OK"
if (waitResponse(10000L) != 1) { return false; }
// wait for the final result - wait full timeout time
if (waitResponse(120000L, GF(AT_NL "+UULOC: ")) != 1) { return false; }
// +UULOC: <date>, <time>, <lat>, <long>, <alt>, <uncertainty>, <speed>,
// <direction>, <vertical_acc>, <sensor_used>, <SV_used>, <antenna_status>,
// <jamming_status>
// init variables
float ilat = 0;
float ilon = 0;
float ispeed = 0;
float ialt = 0;
int iusat = 0;
float iaccuracy = 0;
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
float secondWithSS = 0;
// Date & Time
iday = streamGetIntBefore('/'); // Two digit day
imonth = streamGetIntBefore('/'); // Two digit month
iyear = streamGetIntBefore(','); // Four digit year
ihour = streamGetIntBefore(':'); // Two digit hour
imin = streamGetIntBefore(':'); // Two digit minute
secondWithSS = streamGetFloatBefore(','); // 6 digit second with subseconds
ilat = streamGetFloatBefore(','); // Estimated latitude, in degrees
ilon = streamGetFloatBefore(','); // Estimated longitude, in degrees
ialt = streamGetFloatBefore(
','); // Estimated altitude, in meters - only forGNSS
// positioning, 0 in case of CellLocate
if (ialt != 0) { // values not returned for CellLocate
iaccuracy =
streamGetFloatBefore(','); // Maximum possible error, in meters
ispeed = streamGetFloatBefore(','); // Speed over ground m/s3
streamSkipUntil(','); // Course over ground in degree (0 deg - 360 deg)
streamSkipUntil(','); // Vertical accuracy, in meters
streamSkipUntil(','); // Sensor used for the position calculation
iusat = streamGetIntBefore(','); // Number of satellite used
streamSkipUntil(','); // Antenna status
streamSkipUntil('\n'); // Jamming status
} else {
iaccuracy =
streamGetFloatBefore('\n'); // Maximum possible error, in meters
}
// Set pointers
if (lat != nullptr) *lat = ilat;
if (lon != nullptr) *lon = ilon;
if (speed != nullptr) *speed = ispeed;
if (alt != nullptr) *alt = ialt;
if (vsat != nullptr)
*vsat = 0; // Number of satellites viewed not reported;
if (usat != nullptr) *usat = iusat;
if (accuracy != nullptr) *accuracy = iaccuracy;
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = static_cast<int>(secondWithSS);
// final ok
waitResponse();
return true;
}
bool getGsmLocationImpl(float* lat, float* lon, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0,
int* hour = 0, int* minute = 0, int* second = 0) {
return getUbloxLocation(2, lat, lon, 0, 0, 0, 0, accuracy, year, month, day,
hour, minute, second);
}
bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0,
int* vsat = 0, int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
int* minute = 0, int* second = 0) {
return getUbloxLocation(1, lat, lon, speed, alt, vsat, usat, accuracy, year,
month, day, hour, minute, second);
}
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* Battery functions
*/
protected:
int16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
int8_t getBattPercentImpl() {
sendAT(GF("+CIND?"));
if (waitResponse(GF(AT_NL "+CIND:")) != 1) { return 0; }
int8_t res = streamGetIntBefore(',');
int8_t percent = res * 20; // return is 0-5
// Wait for final OK
waitResponse();
return percent;
}
int8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
bool getBattStatsImpl(int8_t& chargeState, int8_t& percent,
int16_t& milliVolts) {
chargeState = 0;
percent = getBattPercent();
milliVolts = 0;
return true;
}
/*
* Temperature functions
*/
// This would only available for a small number of modules in this group
// (TOBY-L)
float getTemperatureImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Client related functions
*/
protected:
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;
uint32_t startMillis = millis();
// create a socket
sendAT(GF("+USOCR=6"));
// reply is +USOCR: ## of socket created
if (waitResponse(GF(AT_NL "+USOCR:")) != 1) { return false; }
*mux = streamGetIntBefore('\n');
waitResponse();
if (ssl) {
sendAT(GF("+USOSEC="), *mux, ",1");
waitResponse();
}
// Enable NODELAY
// AT+USOSO=<socket>,<level>,<opt_name>,<opt_val>[,<opt_val2>]
// <level> - 0 for IP, 6 for TCP, 65535 for socket level options
// <opt_name> TCP/1 = no delay (do not delay send to coalesce packets)
// NOTE: Enabling this may increase data plan usage
// 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);
int8_t rsp = waitResponse(timeout_ms - (millis() - startMillis));
return (1 == rsp);
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+USOWR="), mux, ',', (uint16_t)len);
if (waitResponse(GF("@")) != 1) { return 0; }
// 50ms delay, see AT manual section 25.10.4
delay(50);
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(GF(AT_NL "+USOWR:")) != 1) { return 0; }
streamSkipUntil(','); // Skip mux
int16_t sent = streamGetIntBefore('\n');
waitResponse(); // sends back OK after the confirmation of number sent
return sent;
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) return 0;
sendAT(GF("+USORD="), mux, ',', (uint16_t)size);
if (waitResponse(GF(AT_NL "+USORD:")) != 1) { return 0; }
streamSkipUntil(','); // Skip mux
int16_t len = streamGetIntBefore(',');
streamSkipUntil('\"');
for (int i = 0; i < len; i++) { moveCharFromStreamToFifo(mux); }
streamSkipUntil('\"');
waitResponse();
// DBG("### READ:", len, "from", mux);
sockets[mux]->sock_available = modemGetAvailable(mux);
return len;
}
size_t modemGetAvailable(uint8_t mux) {
if (!sockets[mux]) return 0;
// 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(AT_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 = streamGetIntBefore('\n');
// if (result) DBG("### DATA AVAILABLE:", result, "on", mux);
waitResponse();
}
if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
// DBG("### Available:", result, "on", 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(AT_NL "+USOCTL:"));
if (res != 1) { return false; }
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip type
int8_t result = streamGetIntBefore('\n');
// 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);
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF("+UUSORD:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
// max size is 1024
if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
}
data = "";
// DBG("### URC Data Received:", len, "on", mux);
return true;
} else if (data.endsWith(GF("+UUSOCL:"))) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### URC Sock Closed: ", mux);
return true;
}
return false;
}
private: // basically the same as waitResponse but without preemptive exiting
// (except when time runs out) this is used for +CGDCONT? as it can
// return multiple cid/apn configurations each terminated with OK\r\n
int8_t waitResponseUntilEndStream(uint32_t timeout_ms, String& data) {
data.reserve(1024); // buffer of the same size as in the SparkFun lib
uint8_t index = 0;
uint32_t startMillis = millis();
do {
TINY_GSM_YIELD();
while (stream.available() > 0) {
TINY_GSM_YIELD();
int8_t a = stream.read();
if (a <= 0) continue; // Skip 0x00 bytes, just in case
data += static_cast<char>(a);
}
} while (millis() - startMillis < timeout_ms);
return index;
}
public:
Stream& stream;
protected:
GsmClientSaraR5* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTSARAR5_H_

View file

@ -0,0 +1,692 @@
/**
* @file TinyGsmClientSequansMonarch.h
* @author Michael Krumpus
* @license LGPL-3.0
* @copyright Copyright (c) 2019 Michael Krumpus
* @date Jan 2019
*/
#ifndef SRC_TINYGSMCLIENTSEQUANSMONARCH_H_
#define SRC_TINYGSMCLIENTSEQUANSMONARCH_H_
// #define TINY_GSM_DEBUG Serial
#define TINY_GSM_MUX_COUNT 6
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "Sequans"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#define MODEM_MODEL "Monarch"
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmSSL.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmCalling.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmTemperature.tpp"
enum MonarchRegStatus {
REG_NO_RESULT = -1,
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 TinyGsmModem<TinyGsmSequansMonarch>,
public TinyGsmGPRS<TinyGsmSequansMonarch>,
public TinyGsmTCP<TinyGsmSequansMonarch, TINY_GSM_MUX_COUNT>,
public TinyGsmSSL<TinyGsmSequansMonarch, TINY_GSM_MUX_COUNT>,
public TinyGsmCalling<TinyGsmSequansMonarch>,
public TinyGsmSMS<TinyGsmSequansMonarch>,
public TinyGsmTime<TinyGsmSequansMonarch>,
public TinyGsmTemperature<TinyGsmSequansMonarch> {
friend class TinyGsmModem<TinyGsmSequansMonarch>;
friend class TinyGsmGPRS<TinyGsmSequansMonarch>;
friend class TinyGsmTCP<TinyGsmSequansMonarch, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSSL<TinyGsmSequansMonarch, TINY_GSM_MUX_COUNT>;
friend class TinyGsmCalling<TinyGsmSequansMonarch>;
friend class TinyGsmSMS<TinyGsmSequansMonarch>;
friend class TinyGsmTime<TinyGsmSequansMonarch>;
friend class TinyGsmTemperature<TinyGsmSequansMonarch>;
/*
* Inner Client
*/
public:
class GsmClientSequansMonarch : public GsmClient {
friend class TinyGsmSequansMonarch;
public:
GsmClientSequansMonarch() {}
explicit GsmClientSequansMonarch(TinyGsmSequansMonarch& modem,
uint8_t mux = 1) {
init(&modem, mux);
}
bool init(TinyGsmSequansMonarch* modem, uint8_t mux = 1) {
this->at = modem;
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
if (mux >= 1 && mux <= TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT) + 1;
}
at->sockets[this->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_OVERRIDES
void stop(uint32_t maxWaitMs) {
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+SQNSH="), mux);
sock_connected = false;
at->waitResponse();
}
void stop() override {
stop(15000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
public:
class GsmClientSecureSequansMonarch : public GsmClientSequansMonarch {
public:
GsmClientSecureSequansMonarch() {}
explicit GsmClientSecureSequansMonarch(TinyGsmSequansMonarch& modem,
uint8_t mux = 1)
: GsmClientSequansMonarch(modem, mux) {}
protected:
bool strictSSL = false;
public:
int connect(const char* host, uint16_t port, int timeout_s) override {
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;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void setStrictSSL(bool strict) {
strictSSL = strict;
}
};
/*
* Constructor
*/
public:
explicit TinyGsmSequansMonarch(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSequansMonarch"));
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();
DBG(GF("### Modem:"), getModemName());
// Make sure the module is enabled. Unlike others, the VZN20Q powers on
// with CFUN=0 not CFUN=1 (that is, at minimum functionality instead of full
// functionality The module cannot even detect the sim card if the cellular
// functionality is disabled so unless we explicitly enable the
// functionality the init will fail.
sendAT(GF("+CFUN=1"));
waitResponse();
// Disable time and time zone URC's
sendAT(GF("+CTZR=0"));
if (waitResponse(10000L) != 1) { return false; }
// Enable automatic time zome update
sendAT(GF("+CTZU=1"));
if (waitResponse(10000L) != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
bool factoryDefaultImpl() {
sendAT(GF("&F0")); // Factory
waitResponse();
sendAT(GF("Z")); // default configuration
waitResponse();
sendAT(GF("+IPR=0")); // Auto-baud
return waitResponse() == 1;
}
void maintainImpl() {
for (int mux = 1; mux <= TINY_GSM_MUX_COUNT; mux++) {
GsmClientSequansMonarch* 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, nullptr, nullptr); }
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
sendAT(GF("+CFUN=0"));
int8_t 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(pin);
}
bool powerOffImpl() {
// NOTE: The only way to turn the modem back on after this shutdown is with
// a hard reset
sendAT(GF("+SQNSSHDN"));
return waitResponse();
}
// 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 sleepEnableImpl(bool enable = true) {
sendAT(GF("+SQNIPSCFG="), enable);
return waitResponse() == 1;
}
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false)
TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Generic network functions
*/
public:
MonarchRegStatus getRegistrationStatus() {
return (MonarchRegStatus)getRegistrationStatusXREG("CEREG");
}
protected:
bool isNetworkConnectedImpl() {
MonarchRegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
String getLocalIPImpl() {
sendAT(GF("+CGPADDR=3"));
if (waitResponse(10000L, GF("+CGPADDR: 3,\"")) != 1) { return ""; }
String res = stream.readStringUntil('\"');
waitResponse();
return res;
}
/*
* Secure socket layer (SSL) functions
*/
// Follows functions as inherited from TinyGsmSSL.tpp
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
gprsDisconnect();
// Define the PDP context (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 gprsDisconnectImpl() {
// Detach from PS network
sendAT(GF("+CGATT=0"));
if (waitResponse(60000L) != 1) { return false; }
// Dectivate all PDP contexts
sendAT(GF("+CGACT=0"));
if (waitResponse(60000L) != 1) { return false; }
return true;
}
/*
* SIM card functions
*/
protected:
String getSimCCIDImpl() {
sendAT(GF("+SQNCCID"));
if (waitResponse(GF(AT_NL "+SQNCCID:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Phone Call functions
*/
protected:
bool callAnswerImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
bool dtmfSendImpl(char cmd,
int duration_ms = 100) TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
/*
* GSM Location functions
*/
// No functions of this type supported
/*
* GPS/GNSS/GLONASS location functions
*/
// No functions of this type supported
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
// No functions of this type supportedp
/*
* Temperature functions
*/
protected:
float getTemperatureImpl() {
sendAT(GF("+SMDTH"));
if (waitResponse(10000L, GF("+SMDTH: ")) != 1) {
return static_cast<float>(-9999);
}
String res;
if (waitResponse(1000L, res) != 1) { return static_cast<float>(-9999); }
if (res.indexOf("ERROR") >= 0) { return static_cast<float>(-9999); }
return res.toFloat();
}
/*
* Client related functions
*/
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux,
bool ssl = false, int timeout_s = 75) {
int8_t rsp;
uint32_t 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 for hex)
// <keepalive1> = unused = 0
// <listenAutoRsp1> = Listen auto-response mode = 0 - deactivated
// <sendDataMode1> = Send data mode = 1 - data as hex (0 for text)
sendAT(GF("+SQNSCFGEXT="), mux, GF(",1,0,0,0,1"));
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" AT_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, ',', (uint16_t)len);
waitResponse(10000L, GF(AT_NL "> "));
// Translate bytes into char to be able to send them as an hex string
char char_command[3];
for (size_t i = 0; i < len; i++) {
memset(&char_command, 0, sizeof(char_command));
sprintf(&char_command[0], "%02X",
reinterpret_cast<const uint8_t*>(buff)[i]);
stream.write(char_command, sizeof(char_command));
}
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(AT_NL "> ")) == 1) {
// gotPrompt = true;
// }
// nAttempts--;
// delay(50);
// }
// if (gotPrompt) {
// stream.write(reinterpret_cast<const uint8_t*>(buff), len);
// stream.write(reinterpret_cast<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, ',', (uint16_t)size);
if (waitResponse(GF("+SQNSRECV: ")) != 1) { return 0; }
streamSkipUntil(','); // Skip mux
int16_t len = streamGetIntBefore('\n');
for (int 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 = streamGetIntBefore(','); // 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(AT_NL "+SQNSS: ")) != 2) { break; }
uint8_t status = 0;
// if (streamGetIntBefore(',') != 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,
GsmClientSequansMonarch* sock = sockets[muxNo % TINY_GSM_MUX_COUNT];
if (sock) {
sock->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;
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF(AT_NL "+SQNSRING:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore('\n');
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);
return true;
} else if (data.endsWith(GF("SQNSH: "))) {
int8_t mux = streamGetIntBefore('\n');
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);
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientSequansMonarch* sockets[TINY_GSM_MUX_COUNT];
// AT_NL (\r\n) is not accepted with SQNSSENDEXT in data mode so use \n
const char* gsmNL = "\n";
};
#endif // SRC_TINYGSMCLIENTSEQUANSMONARCH_H_

View file

@ -0,0 +1,790 @@
/**
* @file TinyGsmClientUBLOX.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCLIENTUBLOX_H_
#define SRC_TINYGSMCLIENTUBLOX_H_
// #pragma message("TinyGSM: TinyGsmClientUBLOX")
// #define TINY_GSM_DEBUG Serial
#define TINY_GSM_MUX_COUNT 7
#define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
#ifdef AT_NL
#undef AT_NL
#endif
#define AT_NL "\r\n"
#ifdef MODEM_MANUFACTURER
#undef MODEM_MANUFACTURER
#endif
#define MODEM_MANUFACTURER "u-blox"
#ifdef MODEM_MODEL
#undef MODEM_MODEL
#endif
#define MODEM_MODEL "unknown"
#include "TinyGsmModem.tpp"
#include "TinyGsmTCP.tpp"
#include "TinyGsmSSL.tpp"
#include "TinyGsmGPRS.tpp"
#include "TinyGsmCalling.tpp"
#include "TinyGsmSMS.tpp"
#include "TinyGsmGSMLocation.tpp"
#include "TinyGsmGPS.tpp"
#include "TinyGsmTime.tpp"
#include "TinyGsmBattery.tpp"
enum UBLOXRegStatus {
REG_NO_RESULT = -1,
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmUBLOX : public TinyGsmModem<TinyGsmUBLOX>,
public TinyGsmGPRS<TinyGsmUBLOX>,
public TinyGsmTCP<TinyGsmUBLOX, TINY_GSM_MUX_COUNT>,
public TinyGsmSSL<TinyGsmUBLOX, TINY_GSM_MUX_COUNT>,
public TinyGsmCalling<TinyGsmUBLOX>,
public TinyGsmSMS<TinyGsmUBLOX>,
public TinyGsmGSMLocation<TinyGsmUBLOX>,
public TinyGsmGPS<TinyGsmUBLOX>,
public TinyGsmTime<TinyGsmUBLOX>,
public TinyGsmBattery<TinyGsmUBLOX> {
friend class TinyGsmModem<TinyGsmUBLOX>;
friend class TinyGsmGPRS<TinyGsmUBLOX>;
friend class TinyGsmTCP<TinyGsmUBLOX, TINY_GSM_MUX_COUNT>;
friend class TinyGsmSSL<TinyGsmUBLOX, TINY_GSM_MUX_COUNT>;
friend class TinyGsmCalling<TinyGsmUBLOX>;
friend class TinyGsmSMS<TinyGsmUBLOX>;
friend class TinyGsmGSMLocation<TinyGsmUBLOX>;
friend class TinyGsmGPS<TinyGsmUBLOX>;
friend class TinyGsmTime<TinyGsmUBLOX>;
friend class TinyGsmBattery<TinyGsmUBLOX>;
/*
* Inner Client
*/
public:
class GsmClientUBLOX : public GsmClient {
friend class TinyGsmUBLOX;
public:
GsmClientUBLOX() {}
explicit GsmClientUBLOX(TinyGsmUBLOX& modem, uint8_t mux = 0) {
init(&modem, mux);
}
bool init(TinyGsmUBLOX* modem, uint8_t mux = 0) {
this->at = modem;
sock_available = 0;
prev_check = 0;
sock_connected = false;
got_data = false;
if (mux < TINY_GSM_MUX_COUNT) {
this->mux = mux;
} else {
this->mux = (mux % TINY_GSM_MUX_COUNT);
}
at->sockets[this->mux] = this;
return true;
}
public:
virtual int connect(const char* host, uint16_t port, int timeout_s) {
// stop(); // DON'T stop!
TINY_GSM_YIELD();
rx.clear();
uint8_t oldMux = mux;
sock_connected = at->modemConnect(host, port, &mux, false, timeout_s);
if (mux != oldMux) {
DBG("WARNING: Mux number changed from", oldMux, "to", mux);
at->sockets[oldMux] = nullptr;
}
at->sockets[mux] = this;
at->maintain();
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
void stop(uint32_t maxWaitMs) {
dumpModemBuffer(maxWaitMs);
at->sendAT(GF("+USOCL="), mux);
at->waitResponse(); // should return within 1s
sock_connected = false;
}
void stop() override {
stop(15000L);
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
/*
* Inner Secure Client
*/
public:
class GsmClientSecureUBLOX : public GsmClientUBLOX {
public:
GsmClientSecureUBLOX() {}
explicit GsmClientSecureUBLOX(TinyGsmUBLOX& modem, uint8_t mux = 0)
: GsmClientUBLOX(modem, mux) {}
int connect(const char* host, uint16_t port, int timeout_s) override {
// stop(); // DON'T stop!
TINY_GSM_YIELD();
rx.clear();
uint8_t oldMux = mux;
sock_connected = at->modemConnect(host, port, &mux, true, timeout_s);
if (mux != oldMux) {
DBG("WARNING: Mux number changed from", oldMux, "to", mux);
at->sockets[oldMux] = nullptr;
}
at->sockets[mux] = this;
at->maintain();
return sock_connected;
}
TINY_GSM_CLIENT_CONNECT_OVERRIDES
};
/*
* Constructor
*/
public:
explicit TinyGsmUBLOX(Stream& stream) : stream(stream) {
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
protected:
bool initImpl(const char* pin = nullptr) {
DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
DBG(GF("### TinyGSM Compiled Module: TinyGsmClientUBLOX"));
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();
DBG(GF("### Modem:"), getModemName());
// Enable automatic time zome update
sendAT(GF("+CTZU=1"));
waitResponse(10000L);
// Ignore the response, in case the network doesn't support it.
// if (waitResponse(10000L) != 1) { return false; }
SimStatus ret = getSimStatus();
// if the sim isn't ready and a pin has been provided, try to unlock the sim
if (ret != SIM_READY && pin != nullptr && strlen(pin) > 0) {
simUnlock(pin);
return (getSimStatus() == SIM_READY);
} else {
// if the sim is ready, or it's locked but no pin has been provided,
// return true
return (ret == SIM_READY || ret == SIM_LOCKED);
}
}
// only difference in implementation is the warning on the wrong type
String getModemNameImpl() {
String manufacturer = getModemManufacturer();
String model = getModemModel();
String name = manufacturer + String(" ") + model;
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!");
}
return name;
}
bool factoryDefaultImpl() {
sendAT(GF("+UFACTORY=0,1")); // No factory restore, erase NVM
waitResponse();
return setPhoneFunctionality(16); // Reset
}
/*
* Power functions
*/
protected:
bool restartImpl(const char* pin = nullptr) {
if (!testAT()) { return false; }
if (!setPhoneFunctionality(16)) { return false; }
delay(3000); // TODO(?): Verify delay timing here
return init(pin);
}
bool powerOffImpl() {
sendAT(GF("+CPWROFF"));
return waitResponse(40000L) == 1;
}
bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
return waitResponse(10000L) == 1;
}
/*
* Generic network functions
*/
public:
UBLOXRegStatus getRegistrationStatus() {
return (UBLOXRegStatus)getRegistrationStatusXREG("CGREG");
}
bool setRadioAccessTecnology(int selected, int preferred) {
// selected:
// 0: GSM / GPRS / eGPRS (single mode)
// 1: GSM / UMTS (dual mode)
// 2: UMTS (single mode)
// 3: LTE (single mode)
// 4: GSM / UMTS / LTE (tri mode)
// 5: GSM / LTE (dual mode)
// 6: UMTS / LTE (dual mode)
// preferred:
// 0: GSM / GPRS / eGPRS
// 2: UTRAN
// 3: LTE
sendAT(GF("+URAT="), selected, GF(","), preferred);
if (waitResponse() != 1) { return false; }
return true;
}
bool getCurrentRadioAccessTecnology(int&) {
// @TODO
return false;
}
protected:
bool isNetworkConnectedImpl() {
UBLOXRegStatus 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;
}
String getLocalIPImpl() {
sendAT(GF("+UPSND=0,0"));
if (waitResponse(GF(AT_NL "+UPSND:")) != 1) { return ""; }
streamSkipUntil(','); // Skip PSD profile
streamSkipUntil('\"'); // Skip request type
String res = stream.readStringUntil('\"');
if (waitResponse() != 1) { return ""; }
return res;
}
/*
* Secure socket layer (SSL) functions
*/
// Follows functions as inherited from TinyGsmSSL.tpp
/*
* WiFi functions
*/
// No functions of this type supported
/*
* GPRS functions
*/
protected:
bool gprsConnectImpl(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
// gprsDisconnect();
sendAT(GF("+CGATT=1")); // attach to GPRS
if (waitResponse(360000L) != 1) { return false; }
// Setting up the PSD profile/PDP context with the UPSD commands sets up an
// "internal" PDP context, i.e. a data connection using the internal IP
// stack and related AT commands for sockets.
// Packet switched data configuration
// AT+UPSD=<profile_id>,<param_tag>,<param_val>
// profile_id = 0 - PSD profile identifier, in range 0-6 (NOT PDP context)
// param_tag = 1: APN
// param_tag = 2: username
// param_tag = 3: password
// param_tag = 7: IP address Note: IP address set as "0.0.0.0" means
// dynamic IP address assigned during PDP context activation
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
waitResponse();
// Packet switched data action
// AT+UPSDA=<profile_id>,<action>
// profile_id = 0: PSD profile identifier, in range 0-6 (NOT PDP context)
// action = 3: activate; it activates a PDP context with the specified
// profile, using the current parameters
sendAT(GF(
"+UPSDA=0,3")); // Activate the PDP context associated with profile 0
if (waitResponse(360000L) != 1) { // Should return ok
return false;
}
// Packet switched network-assigned data - Returns the current (dynamic)
// network-assigned or network-negotiated value of the specified parameter
// for the active PDP context associated with the specified PSD profile.
// AT+UPSND=<profile_id>,<param_tag>
// profile_id = 0: PSD profile identifier, in range 0-6 (NOT PDP context)
// param_tag = 8: PSD profile status: if the profile is active the return
// value is 1, 0 otherwise
sendAT(GF("+UPSND=0,8")); // Check if PSD profile 0 is now active
int8_t res = waitResponse(GF(",8,1"), GF(",8,0"));
waitResponse(); // Should return another OK
if (res == 1) {
return true; // It's now active
} else if (res == 2) { // If it's not active yet, wait for the +UUPSDA URC
if (waitResponse(180000L, GF("+UUPSDA: 0")) != 1) { // 0=successful
return false;
}
streamSkipUntil('\n'); // Ignore the IP address, if returned
} else {
return false;
}
return true;
}
bool gprsDisconnectImpl() {
sendAT(GF(
"+UPSDA=0,4")); // Deactivate the PDP context associated with profile 0
if (waitResponse(360000L) != 1) { return false; }
sendAT(GF("+CGATT=0")); // detach from GPRS
if (waitResponse(360000L) != 1) { return false; }
return true;
}
/*
* SIM card functions
*/
protected:
// This uses "CGSN" instead of "GSN"
String getIMEIImpl() {
sendAT(GF("+CGSN"));
if (waitResponse(GF(AT_NL)) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Phone Call functions
*/
// Follows all phone call functions as inherited from TinyGsmCalling.tpp
/*
* Audio functions
*/
// No functions of this type supported
/*
* Text messaging (SMS) functions
*/
// Follows all text messaging (SMS) functions as inherited from TinyGsmSMS.tpp
/*
* GSM/GPS/GNSS/GLONASS Location functions
* NOTE: u-blox modules use the same function to get location data from both
* GSM tower triangulation and from dedicated GPS/GNSS/GLONASS receivers. The
* only difference in which sensor the data is requested from. If a GNSS
* location is requested from a modem without a GNSS receiver installed on the
* I2C port, the GSM-based "Cell Locate" location will be returned instead.
*/
protected:
bool enableGPSImpl() {
// AT+UGPS=<mode>[,<aid_mode>[,<GNSS_systems>]]
// <mode> - 0: GNSS receiver powered off, 1: on
// <aid_mode> - 0: no aiding (default)
// <GNSS_systems> - 3: GPS + SBAS (default)
sendAT(GF("+UGPS=1,0,3"));
if (waitResponse(10000L, GF(AT_NL "+UGPS:")) != 1) { return false; }
return waitResponse(10000L) == 1;
}
bool disableGPSImpl() {
sendAT(GF("+UGPS=0"));
if (waitResponse(10000L, GF(AT_NL "+UGPS:")) != 1) { return false; }
return waitResponse(10000L) == 1;
}
String inline getUbloxLocationRaw(int8_t sensor) {
// AT+ULOC=<mode>,<sensor>,<response_type>,<timeout>,<accuracy>
// <mode> - 2: single shot position
// <sensor> - 0: use the last fix in the internal database and stop the GNSS
// receiver
// - 1: use the GNSS receiver for localization
// - 2: use cellular CellLocate location information
// - 3: ?? use the combined GNSS receiver and CellLocate service
// information ?? - Docs show using sensor 3 and it's
// documented for the +UTIME command but not for +ULOC
// <response_type> - 0: standard (single-hypothesis) response
// <timeout> - Timeout period in seconds
// <accuracy> - Target accuracy in meters (1 - 999999)
sendAT(GF("+ULOC=2,"), sensor, GF(",0,120,1"));
// wait for first "OK"
if (waitResponse(10000L) != 1) { return ""; }
// wait for the final result - wait full timeout time
if (waitResponse(120000L, GF(AT_NL "+UULOC:")) != 1) { return ""; }
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
String getGsmLocationRawImpl() {
return getUbloxLocationRaw(2);
}
String getGPSrawImpl() {
return getUbloxLocationRaw(1);
}
inline bool getUbloxLocation(int8_t sensor, float* lat, float* lon,
float* speed = 0, float* alt = 0, int* vsat = 0,
int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0,
int* hour = 0, int* minute = 0,
int* second = 0) {
// AT+ULOC=<mode>,<sensor>,<response_type>,<timeout>,<accuracy>
// <mode> - 2: single shot position
// <sensor> - 2: use cellular CellLocate location information
// - 0: use the last fix in the internal database and stop the GNSS
// receiver
// - 1: use the GNSS receiver for localization
// - 3: ?? use the combined GNSS receiver and CellLocate service
// information ?? - Docs show using sensor 3 and it's documented
// for the +UTIME command but not for +ULOC
// <response_type> - 0: standard (single-hypothesis) response
// <timeout> - Timeout period in seconds
// <accuracy> - Target accuracy in meters (1 - 999999)
sendAT(GF("+ULOC=2,"), sensor, GF(",0,120,1"));
// wait for first "OK"
if (waitResponse(10000L) != 1) { return false; }
// wait for the final result - wait full timeout time
if (waitResponse(120000L, GF(AT_NL "+UULOC: ")) != 1) { return false; }
// +UULOC: <date>, <time>, <lat>, <long>, <alt>, <uncertainty>, <speed>,
// <direction>, <vertical_acc>, <sensor_used>, <SV_used>, <antenna_status>,
// <jamming_status>
// init variables
float ilat = 0;
float ilon = 0;
float ispeed = 0;
float ialt = 0;
int iusat = 0;
float iaccuracy = 0;
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
float secondWithSS = 0;
// Date & Time
iday = streamGetIntBefore('/'); // Two digit day
imonth = streamGetIntBefore('/'); // Two digit month
iyear = streamGetIntBefore(','); // Four digit year
ihour = streamGetIntBefore(':'); // Two digit hour
imin = streamGetIntBefore(':'); // Two digit minute
secondWithSS = streamGetFloatBefore(','); // 6 digit second with subseconds
ilat = streamGetFloatBefore(','); // Estimated latitude, in degrees
ilon = streamGetFloatBefore(','); // Estimated longitude, in degrees
ialt = streamGetFloatBefore(
','); // Estimated altitude, in meters - only forGNSS
// positioning, 0 in case of CellLocate
if (ialt != 0) { // values not returned for CellLocate
iaccuracy =
streamGetFloatBefore(','); // Maximum possible error, in meters
ispeed = streamGetFloatBefore(','); // Speed over ground m/s3
streamSkipUntil(','); // Course over ground in degree (0 deg - 360 deg)
streamSkipUntil(','); // Vertical accuracy, in meters
streamSkipUntil(','); // Sensor used for the position calculation
iusat = streamGetIntBefore(','); // Number of satellite used
streamSkipUntil(','); // Antenna status
streamSkipUntil('\n'); // Jamming status
} else {
iaccuracy =
streamGetFloatBefore('\n'); // Maximum possible error, in meters
}
// Set pointers
if (lat != nullptr) *lat = ilat;
if (lon != nullptr) *lon = ilon;
if (speed != nullptr) *speed = ispeed;
if (alt != nullptr) *alt = ialt;
if (vsat != nullptr)
*vsat = 0; // Number of satellites viewed not reported;
if (usat != nullptr) *usat = iusat;
if (accuracy != nullptr) *accuracy = iaccuracy;
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = static_cast<int>(secondWithSS);
// final ok
waitResponse();
return true;
}
bool getGsmLocationImpl(float* lat, float* lon, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0,
int* hour = 0, int* minute = 0, int* second = 0) {
return getUbloxLocation(2, lat, lon, 0, 0, 0, 0, accuracy, year, month, day,
hour, minute, second);
}
bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0,
int* vsat = 0, int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
int* minute = 0, int* second = 0) {
return getUbloxLocation(1, lat, lon, speed, alt, vsat, usat, accuracy, year,
month, day, hour, minute, second);
}
/*
* Time functions
*/
// Follows all clock functions as inherited from TinyGsmTime.tpp
/*
* NTP server functions
*/
// No functions of this type supported
/*
* BLE functions
*/
// No functions of this type supported
/*
* Battery functions
*/
protected:
int16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
int8_t getBattPercentImpl() {
sendAT(GF("+CIND?"));
if (waitResponse(GF(AT_NL "+CIND:")) != 1) { return 0; }
int8_t res = streamGetIntBefore(',');
int8_t percent = res * 20; // return is 0-5
// Wait for final OK
waitResponse();
return percent;
}
int8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
bool getBattStatsImpl(int8_t& chargeState, int8_t& percent,
int16_t& milliVolts) {
chargeState = 0;
percent = getBattPercent();
milliVolts = 0;
return true;
}
/*
* Temperature functions
*/
// This would only available for a small number of modules in this group
// (TOBY-L)
float getTemperatureImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Client related functions
*/
protected:
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;
uint32_t startMillis = millis();
// create a socket
sendAT(GF("+USOCR=6"));
// reply is +USOCR: ## of socket created
if (waitResponse(GF(AT_NL "+USOCR:")) != 1) { return false; }
*mux = streamGetIntBefore('\n');
waitResponse();
if (ssl) {
sendAT(GF("+USOSEC="), *mux, ",1");
waitResponse();
}
// Enable NODELAY
// AT+USOSO=<socket>,<level>,<opt_name>,<opt_val>[,<opt_val2>]
// <level> - 0 for IP, 6 for TCP, 65535 for socket level options
// <opt_name> TCP/1 = no delay (do not delay send to coalesce packets)
// NOTE: Enabling this may increase data plan usage
// 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);
int8_t rsp = waitResponse(timeout_ms - (millis() - startMillis));
return (1 == rsp);
}
int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
sendAT(GF("+USOWR="), mux, ',', (uint16_t)len);
if (waitResponse(GF("@")) != 1) { return 0; }
// 50ms delay, see AT manual section 25.10.4
delay(50);
stream.write(reinterpret_cast<const uint8_t*>(buff), len);
stream.flush();
if (waitResponse(GF(AT_NL "+USOWR:")) != 1) { return 0; }
streamSkipUntil(','); // Skip mux
int16_t sent = streamGetIntBefore('\n');
waitResponse(); // sends back OK after the confirmation of number sent
return sent;
}
size_t modemRead(size_t size, uint8_t mux) {
if (!sockets[mux]) return 0;
sendAT(GF("+USORD="), mux, ',', (uint16_t)size);
if (waitResponse(GF(AT_NL "+USORD:")) != 1) { return 0; }
streamSkipUntil(','); // Skip mux
int16_t len = streamGetIntBefore(',');
streamSkipUntil('\"');
for (int i = 0; i < len; i++) { moveCharFromStreamToFifo(mux); }
streamSkipUntil('\"');
waitResponse();
// DBG("### READ:", len, "from", mux);
sockets[mux]->sock_available = modemGetAvailable(mux);
return len;
}
size_t modemGetAvailable(uint8_t mux) {
if (!sockets[mux]) return 0;
// 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(AT_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 = streamGetIntBefore('\n');
// if (result) DBG("### DATA AVAILABLE:", result, "on", mux);
waitResponse();
}
if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
// DBG("### AvailablE:", result, "on", 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(AT_NL "+USOCTL:"));
if (res != 1) { return false; }
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip type
int8_t result = streamGetIntBefore('\n');
// 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);
}
/*
* Utilities
*/
public:
bool handleURCs(String& data) {
if (data.endsWith(GF("+UUSORD:"))) {
int8_t mux = streamGetIntBefore(',');
int16_t len = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
// max size is 1024
if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
}
data = "";
// DBG("### URC Data Received:", len, "on", mux);
return true;
} else if (data.endsWith(GF("+UUSOCL:"))) {
int8_t mux = streamGetIntBefore('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### URC Sock Closed: ", mux);
return true;
}
return false;
}
public:
Stream& stream;
protected:
GsmClientUBLOX* sockets[TINY_GSM_MUX_COUNT];
};
#endif // SRC_TINYGSMCLIENTUBLOX_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,142 @@
/**
* @file TinyGsmCommon.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMCOMMON_H_
#define SRC_TINYGSMCOMMON_H_
// The current library version number
#define TINYGSM_VERSION "0.12.0"
#if defined(SPARK) || defined(PARTICLE)
#include "Particle.h"
#elif defined(ARDUINO)
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#endif
#if defined(ARDUINO_DASH)
#include <ArduinoCompat/Client.h>
#else
#include <Client.h>
#endif
#ifndef TINY_GSM_YIELD_MS
#define TINY_GSM_YIELD_MS 0
#endif
#ifndef TINY_GSM_YIELD
#define TINY_GSM_YIELD() \
{ delay(TINY_GSM_YIELD_MS); }
#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")))
#if defined(__AVR__) && !defined(__AVR_ATmega4809__)
#define TINY_GSM_PROGMEM PROGMEM
typedef const __FlashStringHelper* GsmConstStr;
#define GFP(x) (reinterpret_cast<GsmConstStr>(x))
#define GF(x) F(x)
#else
#define TINY_GSM_PROGMEM
typedef const char* GsmConstStr;
#define GFP(x) x
#define GF(x) x
#endif
#ifdef TINY_GSM_DEBUG
namespace {
template <typename T>
static void DBG_PLAIN(T last) {
TINY_GSM_DEBUG.println(last);
}
template <typename T, typename... Args>
static void DBG_PLAIN(T head, Args... tail) {
TINY_GSM_DEBUG.print(head);
TINY_GSM_DEBUG.print(' ');
DBG_PLAIN(tail...);
}
template <typename... Args>
static void DBG(Args... args) {
TINY_GSM_DEBUG.print(GF("["));
TINY_GSM_DEBUG.print(millis());
TINY_GSM_DEBUG.print(GF("] "));
DBG_PLAIN(args...);
}
} // namespace
#else
#define DBG_PLAIN(...)
#define DBG(...)
#endif
/*
* CRTP Helper
*/
template <typename modemType, template <typename> class crtpType>
struct tinygsm_crtp {
modemType& thisModem() {
return static_cast<modemType&>(*this);
}
modemType const& thisModem() const {
return static_cast<modemType const&>(*this);
}
private:
tinygsm_crtp() {}
friend crtpType<modemType>;
};
/*
* Min/Max Helpers
*/
template <class T>
const T& TinyGsmMin(const T& a, const T& b) {
return (b < a) ? b : a;
}
template <class T>
const T& TinyGsmMax(const T& a, const T& b) {
return (b < a) ? a : b;
}
/*
* Automatically find baud rate
*/
template <class T>
uint32_t TinyGsmAutoBaud(T& SerialAT, uint32_t minimum = 9600,
uint32_t maximum = 115200) {
static uint32_t rates[] = {115200, 57600, 38400, 19200, 9600, 74400, 74880,
230400, 460800, 2400, 4800, 14400, 28800};
for (uint8_t i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) {
uint32_t rate = rates[i];
if (rate < minimum || rate > maximum) continue;
DBG("Trying baud rate", rate, "...");
SerialAT.begin(rate);
delay(10);
for (int j = 0; j < 10; j++) {
SerialAT.print("AT\r\n");
String input = SerialAT.readString();
if (input.indexOf("OK") >= 0) {
DBG("Modem responded at rate", rate);
return rate;
}
}
}
SerialAT.begin(minimum);
return 0;
}
#endif // SRC_TINYGSMCOMMON_H_

View file

@ -0,0 +1,167 @@
#ifndef TinyGsmFifo_h
#define TinyGsmFifo_h
template <class T, unsigned N>
class TinyGsmFifo {
public:
/**
* @brief Construct a new Tiny Gsm Fifo object, setting the head and tail to
* 0.
*/
TinyGsmFifo() {
clear();
}
/**
* @brief Clear the FIFO - set the read and write positions to 0
*/
void clear() {
_r = 0;
_w = 0;
}
// writing thread/context API
//-------------------------------------------------------------
/**
* @brief Check if the buffer is writable - that is if it has any space left
*
* @return *true* The buffer has free space.
* @return *false* There is no space left in the buffer.
*/
bool writeable(void) {
return free() > 0;
}
/**
* @brief Check the number of free positions in the buffer.
*
* @return *int* The number number of free positions in the buffer
*/
int free(void) {
int s = _r - _w; // Check if the read is ahead of the write
if (s <= 0) s += N; // if not wrap
return s - 1; // return the difference between r and w, accounting for wrap
}
/**
* @brief Add a single item to the buffer. This is non-blocking.
*
* @param c Reference of the item of type 'T' to add to the buffer
* @return *true* The item was successfully added to the buffer
* @return *false* Nothing was added to the buffer
*/
bool put(const T& c) {
int i = _w; // check the write position
int j = i; // set the spot for the new item to the write position
i = _inc(i); // check where the next increment of the write will be
if (i == _r) // make sure the next spot isn't the position of the read (ie,
// the buffer is full)
return false;
_b[j] = c; // add the item at position j
_w = i; // bump the write position
return true;
}
/**
* @brief Add multiple items to be buffer
*
* @param p Pointer to the items to add
* @param n The number of items to add
* @param t Whether to block while waiting for space enough space to clear to
* add all items
* @return *int* The number of items successfully added
*/
int put(const T* p, int n, bool t = false) {
int c = n;
while (c) {
int f;
while ((f = free()) == 0) // wait for space
{
if (!t) return n - c; // no more space and not blocking
/* nothing / just wait */;
}
// check free space
if (c < f) f = c;
int w = _w;
int m = N - w;
// check wrap
if (f > m) f = m;
memcpy(&_b[w], p, f);
_w = _inc(w, f);
c -= f;
p += f;
}
return n - c;
}
// reading thread/context API
// --------------------------------------------------------
bool readable(void) {
return (_r != _w);
}
size_t size(void) {
int s = _w - _r;
if (s < 0) s += N;
return s;
}
bool get(T* p) {
int r = _r;
if (r == _w) // !readable()
return false;
*p = _b[r];
_r = _inc(r);
return true;
}
int get(T* p, int n, bool t = false) {
int c = n;
while (c) {
int f;
for (;;) // wait for data
{
f = size();
if (f) break; // free space
if (!t) return n - c; // no space and not blocking
/* nothing / just wait */;
}
// check available data
if (c < f) f = c;
int r = _r;
int m = N - r;
// check wrap
if (f > m) f = m;
memcpy(p, &_b[r], f);
_r = _inc(r, f);
c -= f;
p += f;
}
return n - c;
}
uint8_t peek() {
return _b[_r];
}
private:
/**
* @brief Get the next increment spot in the buffer, accounting for the size
* of each item in the buffer
*
* @param i
* @param n
* @return *int*
*/
int _inc(int i, int n = 1) {
return (i + n) % N;
}
T _b[N]; /// The buffer, containing 'N' items of type 'T'
int _w; /// The write position in the buffer
int _r; /// The read position in the buffer
};
#endif

View file

@ -0,0 +1,190 @@
/**
* @file TinyGsmGPRS.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMGPRS_H_
#define SRC_TINYGSMGPRS_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_GPRS
enum SimStatus {
SIM_ERROR = 0,
SIM_READY = 1,
SIM_LOCKED = 2,
SIM_ANTITHEFT_LOCKED = 3,
};
template <class modemType>
class TinyGsmGPRS {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* SIM card functions
*/
// Unlocks the SIM
bool simUnlock(const char* pin) {
return thisModem().simUnlockImpl(pin);
}
// Gets the CCID of a sim card via AT+CCID
String getSimCCID() {
return thisModem().getSimCCIDImpl();
}
// Asks for TA Serial Number Identification (IMEI)
String getIMEI() {
return thisModem().getIMEIImpl();
}
// Asks for International Mobile Subscriber Identity IMSI
String getIMSI() {
return thisModem().getIMSIImpl();
}
SimStatus getSimStatus(uint32_t timeout_ms = 10000L) {
return thisModem().getSimStatusImpl(timeout_ms);
}
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = nullptr,
const char* pwd = nullptr) {
return thisModem().gprsConnectImpl(apn, user, pwd);
}
bool gprsDisconnect() {
return thisModem().gprsDisconnectImpl();
}
// Checks if current attached to GPRS/EPS service
bool isGprsConnected() {
return thisModem().isGprsConnectedImpl();
}
// Gets the current network operator
String getOperator() {
return thisModem().getOperatorImpl();
}
// Gets the current network provider
String getProvider() {
return thisModem().getProviderImpl();
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmGPRS() {}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* SIM card functions
*/
protected:
// Unlocks a sim via the 3GPP TS command AT+CPIN
bool simUnlockImpl(const char* pin) {
if (pin && strlen(pin) > 0) {
thisModem().sendAT(GF("+CPIN=\""), pin, GF("\""));
return thisModem().waitResponse() == 1;
}
return true;
}
// Gets the CCID of a sim card via AT+CCID
String getSimCCIDImpl() {
thisModem().sendAT(GF("+CCID"));
if (thisModem().waitResponse(GF("+CCID:")) != 1) { return ""; }
String res = thisModem().stream.readStringUntil('\n');
thisModem().waitResponse();
res.trim();
return res;
}
// Asks for TA Serial Number Identification (IMEI) via the V.25TER standard
// AT+GSN command
String getIMEIImpl() {
thisModem().sendAT(GF("+GSN"));
thisModem().streamSkipUntil('\n'); // skip first newline
String res = thisModem().stream.readStringUntil('\n');
thisModem().waitResponse();
res.trim();
return res;
}
// Asks for International Mobile Subscriber Identity IMSI via the AT+CIMI
// command
String getIMSIImpl() {
thisModem().sendAT(GF("+CIMI"));
thisModem().streamSkipUntil('\n'); // skip first newline
String res = thisModem().stream.readStringUntil('\n');
thisModem().waitResponse();
res.trim();
return res;
}
SimStatus getSimStatusImpl(uint32_t timeout_ms = 10000L) {
for (uint32_t start = millis(); millis() - start < timeout_ms;) {
thisModem().sendAT(GF("+CPIN?"));
if (thisModem().waitResponse(GF("+CPIN:")) != 1) {
delay(1000);
continue;
}
int8_t status =
thisModem().waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"),
GF("NOT INSERTED"), GF("NOT READY"));
thisModem().waitResponse();
switch (status) {
case 2:
case 3: return SIM_LOCKED;
case 1: return SIM_READY;
default: return SIM_ERROR;
}
}
return SIM_ERROR;
}
/*
* GPRS functions
*/
protected:
// Checks if current attached to GPRS/EPS service
bool isGprsConnectedImpl() {
thisModem().sendAT(GF("+CGATT?"));
if (thisModem().waitResponse(GF("+CGATT:")) != 1) { return false; }
int8_t res = thisModem().streamGetIntBefore('\n');
thisModem().waitResponse();
if (res != 1) { return false; }
return thisModem().localIP() != IPAddress(0, 0, 0, 0);
}
// Gets the current network operator via the 3GPP TS command AT+COPS
String getOperatorImpl() {
thisModem().sendAT(GF("+COPS?"));
if (thisModem().waitResponse(GF("+COPS:")) != 1) { return ""; }
thisModem().streamSkipUntil('"'); /* Skip mode and format */
String res = thisModem().stream.readStringUntil('"');
thisModem().waitResponse();
return res;
}
String getProviderImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
#endif // SRC_TINYGSMGPRS_H_

View file

@ -0,0 +1,94 @@
/**
* @file TinyGsmGPS.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMGPS_H_
#define SRC_TINYGSMGPS_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_GPS
template <class modemType>
class TinyGsmGPS {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* GPS/GNSS/GLONASS location functions
*/
bool enableGPS() {
return thisModem().enableGPSImpl();
}
bool disableGPS() {
return thisModem().disableGPSImpl();
}
String getGPSraw() {
return thisModem().getGPSrawImpl();
}
bool getGPS(float* lat, float* lon, float* speed = 0, float* alt = 0,
int* vsat = 0, int* usat = 0, float* accuracy = 0, int* year = 0,
int* month = 0, int* day = 0, int* hour = 0, int* minute = 0,
int* second = 0) {
return thisModem().getGPSImpl(lat, lon, speed, alt, vsat, usat, accuracy,
year, month, day, hour, minute, second);
}
bool getGPSTime(int* year, int* month, int* day, int* hour, int* minute,
int* second) {
float lat = 0;
float lon = 0;
return thisModem().getGPSImpl(&lat, &lon, 0, 0, 0, 0, 0, year, month, day,
hour, minute, second);
}
String setGNSSMode(uint8_t mode, bool dpo) {
return thisModem().setGNSSModeImpl(mode, dpo);
}
uint8_t getGNSSMode() {
return thisModem().getGNSSModeImpl();
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmGPS() {}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* GPS/GNSS/GLONASS location functions
*/
bool enableGPSImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool disableGPSImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
String getGPSrawImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0,
int* vsat = 0, int* usat = 0, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
int* minute = 0,
int* second = 0) TINY_GSM_ATTR_NOT_IMPLEMENTED;
String setGNSSModeImpl(uint8_t mode, bool dpo) TINY_GSM_ATTR_NOT_IMPLEMENTED;
uint8_t getGNSSModeImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
#endif // SRC_TINYGSMGPS_H_

View file

@ -0,0 +1,161 @@
/**
* @file TinyGsmGSMLocation.h
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMGSMLOCATION_H_
#define SRC_TINYGSMGSMLOCATION_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_GSM_LOCATION
template <class modemType>
class TinyGsmGSMLocation {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* GSM Location functions
*/
String getGsmLocationRaw() {
return thisModem().getGsmLocationRawImpl();
}
String getGsmLocation() {
return thisModem().getGsmLocationRawImpl();
}
bool getGsmLocation(float* lat, float* lon, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0,
int* hour = 0, int* minute = 0, int* second = 0) {
return thisModem().getGsmLocationImpl(lat, lon, accuracy, year, month, day,
hour, minute, second);
};
bool getGsmLocationTime(int* year, int* month, int* day, int* hour,
int* minute, int* second) {
float lat = 0;
float lon = 0;
float accuracy = 0;
return thisModem().getGsmLocation(&lat, &lon, &accuracy, year, month, day,
hour, minute, second);
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmGSMLocation() {}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* GSM Location functions
* Template is based on SIMCOM commands
*/
protected:
// String getGsmLocationImpl() {
// thisModem().sendAT(GF("+CIPGSMLOC=1,1"));
// if (thisModem().waitResponse(10000L, GF("+CIPGSMLOC:")) != 1) { return
// ""; } String res = thisModem().stream.readStringUntil('\n');
// thisModem().waitResponse();
// res.trim();
// return res;
// }
String getGsmLocationRawImpl() {
// AT+CLBS=<type>,<cid>
// <type> 1 = location using 3 cell's information
// 3 = get number of times location has been accessed
// 4 = Get longitude latitude and date time
thisModem().sendAT(GF("+CLBS=1,1"));
// Should get a location code of "0" indicating success
if (thisModem().waitResponse(120000L, GF("+CLBS: ")) != 1) { return ""; }
int8_t locationCode = thisModem().streamGetIntLength(2);
// 0 = success, else, error
if (locationCode != 0) {
thisModem().waitResponse(); // should be an ok after the error
return "";
}
String res = thisModem().stream.readStringUntil('\n');
thisModem().waitResponse();
res.trim();
return res;
}
bool getGsmLocationImpl(float* lat, float* lon, float* accuracy = 0,
int* year = 0, int* month = 0, int* day = 0,
int* hour = 0, int* minute = 0, int* second = 0) {
// AT+CLBS=<type>,<cid>
// <type> 1 = location using 3 cell's information
// 3 = get number of times location has been accessed
// 4 = Get longitude latitude and date time
thisModem().sendAT(GF("+CLBS=4,1"));
// Should get a location code of "0" indicating success
if (thisModem().waitResponse(120000L, GF("+CLBS: ")) != 1) { return false; }
int8_t locationCode = thisModem().streamGetIntLength(2);
// 0 = success, else, error
if (locationCode != 0) {
thisModem().waitResponse(); // should be an ok after the error
return false;
}
// init variables
float ilat = 0;
float ilon = 0;
float iaccuracy = 0;
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
int isec = 0;
ilat = thisModem().streamGetFloatBefore(','); // Latitude
ilon = thisModem().streamGetFloatBefore(','); // Longitude
iaccuracy = thisModem().streamGetIntBefore(','); // Positioning accuracy
// Date & Time
iyear = thisModem().streamGetIntBefore('/');
imonth = thisModem().streamGetIntBefore('/');
iday = thisModem().streamGetIntBefore(',');
ihour = thisModem().streamGetIntBefore(':');
imin = thisModem().streamGetIntBefore(':');
isec = thisModem().streamGetIntBefore('\n');
// Set pointers
if (lat != nullptr) *lat = ilat;
if (lon != nullptr) *lon = ilon;
if (accuracy != nullptr) *accuracy = iaccuracy;
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = isec;
// Final OK
thisModem().waitResponse();
return true;
}
};
#endif // SRC_TINYGSMGSMLOCATION_H_

View file

@ -0,0 +1,803 @@
/**
* @file TinyGsmModem.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMMODEM_H_
#define SRC_TINYGSMMODEM_H_
#include "TinyGsmCommon.h"
#ifndef AT_NL
#define AT_NL "\r\n"
#endif
#ifndef AT_OK
#define AT_OK "OK"
#endif
#ifndef AT_ERROR
#define AT_ERROR "ERROR"
#endif
#if defined TINY_GSM_DEBUG
#ifndef AT_VERBOSE
#define AT_VERBOSE "+CME ERROR:"
#endif
#ifndef AT_VERBOSE_2
#define AT_VERBOSE_2 "+CMS ERROR:"
#endif
#endif
#ifndef MODEM_MANUFACTURER
#define MODEM_MANUFACTURER "unknown"
#endif
#ifndef MODEM_MODEL
#define MODEM_MODEL "unknown"
#endif
static const char GSM_OK[] TINY_GSM_PROGMEM = AT_OK AT_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = AT_ERROR AT_NL;
#if defined TINY_GSM_DEBUG
static const char GSM_VERBOSE[] TINY_GSM_PROGMEM = AT_VERBOSE;
static const char GSM_VERBOSE_2[] TINY_GSM_PROGMEM = AT_VERBOSE_2;
#endif
template <class modemType>
class TinyGsmModem {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/**
* @anchor basic_functions
* @name Basic functions
*/
/**@{*/
/**
* @brief Sets up the GSM module
*
* @param pin A pin code to unlock the SIM, if necessary
*
* @return *true* The module was set up as expected
* @return *false* Something failed in module set up
*/
bool begin(const char* pin = nullptr) {
return thisModem().initImpl(pin);
}
/**
* @copydoc TinyGsmModem::begin()
*/
bool init(const char* pin = nullptr) {
return thisModem().initImpl(pin);
}
/**
* @brief Recursive variadic template to send AT commands
*
* @tparam Args
* @param cmd The commands to send
*/
template <typename... Args>
inline void sendAT(Args... cmd) {
thisModem().streamWrite("AT", cmd..., AT_NL);
thisModem().stream.flush();
TINY_GSM_YIELD(); /* DBG("### AT:", cmd...); */
}
/**
* @brief Set the module baud rate
*
* @param baud The baud rate the use
*
* @note After setting and applying the new baud rate, you will have to end()
* and begin() the serial object.
*/
bool setBaud(uint32_t baud) {
return thisModem().setBaudImpl(baud);
}
/**
* @brief Test response to AT commands
*
* @param timeout_ms The the amount of time to test for; optional with a
* default value of 10s.
* @return *true* The module responeded to AT commands
* @return *false* The module failed to respond
*/
bool testAT(uint32_t timeout_ms = 10000L) {
return thisModem().testATImpl(timeout_ms);
}
/**
* @brief Listen for responses to commands and handle URCs
*
* @param timeout_ms The time to wait for a response
* @param data A string of data to fill in with response results
* @param r1 The first output to test against, optional with a default value
* of "OK"
* @param r2 The second output to test against, optional with a default value
* of "ERROR"
* @param r3 The third output to test against, optional with a default value
* of NULL
* @param r4 The fourth output to test against, optional with a default value
* of NULL
* @param r5 The fifth output to test against, optional with a default value
* of NULL
* @param r6 The sixth output to test against, optional with a default value
* of NULL
* @param r7 The seventh output to test against, optional with a default value
* of NULL
* @return *int8_t* the index of the response input
*/
int8_t waitResponse(uint32_t timeout_ms, String& data,
GsmConstStr r1 = GFP(GSM_OK),
GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = nullptr,
GsmConstStr r4 = nullptr, GsmConstStr r5 = nullptr,
GsmConstStr r6 = nullptr, GsmConstStr r7 = nullptr) {
return thisModem().waitResponseImpl(timeout_ms, data, r1, r2, r3, r4, r5,
r6, r7);
}
/**
* @brief Listen for responses to commands and handle URCs
*
* @param timeout_ms The time to wait for a response
* @param r1 The first output to test against, optional with a default value
* of "OK"
* @param r2 The second output to test against, optional with a default value
* of "ERROR"
* @param r3 The third output to test against, optional with a default value
* of NULL
* @param r4 The fourth output to test against, optional with a default value
* of NULL
* @param r5 The fifth output to test against, optional with a default value
* of NULL
* @param r6 The sixth output to test against, optional with a default value
* of NULL
* @param r7 The seventh output to test against, optional with a default value
* of NULL
* @return *int8_t* the index of the response input
*/
int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = nullptr,
GsmConstStr r4 = nullptr, GsmConstStr r5 = nullptr,
GsmConstStr r6 = nullptr, GsmConstStr r7 = nullptr) {
String data;
return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5, r6, r7);
}
/**
* @brief Listen for responses to commands and handle URCs; listening for 1
* second.
*
* @param r1 The first output to test against, optional with a default value
* of "OK"
* @param r2 The second output to test against, optional with a default value
* of "ERROR"
* @param r3 The third output to test against, optional with a default value
* of NULL
* @param r4 The fourth output to test against, optional with a default value
* of NULL
* @param r5 The fifth output to test against, optional with a default value
* of NULL
* @param r6 The sixth output to test against, optional with a default value
* of NULL
* @param r7 The seventh output to test against, optional with a default value
* of NULL
* @return *int8_t* the index of the response input
*/
int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = nullptr,
GsmConstStr r4 = nullptr, GsmConstStr r5 = nullptr,
GsmConstStr r6 = nullptr, GsmConstStr r7 = nullptr) {
return waitResponse(1000L, r1, r2, r3, r4, r5, r6, r7);
}
/**
* @brief Asks for modem information via the 3GPP TS 27.007 standard ATI
* command
*
* @note The actual value and style of the response is quite varied
* @return *String* Some info about the GSM module.
*/
String getModemInfo() {
return thisModem().getModemInfoImpl();
}
/**
* @brief Get the modem name - a combination of the manufacturer and model, as
* the modem calls itself
*
* @return *String* The modem name
*/
String getModemName() {
return thisModem().getModemNameImpl();
}
/**
* @brief Get the modem manufacturer
*
* @return *String* The modem manufacturer
*/
String getModemManufacturer() {
return thisModem().getModemManufacturerImpl();
}
/**
* @brief Get the modem model
*
* @return *String* The modem model, as it calls itself
*/
String getModemModel() {
return thisModem().getModemModelImpl();
}
/**
* @brief Get the modem revision information.
*
* What is returned as the revision may be either a hardware or a firmware
* version or some combination of both.
*
* @return *String* The modem revision information
*/
String getModemRevision() {
return thisModem().getModemRevisionImpl();
}
/**
* @brief Get the modem serial number
*
* @return *String* The modem serial number
*/
String getModemSerialNumber() {
return thisModem().getModemSerialNumberImpl();
}
/**
* @brief Reset the module to factory defaults.
*
* This generally restarts the module as well.
*
* @return *true* The module successfully reset to default.
* @return *false* The module failed to reset to default.
*/
bool factoryDefault() {
return thisModem().factoryDefaultImpl();
}
/**@}*/
/**
* @anchor power_functions
* @name Power functions
*/
/**@{*/
/**
* @brief Restart the module
*
* @param pin A pin code to unlock the SIM, if necessary
*
* @return *true* The module was successfully restarted.
* @return *false* There was an error in restarting the module.
*/
bool restart(const char* pin = nullptr) {
return thisModem().restartImpl(pin);
}
/**
* @brief Power off the module
*
* @return *true* The module was successfully powered down.
* @return *false* There was an error in powering down module.
*/
bool poweroff() {
return thisModem().powerOffImpl();
}
/**
* @brief Turn off the module radio
*
* @return *true* The module radio was successfully turned off.
* @return *false* There was an error in turning off the radio.
*/
bool radioOff() {
return thisModem().radioOffImpl();
}
/**
* @brief Enable sleep on the module.
*
* For some modules this immediately puts
* the module to sleep, for others this sets them to be able to sleep based on
* pin levels.
*
* @param enable True to enable sleep, false to disable
* @return *true* Sleep was successfully enabled or disabled
* @return *false* There was a problem setting sleep
*/
bool sleepEnable(bool enable = true) {
return thisModem().sleepEnableImpl(enable);
}
/**
* @brief Set the phone functionality
*
* @param fun The phone functionality setting. The value and meaning of this
* varies by module; check your documentation.
* @param reset True to reset the module before changing the functionality.
* @return *true* The phone functionalilty was successfully changed.
* @return *false* There was a problem changing the functionality.
*/
bool setPhoneFunctionality(uint8_t fun, bool reset = false) {
return thisModem().setPhoneFunctionalityImpl(fun, reset);
}
/**@}*/
/**
* @anchor network_functions
* @name Generic Network Functions
*/
/**@{*/
// RegStatus getRegistrationStatus() {}
/**
* @brief Confirm whether the module is currently connected to the
* GSM/GPRS/LTE network.
*
* @return *true* The module is connected to the network
* @return *false* The module is not connected to the network
*/
bool isNetworkConnected() {
return thisModem().isNetworkConnectedImpl();
}
/**
* @brief Wait until the module has connected to the network
*
* @param timeout_ms The time to wait for attachment in milliseconds. Optional
* with a default value of 1 minute.
* @param check_signal True to alternate between checking for connection and
* checking the signal strength.
* @return *true* The module is now connected to the network.
* @return *false* The module did not connect to the network even after
* waiting.
*/
bool waitForNetwork(uint32_t timeout_ms = 60000L, bool check_signal = false) {
return thisModem().waitForNetworkImpl(timeout_ms, check_signal);
}
/**
* @brief Get the signal quality report
*
* This is often a "CSQ" value ranging from 0 to 32, but may be an RSSI or a
* percent.
*
* @return *int16_t* The signal quality
*/
int16_t getSignalQuality() {
return thisModem().getSignalQualityImpl();
}
/**
* @brief Get the Local IP address assigned to the module by the network as a
* String
*
* @return *String* The local IP address
*/
String getLocalIP() {
return thisModem().getLocalIPImpl();
}
/**
* @brief Get the Local IP address assigned to the module by the network as an
* IPAddress object.
*
* @return *IPAddress* The local IP address
*/
IPAddress localIP() {
return thisModem().TinyGsmIpFromString(thisModem().getLocalIP());
}
/**@}*/
/**
* @anchor crtp_helper
* @name CRTP Helper
*/
/**@{*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
/**@}*/
~TinyGsmModem() {}
/**
* @anchor modem_utilities
* @name Utilities
*/
/**@{*/
public:
// Utility templates for writing/skipping characters on a stream
template <typename T>
inline void streamWrite(T last) {
thisModem().stream.print(last);
}
template <typename T, typename... Args>
inline void streamWrite(T head, Args... tail) {
thisModem().stream.print(head);
thisModem().streamWrite(tail...);
}
inline void streamClear() {
while (thisModem().stream.available()) {
thisModem().waitResponse(50, nullptr, nullptr);
}
}
protected:
inline bool streamGetLength(char* buf, int8_t numChars,
const uint32_t timeout_ms = 1000L) {
if (!buf) { return false; }
int8_t numCharsReady = -1;
uint32_t startMillis = millis();
while (millis() - startMillis < timeout_ms &&
(numCharsReady = thisModem().stream.available()) < numChars) {
TINY_GSM_YIELD();
}
if (numCharsReady >= numChars) {
thisModem().stream.readBytes(buf, numChars);
return true;
}
return false;
}
inline int16_t streamGetIntLength(int8_t numChars,
const uint32_t timeout_ms = 1000L) {
char buf[numChars + 1];
if (streamGetLength(buf, numChars, timeout_ms)) {
buf[numChars] = '\0';
return atoi(buf);
}
return -9999;
}
inline int16_t streamGetIntBefore(char lastChar) {
char buf[7];
size_t bytesRead = thisModem().stream.readBytesUntil(
lastChar, buf, static_cast<size_t>(7));
// if we read 7 or more bytes, it's an overflow
if (bytesRead && bytesRead < 7) {
buf[bytesRead] = '\0';
int16_t res = atoi(buf);
return res;
}
return -9999;
}
inline float streamGetFloatLength(int8_t numChars,
const uint32_t timeout_ms = 1000L) {
char buf[numChars + 1];
if (streamGetLength(buf, numChars, timeout_ms)) {
buf[numChars] = '\0';
return atof(buf);
}
return -9999.0F;
}
inline float streamGetFloatBefore(char lastChar) {
char buf[16];
size_t bytesRead = thisModem().stream.readBytesUntil(
lastChar, buf, static_cast<size_t>(16));
// if we read 16 or more bytes, it's an overflow
if (bytesRead && bytesRead < 16) {
buf[bytesRead] = '\0';
float res = atof(buf);
return res;
}
return -9999.0F;
}
inline bool streamSkipUntil(const char c, const uint32_t timeout_ms = 1000L) {
uint32_t startMillis = millis();
while (millis() - startMillis < timeout_ms) {
while (millis() - startMillis < timeout_ms &&
!thisModem().stream.available()) {
TINY_GSM_YIELD();
}
if (thisModem().stream.read() == c) { return true; }
}
return false;
}
inline void cleanResponseString(String& res) {
// Do the replaces twice so we cover both \r and \r\n type endings
res.replace("\r\nOK\r\n", "");
res.replace("\rOK\r", "");
res.replace("\r\n", " ");
res.replace("\r", " ");
res.trim();
}
static inline IPAddress TinyGsmIpFromString(const String& strIP) {
int Parts[4] = {
0,
};
int Part = 0;
for (uint8_t i = 0; i < strIP.length(); i++) {
char c = strIP[i];
if (c == '.') {
Part++;
if (Part > 3) { return IPAddress(0, 0, 0, 0); }
continue;
} else if (c >= '0' && c <= '9') {
Parts[Part] *= 10;
Parts[Part] += c - '0';
} else {
if (Part == 3) break;
}
}
return IPAddress(Parts[0], Parts[1], Parts[2], Parts[3]);
}
/**@}*/
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* Basic functions
*/
protected:
bool initImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool setBaudImpl(uint32_t baud) {
thisModem().sendAT(GF("+IPR="), baud);
return thisModem().waitResponse() == 1;
}
bool testATImpl(uint32_t timeout_ms = 10000L) {
for (uint32_t start = millis(); millis() - start < timeout_ms;) {
thisModem().sendAT(GF(""));
if (thisModem().waitResponse(200) == 1) { return true; }
delay(100);
}
return false;
}
// TODO(vshymanskyy): Optimize this!
int8_t waitResponseImpl(uint32_t timeout_ms, String& data,
GsmConstStr r1 = GFP(GSM_OK),
GsmConstStr r2 = GFP(GSM_ERROR),
GsmConstStr r3 = nullptr, GsmConstStr r4 = nullptr,
GsmConstStr r5 = nullptr, GsmConstStr r6 = nullptr,
GsmConstStr r7 = nullptr) {
data.reserve(64);
#ifdef TINY_GSM_DEBUG_DEEP
DBG(GF("r1 <"), r1 ? r1 : GF("NULL"), GF("> r2 <"), r2 ? r2 : GF("NULL"),
GF("> r3 <"), r3 ? r3 : GF("NULL"), GF("> r4 <"), r4 ? r4 : GF("NULL"),
GF("> r5 <"), r5 ? r5 : GF("NULL"), GF("> r6 <"), r6 ? r6 : GF("NULL"),
GF("> r7 <"), r7 ? r7 : GF("NULL"), '>');
#endif
uint8_t index = 0;
uint32_t startMillis = millis();
do {
TINY_GSM_YIELD();
while (thisModem().stream.available() > 0) {
TINY_GSM_YIELD();
int8_t a = thisModem().stream.read();
if (a <= 0) continue; // Skip 0x00 bytes, just in case
data += static_cast<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 (r6 && data.endsWith(r6)) {
index = 6;
goto finish;
} else if (r7 && data.endsWith(r7)) {
index = 7;
goto finish;
}
#if defined TINY_GSM_DEBUG
else if (data.endsWith(GFP(GSM_VERBOSE)) ||
data.endsWith(GFP(GSM_VERBOSE_2))) {
// check how long the new line is
// should be either 1 ('\r' or '\n') or 2 ("\r\n"))
int len_atnl = strnlen(AT_NL, 3);
// Read out the verbose message, until the last character of the new
// line
data += thisModem().stream.readStringUntil(AT_NL[len_atnl]);
#ifdef TINY_GSM_DEBUG_DEEP
data.trim();
DBG(GF("Verbose details <<<"), data, GF(">>>"));
#endif
data = "";
goto finish;
}
#endif
else if (thisModem().handleURCs(data)) {
data = "";
}
}
} while (millis() - startMillis < timeout_ms);
finish:
#ifdef TINY_GSM_DEBUG_DEEP
data.replace("\r", "←");
data.replace("\n", "↓");
#endif
if (!index) {
data.trim();
if (data.length()) { DBG("### Unhandled:", data); }
data = "";
} else {
#ifdef TINY_GSM_DEBUG_DEEP
DBG('<', index, '>', data);
#endif
}
return index;
}
String getModemInfoImpl() {
thisModem().sendAT(GF("I")); // 3GPP TS 27.007
String res;
if (thisModem().waitResponse(1000L, res) != 1) { return ""; }
thisModem().cleanResponseString(res);
return res;
}
String getModemNameImpl() {
String manufacturer = getModemManufacturer();
String model = getModemModel();
String name = manufacturer + String(" ") + model;
DBG("### Modem:", name);
return name;
}
// Gets the modem manufacturer
String getModemManufacturerImpl() {
String manufacturer = MODEM_MANUFACTURER;
thisModem().sendAT(GF("+CGMI")); // 3GPP TS 27.007 standard
String res;
if (thisModem().waitResponse(1000L, res) != 1) { return manufacturer; }
thisModem().cleanResponseString(res);
return res;
}
// Gets the modem hardware version
String getModemModelImpl() {
String model = MODEM_MODEL;
thisModem().sendAT(GF("+CGMM")); // 3GPP TS 27.007 standard
String res;
if (thisModem().waitResponse(1000L, res) != 1) { return model; }
thisModem().cleanResponseString(res);
return res;
}
// Gets the modem firmware version
String getModemRevisionImpl() {
thisModem().sendAT(GF("+CGMR")); // 3GPP TS 27.007 standard
String res;
if (thisModem().waitResponse(1000L, res) != 1) { return "unknown"; }
thisModem().cleanResponseString(res);
return res;
}
// Gets the modem serial number
String getModemSerialNumberImpl() {
thisModem().sendAT(GF("+CGSN")); // 3GPP TS 27.007 standard
String res;
if (thisModem().waitResponse(1000L, res) != 1) { return "unknown"; }
thisModem().cleanResponseString(res);
return res;
}
bool factoryDefaultImpl() {
thisModem().sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
thisModem().waitResponse();
thisModem().sendAT(GF("+IPR=0")); // Auto-baud
thisModem().waitResponse();
thisModem().sendAT(GF("&W")); // Write configuration
return thisModem().waitResponse() == 1;
}
/*
* Power functions
*/
protected:
bool radioOffImpl() {
if (!thisModem().setPhoneFunctionality(0)) { return false; }
delay(3000);
return true;
}
bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false)
TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Generic network functions
*/
protected:
// Gets the modem's registration status via CREG/CGREG/CEREG
// CREG = Generic network registration
// CGREG = GPRS service registration
// CEREG = EPS registration for LTE modules
int8_t getRegistrationStatusXREG(const char* regCommand) {
thisModem().sendAT('+', regCommand, '?');
// check for any of the three for simplicity
int8_t resp = thisModem().waitResponse(GF("+CREG:"), GF("+CGREG:"),
GF("+CEREG:"));
if (resp != 1 && resp != 2 && resp != 3) { return -1; }
thisModem().streamSkipUntil(','); /* Skip format (0) */
int status = thisModem().stream.parseInt();
thisModem().waitResponse();
return status;
}
bool waitForNetworkImpl(uint32_t timeout_ms = 60000L,
bool check_signal = false) {
for (uint32_t start = millis(); millis() - start < timeout_ms;) {
if (check_signal) { thisModem().getSignalQuality(); }
if (thisModem().isNetworkConnected()) { return true; }
delay(250);
}
return false;
}
// Gets signal quality report according to 3GPP TS command AT+CSQ
int8_t getSignalQualityImpl() {
thisModem().sendAT(GF("+CSQ"));
if (thisModem().waitResponse(GF("+CSQ:")) != 1) { return 99; }
int8_t res = thisModem().streamGetIntBefore(',');
thisModem().waitResponse();
return res;
}
String getLocalIPImpl() {
thisModem().sendAT(GF("+CGPADDR=1"));
if (thisModem().waitResponse(GF("+CGPADDR:")) != 1) { return ""; }
thisModem().streamSkipUntil(','); // Skip context id
String res = thisModem().stream.readStringUntil('\r');
if (thisModem().waitResponse() != 1) { return ""; }
return res;
}
};
#endif // SRC_TINYGSMMODEM_H_

View file

@ -0,0 +1,111 @@
/**
* @file TinyGsmNTP.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMNTP_H_
#define SRC_TINYGSMNTP_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_NTP
template <class modemType>
class TinyGsmNTP {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* NTP server functions
*/
public:
byte NTPServerSync(String server = "pool.ntp.org", int TimeZone = 0) {
return thisModem().NTPServerSyncImpl(server, TimeZone);
}
String ShowNTPError(byte error) {
return thisModem().ShowNTPErrorImpl(error);
}
/*
* Utilities
*/
bool TinyGsmIsValidNumber(String str) {
if (!(str.charAt(0) == '+' || str.charAt(0) == '-' ||
isDigit(str.charAt(0))))
return false;
for (byte i = 1; i < str.length(); i++) {
if (!(isDigit(str.charAt(i)) || str.charAt(i) == '.')) { return false; }
}
return true;
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmNTP() {}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* NTP server functions
*/
protected:
byte NTPServerSyncImpl(String server = "pool.ntp.org", int TimeZone = 0) {
// Set GPRS bearer profile to associate with NTP sync
// this may fail, it's not supported by all modules
thisModem().sendAT(GF("+CNTPCID=1"));
thisModem().waitResponse(10000L);
// Set NTP server and timezone
thisModem().sendAT(GF("+CNTP=\""), server, "\",", String(TimeZone));
if (thisModem().waitResponse(10000L) != 1) { return -1; }
// Request network synchronization
thisModem().sendAT(GF("+CNTP"));
if (thisModem().waitResponse(10000L, GF("+CNTP:"))) {
String result = thisModem().stream.readStringUntil('\n');
// Check for ',' in case the module appends the time next to the return
// code. Eg: +CNTP: <code>[,<time>]
int index = result.indexOf(',');
if (index > 0) { result.remove(index); }
result.trim();
if (TinyGsmIsValidNumber(result)) { return result.toInt(); }
} else {
return -1;
}
return -1;
}
String ShowNTPErrorImpl(byte error) {
switch (error) {
case 1: return "Network time synchronization is successful";
case 61: return "Network error";
case 62: return "DNS resolution error";
case 63: return "Connection error";
case 64: return "Service response error";
case 65: return "Service response timeout";
default: return "Unknown error: " + String(error);
}
}
};
#endif // SRC_TINYGSMNTP_H_

View file

@ -0,0 +1,240 @@
/**
* @file TinyGsmSMS.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMSMS_H_
#define SRC_TINYGSMSMS_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_SMS
template <class modemType>
class TinyGsmSMS {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* Text messaging (SMS) functions
*/
String sendUSSD(const String& code) {
return thisModem().sendUSSDImpl(code);
}
bool sendSMS(const String& number, const String& text) {
return thisModem().sendSMSImpl(number, text);
}
bool sendSMS_UTF16(const char* const number, const void* text, size_t len) {
return thisModem().sendSMS_UTF16Impl(number, text, len);
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmSMS() {}
/*
* Utilities
*/
protected:
static inline String TinyGsmDecodeHex7bit(String& instr) {
String result;
byte reminder = 0;
int8_t bitstate = 7;
for (uint8_t i = 0; i < instr.length(); i += 2) {
char buf[4] = {
0,
};
buf[0] = instr[i];
buf[1] = instr[i + 1];
byte b = strtol(buf, nullptr, 16);
byte bb = b << (7 - bitstate);
char c = (bb + reminder) & 0x7F;
result += c;
reminder = b >> bitstate;
bitstate--;
if (bitstate == 0) {
char cc = reminder;
result += cc;
reminder = 0;
bitstate = 7;
}
}
return result;
}
static inline String TinyGsmDecodeHex8bit(String& instr) {
String result;
for (uint16_t i = 0; i < instr.length(); i += 2) {
char buf[4] = {
0,
};
buf[0] = instr[i];
buf[1] = instr[i + 1];
char b = strtol(buf, nullptr, 16);
result += b;
}
return result;
}
static inline String TinyGsmDecodeHex16bit(String& instr) {
String result;
for (uint16_t i = 0; i < instr.length(); i += 4) {
char buf[4] = {
0,
};
buf[0] = instr[i];
buf[1] = instr[i + 1];
char b = strtol(buf, nullptr, 16);
if (b) { // If high byte is non-zero, we can't handle it ;(
#if defined(TINY_GSM_UNICODE_TO_HEX)
result += "\\x";
result += instr.substring(i, i + 4);
#else
result += "?";
#endif
} else {
buf[0] = instr[i + 2];
buf[1] = instr[i + 3];
b = strtol(buf, nullptr, 16);
result += b;
}
}
return result;
}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* Text messaging (SMS) functions
*/
String sendUSSDImpl(const String& code) {
// Set preferred message format to text mode
thisModem().sendAT(GF("+CMGF=1"));
thisModem().waitResponse();
// Set 8-bit hexadecimal alphabet (3GPP TS 23.038)
thisModem().sendAT(GF("+CSCS=\"HEX\""));
thisModem().waitResponse();
// Send the message
thisModem().sendAT(GF("+CUSD=1,\""), code, GF("\""));
if (thisModem().waitResponse() != 1) { return ""; }
if (thisModem().waitResponse(10000L, GF("+CUSD:")) != 1) { return ""; }
thisModem().stream.readStringUntil('"');
String hex = thisModem().stream.readStringUntil('"');
thisModem().stream.readStringUntil(',');
int8_t dcs = thisModem().streamGetIntBefore('\n');
if (dcs == 15) {
return TinyGsmDecodeHex8bit(hex);
} else if (dcs == 72) {
return TinyGsmDecodeHex16bit(hex);
} else {
return hex;
}
}
bool sendSMSImpl(const String& number, const String& text) {
// Set preferred message format to text mode
thisModem().sendAT(GF("+CMGF=1"));
thisModem().waitResponse();
// Set GSM 7 bit default alphabet (3GPP TS 23.038)
thisModem().sendAT(GF("+CSCS=\"GSM\""));
thisModem().waitResponse();
thisModem().sendAT(GF("+CMGS=\""), number, GF("\""));
if (thisModem().waitResponse(GF(">")) != 1) { return false; }
thisModem().stream.print(text); // Actually send the message
thisModem().stream.write(static_cast<char>(0x1A)); // Terminate the message
thisModem().stream.flush();
return thisModem().waitResponse(60000L) == 1;
}
// Common methods for UTF8/UTF16 SMS.
// Supported by: BG96, M95, MC60, SIM5360, SIM7000, SIM7600, SIM800
class UTF8Print : public Print {
public:
explicit UTF8Print(Print& p) : p(p) {}
size_t write(const uint8_t c) override {
if (prv < 0xC0) {
if (c < 0xC0) printHex(c);
prv = c;
} else {
uint16_t v = uint16_t(prv) << 8 | c;
v -= (v >> 8 == 0xD0) ? 0xCC80 : 0xCD40;
printHex(v);
prv = 0;
}
return 1;
}
private:
Print& p;
uint8_t prv = 0;
void printHex(const uint16_t v) {
uint8_t c = v >> 8;
if (c < 0x10) p.print('0');
p.print(c, HEX);
c = v & 0xFF;
if (c < 0x10) p.print('0');
p.print(c, HEX);
}
};
bool sendSMS_UTF8_begin(const char* const number) {
thisModem().sendAT(GF("+CMGF=1"));
thisModem().waitResponse();
thisModem().sendAT(GF("+CSCS=\"HEX\""));
thisModem().waitResponse();
thisModem().sendAT(GF("+CSMP=17,167,0,8"));
thisModem().waitResponse();
thisModem().sendAT(GF("+CMGS=\""), number, GF("\""));
return thisModem().waitResponse(GF(">")) == 1;
}
bool sendSMS_UTF8_end() {
thisModem().stream.write(static_cast<char>(0x1A));
thisModem().stream.flush();
return thisModem().waitResponse(60000L) == 1;
}
UTF8Print sendSMS_UTF8_stream() {
return UTF8Print(thisModem().stream);
}
bool sendSMS_UTF16Impl(const char* const number, const void* text,
size_t len) {
if (!sendSMS_UTF8_begin(number)) { return false; }
uint16_t* t =
const_cast<uint16_t*>(reinterpret_cast<const uint16_t*>(text));
for (size_t i = 0; i < len; i++) {
uint8_t c = t[i] >> 8;
if (c < 0x10) { thisModem().stream.print('0'); }
thisModem().stream.print(c, HEX);
c = t[i] & 0xFF;
if (c < 0x10) { thisModem().stream.print('0'); }
thisModem().stream.print(c, HEX);
}
return sendSMS_UTF8_end();
}
};
#endif // SRC_TINYGSMSMS_H_

View file

@ -0,0 +1,83 @@
/**
* @file TinyGsmSSL.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMSSL_H_
#define SRC_TINYGSMSSL_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_SSL
template <class modemType, uint8_t muxCount>
class TinyGsmSSL {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* Secure socket layer (SSL) functions
*/
bool addCertificate(const char* filename) {
return thisModem().addCertificateImpl(filename);
}
bool addCertificate(const String& filename) {
return addCertificate(filename.c_str());
}
bool addCertificate(const char* certificateName, const char* cert,
const uint16_t len) {
return thisModem().addCertificateImpl(certificateName, cert, len);
}
bool addCertificate(const String& certificateName, const String& cert,
const uint16_t len) {
return addCertificate(certificateName.c_str(), cert.c_str(), len);
}
bool deleteCertificate(const char* filename) {
return thisModem().deleteCertificateImpl(filename);
}
bool setCertificate(const String& certificateName, const uint8_t mux = 0) {
if (mux >= muxCount) return false;
certificates[mux] = certificateName;
return true;
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmSSL() {}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* Secure socket layer (SSL) functions
*/
protected:
bool addCertificateImpl(const char* filename) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool addCertificateImpl(const char* certificateName, const char* cert,
const uint16_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool
deleteCertificateImpl(const char* filename) TINY_GSM_ATTR_NOT_IMPLEMENTED;
String certificates[muxCount];
};
#endif // SRC_TINYGSMSSL_H_

View file

@ -0,0 +1,376 @@
/**
* @file TinyGsmTCP.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMTCP_H_
#define SRC_TINYGSMTCP_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_TCP
#include "TinyGsmFifo.h"
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
// Because of the ordering of resolution of overrides in templates, these need
// to be written out every time. This macro is to shorten that.
#define TINY_GSM_CLIENT_CONNECT_OVERRIDES \
int connect(IPAddress ip, uint16_t port, int timeout_s) { \
return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); \
} \
int connect(const char* host, uint16_t port) override { \
return connect(host, port, 75); \
} \
int connect(IPAddress ip, uint16_t port) override { \
return connect(ip, port, 75); \
}
// // For modules that do not store incoming data in any sort of buffer
// #define TINY_GSM_NO_MODEM_BUFFER
// // Data is stored in a buffer, but we can only read from the buffer,
// // not check how much data is stored in it
// #define TINY_GSM_BUFFER_READ_NO_CHECK
// // Data is stored in a buffer and we can both read and check the size
// // of the buffer
// #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
template <class modemType, uint8_t muxCount>
class TinyGsmTCP {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* Basic functions
*/
void maintain() {
return thisModem().maintainImpl();
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmTCP() {}
/*
* Inner Client
*/
public:
class GsmClient : public Client {
// Make all classes created from the modem template friends
friend class TinyGsmTCP<modemType, muxCount>;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public:
// bool init(modemType* modem, uint8_t);
// int connect(const char* host, uint16_t port, int timeout_s);
// Connect to a IP address given as an IPAddress object by
// converting said IP address to text
// virtual int connect(IPAddress ip,uint16_t port, int timeout_s) {
// return connect(TinyGsmStringFromIp(ip).c_str(), port,
// timeout_s);
// }
// int connect(const char* host, uint16_t port) override {
// return connect(host, port, 75);
// }
// int connect(IPAddress ip,uint16_t port) override {
// return connect(ip, port, 75);
// }
static inline String TinyGsmStringFromIp(IPAddress ip) {
String host;
host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return host;
}
// void stop(uint32_t maxWaitMs);
// void stop() override {
// stop(15000L);
// }
// Writes data out on the client using the modem send functionality
size_t write(const uint8_t* buf, size_t size) override {
TINY_GSM_YIELD();
at->maintain();
return at->modemSend(buf, size, mux);
}
size_t write(uint8_t c) override {
return write(&c, 1);
}
size_t write(const char* str) {
if (str == nullptr) return 0;
return write((const uint8_t*)str, strlen(str));
}
int available() override {
TINY_GSM_YIELD();
#if defined TINY_GSM_NO_MODEM_BUFFER
// Returns the number of characters available in the TinyGSM fifo
if (!rx.size() && sock_connected) { at->maintain(); }
return rx.size();
#elif defined TINY_GSM_BUFFER_READ_NO_CHECK
// Returns the combined number of characters available in the TinyGSM
// fifo and the modem chips internal fifo.
if (!rx.size()) { at->maintain(); }
return static_cast<uint16_t>(rx.size()) + sock_available;
#elif defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
// 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.
if (!rx.size()) {
if (millis() - prev_check > 500) {
// setting got_data to true will tell maintain to run
// modemGetAvailable(mux)
got_data = true;
prev_check = millis();
}
at->maintain();
}
return static_cast<uint16_t>(rx.size()) + sock_available;
#else
#error Modem client has been incorrectly created
#endif
}
int read(uint8_t* buf, size_t size) override {
TINY_GSM_YIELD();
size_t cnt = 0;
#if defined TINY_GSM_NO_MODEM_BUFFER
// 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.
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;
#elif defined TINY_GSM_BUFFER_READ_NO_CHECK
// Reads characters out of the TinyGSM fifo, and from the modem chip's
// internal fifo if avaiable.
at->maintain();
while (cnt < size) {
size_t chunk = TinyGsmMin(size - cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
} /* TODO: Read directly into user buffer? */
at->maintain();
if (sock_available > 0) {
int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available),
mux);
if (n == 0) break;
} else {
break;
}
}
return cnt;
#elif defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
// 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.
at->maintain();
while (cnt < size) {
size_t chunk = TinyGsmMin(size - cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// Workaround: Some modules "forget" to notify about data arrival
if (millis() - prev_check > 500) {
// setting got_data to true will tell maintain to run
// modemGetAvailable()
got_data = true;
prev_check = millis();
}
// TODO(vshymanskyy): Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available),
mux);
if (n == 0) break;
} else {
break;
}
}
return cnt;
#else
#error Modem client has been incorrectly created
#endif
}
int read() override {
uint8_t c;
if (read(&c, 1) == 1) { return c; }
return -1;
}
int peek() override {
return (uint8_t)rx.peek();
}
void flush() override {
at->stream.flush();
}
uint8_t connected() override {
if (available()) { return true; }
#if defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
// If the modem is one where we can read and check the size of the buffer,
// then the 'available()' function will call a check of the current size
// of the buffer and state of the connection. [available calls maintain,
// maintain calls modemGetAvailable, modemGetAvailable calls
// modemGetConnected] This cascade means that the sock_connected value
// should be correct and all we need
return sock_connected;
#elif defined TINY_GSM_NO_MODEM_BUFFER || defined TINY_GSM_BUFFER_READ_NO_CHECK
// If the modem doesn't have an internal buffer, or if we can't check how
// many characters are in the buffer then the cascade won't happen.
// We need to call modemGetConnected to check the sock state.
return at->modemGetConnected(mux);
#else
#error Modem client has been incorrectly created
#endif
}
operator bool() override {
return connected();
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
protected:
// Read and dump anything remaining in the modem's internal buffer.
// Using this in the client stop() function.
// The socket will appear open in response to connected() even after it
// closes until all data is read from the buffer.
// Doing it this way allows the external mcu to find and get all of the
// data that it wants from the socket even if it was closed externally.
inline void dumpModemBuffer(uint32_t maxWaitMs) {
#if defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE || \
defined TINY_GSM_BUFFER_READ_NO_CHECK
TINY_GSM_YIELD();
uint32_t startMillis = millis();
while (sock_available > 0 && (millis() - startMillis < maxWaitMs)) {
rx.clear();
at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
}
rx.clear();
at->streamClear();
#elif defined TINY_GSM_NO_MODEM_BUFFER
rx.clear();
at->streamClear();
#else
#error Modem client has been incorrectly created
#endif
}
modemType* at;
uint8_t mux;
uint16_t sock_available;
uint32_t prev_check;
bool sock_connected;
bool got_data;
RxFifo rx;
};
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* Basic functions
*/
protected:
void maintainImpl() {
#if defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
// Keep listening for modem URC's and proactively iterate through
// sockets asking if any data is avaiable
for (int mux = 0; mux < muxCount; mux++) {
GsmClient* sock = thisModem().sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
sock->sock_available = thisModem().modemGetAvailable(mux);
}
}
while (thisModem().stream.available()) {
thisModem().waitResponse(15, nullptr, nullptr);
}
#elif defined TINY_GSM_NO_MODEM_BUFFER || defined TINY_GSM_BUFFER_READ_NO_CHECK
// Just listen for any URC's
thisModem().waitResponse(100, nullptr, nullptr);
#else
#error Modem client has been incorrectly created
#endif
}
// Yields up to a time-out period and then reads a character from the stream
// into the mux FIFO
// TODO(SRGDamia1): Do we really 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.
inline void moveCharFromStreamToFifo(uint8_t mux) {
if (!thisModem().sockets[mux]) return;
uint32_t startMillis = millis();
while (!thisModem().stream.available() &&
(millis() - startMillis < thisModem().sockets[mux]->_timeout)) {
TINY_GSM_YIELD();
}
char c = thisModem().stream.read();
thisModem().sockets[mux]->rx.put(c);
}
};
#endif // SRC_TINYGSMTCP_H_

View file

@ -0,0 +1,62 @@
/**
* @file TinyGsmTemperature.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMTEMPERATURE_H_
#define SRC_TINYGSMTEMPERATURE_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_TEMPERATURE
template <class modemType>
class TinyGsmTemperature {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* Temperature functions
*/
/**
* @brief Get the modem chip temperature in degrees celsius.
*
* @return *float* The modem chip temperature in degrees celsius.
*/
float getTemperature() {
return thisModem().getTemperatureImpl();
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmTemperature() {}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* Temperature functions
*/
float getTemperatureImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
#endif // SRC_TINYGSMTEMPERATURE_H_

View file

@ -0,0 +1,165 @@
/**
* @file TinyGsmTime.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMTIME_H_
#define SRC_TINYGSMTIME_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_TIME
enum TinyGSMDateTimeFormat { DATE_FULL = 0, DATE_TIME = 1, DATE_DATE = 2 };
template <class modemType>
class TinyGsmTime {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* Time functions
*/
/**
* @brief Get the Date Time as a String
*
* @param format The date or time part to get: DATE_FULL,
* DATE_TIME, or DATE_DATE
* @return *String* The date and/or time from the module
*/
String getGSMDateTime(TinyGSMDateTimeFormat format) {
return thisModem().getGSMDateTimeImpl(format);
}
/**
* @brief Get the date and time as parts
*
* @param year Reference to an int for the year
* @param month Reference to an int for the month
* @param day Reference to an int for the day
* @param hour Reference to an int for the hour
* @param minute Reference to an int for the minute
* @param second Reference to an int for the second
* @param timezone Reference to a float for the timezone
* @return *true* The references have been filled with valid values from the
* GSM module.
* @return *false* There was a problem getting the time from the module.
*/
bool getNetworkTime(int* year, int* month, int* day, int* hour, int* minute,
int* second, float* timezone) {
return thisModem().getNetworkTimeImpl(year, month, day, hour, minute,
second, timezone);
}
/**
* @brief Get the date and time as parts in UTC
*
* @param year Reference to an int for the year
* @param month Reference to an int for the month
* @param day Reference to an int for the day
* @param hour Reference to an int for the hour
* @param minute Reference to an int for the minute
* @param second Reference to an int for the second
* @param timezone Reference to a float for the timezone
* @return *true* The references have been filled with valid values from the
* GSM module.
* @return *false* There was a problem getting the time from the module.
*/
bool getNetworkUTCTime(int* year, int* month, int* day, int* hour,
int* minute, int* second, float* timezone) {
return thisModem().getNetworkUTCTimeImpl(year, month, day, hour, minute,
second, timezone);
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmTime() {}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* Time functions
*/
protected:
String getGSMDateTimeImpl(TinyGSMDateTimeFormat format) {
thisModem().sendAT(GF("+CCLK?"));
if (thisModem().waitResponse(2000L, GF("+CCLK: \"")) != 1) { return ""; }
String res;
switch (format) {
case DATE_FULL: res = thisModem().stream.readStringUntil('"'); break;
case DATE_TIME:
thisModem().streamSkipUntil(',');
res = thisModem().stream.readStringUntil('"');
break;
case DATE_DATE: res = thisModem().stream.readStringUntil(','); break;
}
thisModem().waitResponse(); // Ends with OK
return res;
}
bool getNetworkTimeImpl(int* year, int* month, int* day, int* hour,
int* minute, int* second, float* timezone) {
thisModem().sendAT(GF("+CCLK?"));
if (thisModem().waitResponse(2000L, GF("+CCLK: \"")) != 1) { return false; }
int iyear = 0;
int imonth = 0;
int iday = 0;
int ihour = 0;
int imin = 0;
int isec = 0;
int itimezone = 0;
// Date & Time
iyear = thisModem().streamGetIntBefore('/');
imonth = thisModem().streamGetIntBefore('/');
iday = thisModem().streamGetIntBefore(',');
ihour = thisModem().streamGetIntBefore(':');
imin = thisModem().streamGetIntBefore(':');
isec = thisModem().streamGetIntLength(2);
char tzSign = thisModem().stream.read();
itimezone = thisModem().streamGetIntBefore('\n');
if (tzSign == '-') { itimezone = itimezone * -1; }
// Set pointers
if (iyear < 2000) iyear += 2000;
if (year != nullptr) *year = iyear;
if (month != nullptr) *month = imonth;
if (day != nullptr) *day = iday;
if (hour != nullptr) *hour = ihour;
if (minute != nullptr) *minute = imin;
if (second != nullptr) *second = isec;
if (timezone != nullptr) *timezone = static_cast<float>(itimezone) / 4.0;
// Final OK
thisModem().waitResponse();
return true;
}
bool getNetworkUTCTimeImpl(int* year, int* month, int* day, int* hour,
int* minute, int* second,
float* timezone) TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
#endif // SRC_TINYGSMTIME_H_

View file

@ -0,0 +1,61 @@
/**
* @file TinyGsmWifi.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMWIFI_H_
#define SRC_TINYGSMWIFI_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_WIFI
template <class modemType>
class TinyGsmWifi {
/* =========================================== */
/* =========================================== */
/*
* Define the interface
*/
public:
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd) {
return thisModem().networkConnectImpl(ssid, pwd);
}
bool networkDisconnect() {
return thisModem().networkDisconnectImpl();
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
~TinyGsmWifi() {}
/* =========================================== */
/* =========================================== */
/*
* Define the default function implementations
*/
/*
* WiFi functions
*/
bool networkConnectImpl(const char* ssid,
const char* pwd) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool networkDisconnectImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
};
#endif // SRC_TINYGSMWIFI_H_

View file

@ -0,0 +1,99 @@
/**************************************************************
*
* This script tries to auto-detect the baud rate
* and allows direct AT commands access
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
**************************************************************/
// Select your modem:
#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_SIM7000SSL
// #define TINY_GSM_MODEM_SIM7080
// #define TINY_GSM_MODEM_SIM5360
// #define TINY_GSM_MODEM_SIM7600
// #define TINY_GSM_MODEM_A7672X
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_SARAR5
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_ESP32
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// Set serial for debug console (to the Serial Monitor, speed 115200)
#define SerialMon Serial
// Set serial for AT commands (to the module)
// Use Hardware Serial on Mega, Leonardo, Micro
#ifndef __AVR_ATmega328P__
#define SerialAT Serial1
// or Software Serial on Uno, Nano
#else
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(2, 3); // RX, TX
#endif
#define TINY_GSM_DEBUG SerialMon
#include <TinyGsmClient.h>
// Module baud rate
uint32_t rate = 0; // Set to 0 for Auto-Detect
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(6000);
}
void loop() {
if (!rate) { rate = TinyGsmAutoBaud(SerialAT); }
if (!rate) {
SerialMon.println(
F("***********************************************************"));
SerialMon.println(F(" Module does not respond!"));
SerialMon.println(F(" Check your Serial wiring"));
SerialMon.println(
F(" Check the module is correctly powered and turned on"));
SerialMon.println(
F("***********************************************************"));
delay(30000L);
return;
}
SerialAT.begin(rate);
// Access AT commands from Serial Monitor
SerialMon.println(
F("***********************************************************"));
SerialMon.println(F(" You can now send AT commands"));
SerialMon.println(
F(" Enter \"AT\" (without quotes), and you should see \"OK\""));
SerialMon.println(
F(" If it doesn't work, select \"Both NL & CR\" in Serial Monitor"));
SerialMon.println(
F("***********************************************************"));
while (true) {
if (SerialAT.available()) { SerialMon.write(SerialAT.read()); }
if (SerialMon.available()) { SerialAT.write(SerialMon.read()); }
delay(0);
}
}

View file

@ -0,0 +1,46 @@
/**************************************************************
*
* This script just listens in on the communication
* between an Arduino and the modem.
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
**************************************************************/
// Set the baud rate between the modem and the board
#define BAUD_RATE 9600
// Set serial printing out the communication
#define SPY Serial
// Set serial for input from modem
#define MODEM_TX Serial1
// Set serial for AT commands (to the module)
// Use Hardware Serial on Mega, Leonardo, Micro
// #define BOARD_TX Serial1
// or AltSoftware
#include <AltSoftSerial.h>
AltSoftSerial BOARD_TX;
void setup() {
// Set console baud rate
SPY.begin(115200);
MODEM_TX.begin(BAUD_RATE);
BOARD_TX.begin(BAUD_RATE);
delay(6000);
}
void loop()
{
while (MODEM_TX.available()) {
SPY.write(MODEM_TX.read());
}
while (BOARD_TX.available()) {
SPY.write(BOARD_TX.read());
}
}

View file

@ -0,0 +1,289 @@
/**************************************************************
*
* To run this tool you need StreamDebugger library:
* https://github.com/vshymanskyy/StreamDebugger
* or from http://librarymanager/all#StreamDebugger
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
**************************************************************/
// Select your modem:
#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_SIM7000SSL
// #define TINY_GSM_MODEM_SIM7080
// #define TINY_GSM_MODEM_SIM5360
// #define TINY_GSM_MODEM_SIM7600
// #define TINY_GSM_MODEM_A7672X
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_ESP32
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
// 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
#ifndef __AVR_ATmega328P__
#define SerialAT Serial1
// or Software Serial on Uno, Nano
#else
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(2, 3); // RX, TX
#endif
// Increase RX buffer to capture the entire response
// Chips without internal buffering (A6/A7, ESP8266, M590)
// need enough space in the buffer for the entire response
// else data will be lost (and the http library will fail).
#ifndef TINY_GSM_RX_BUFFER
#define TINY_GSM_RX_BUFFER 1024
#endif
// See all AT commands, if wanted
// #define DUMP_AT_COMMANDS
// Define the serial console for debug prints, if needed
#define TINY_GSM_DEBUG SerialMon
// Range to attempt to autobaud
// NOTE: DO NOT AUTOBAUD in production code. Once you've established
// communication, set a fixed baud rate using modem.setBaud(#).
#define GSM_AUTOBAUD_MIN 9600
#define GSM_AUTOBAUD_MAX 115200
// Add a reception delay - may be needed for a fast processor at a slow baud
// rate #define TINY_GSM_YIELD() { delay(2); }
// Uncomment this if you want to use SSL
// #define USE_SSL
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
// set GSM PIN, if any
#define GSM_PIN ""
// Your GPRS credentials, if any
const char apn[] = "YourAPN";
const char gprsUser[] = "";
const char gprsPass[] = "";
// Your WiFi connection credentials, if applicable
const char wifiSSID[] = "YourSSID";
const char wifiPass[] = "YourWiFiPass";
// Server details
const char server[] = "vsh.pp.ua";
const char resource[] = "/TinyGSM/logo.txt";
#include <TinyGsmClient.h>
// Just in case someone defined the wrong thing..
#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS false
#define TINY_GSM_USE_WIFI true
#endif
#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI
#undef TINY_GSM_USE_GPRS
#undef TINY_GSM_USE_WIFI
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false
#endif
#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
#if defined(TINY_GSM_MODEM_HAS_SSL)
#define USE_SSL
#endif
#ifdef USE_SSL
TinyGsmClientSecure client(modem);
const int port = 443;
#else
TinyGsmClient client(modem);
const int port = 80;
#endif
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
// !!!!!!!!!!!
// Set your reset, enable, power pins here
// !!!!!!!!!!!
SerialMon.println("Wait...");
// Set GSM module baud rate
TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);
// SerialAT.begin(9600);
delay(6000);
}
void loop() {
// Restart takes quite some time
// To skip it, call init() instead of restart()
SerialMon.print("Initializing modem...");
if (!modem.restart()) {
// if (!modem.init()) {
SerialMon.println(F(" [fail]"));
SerialMon.println(F("************************"));
SerialMon.println(F(" Is your modem connected properly?"));
SerialMon.println(F(" Is your serial speed (baud rate) correct?"));
SerialMon.println(F(" Is your modem powered on?"));
SerialMon.println(F(" Do you use a good, stable power source?"));
SerialMon.println(F(" Try using File -> Examples -> TinyGSM -> tools -> "
"AT_Debug to find correct configuration"));
SerialMon.println(F("************************"));
delay(10000);
return;
}
SerialMon.println(F(" [OK]"));
String modemInfo = modem.getModemInfo();
SerialMon.print("Modem Info: ");
SerialMon.println(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
// Wifi connection parameters must be set before waiting for the network
SerialMon.print(F("Setting SSID/password..."));
if (!modem.networkConnect(wifiSSID, wifiPass)) {
SerialMon.println(" fail");
delay(10000);
return;
}
SerialMon.println(" success");
#endif
#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_XBEE
// The XBee must run the gprsConnect function BEFORE waiting for network!
modem.gprsConnect(apn, gprsUser, gprsPass);
#endif
SerialMon.print("Waiting for network...");
if (!modem.waitForNetwork(
600000L)) { // You may need lengthen this in poor service areas
SerialMon.println(F(" [fail]"));
SerialMon.println(F("************************"));
SerialMon.println(F(" Is your sim card locked?"));
SerialMon.println(F(" Do you have a good signal?"));
SerialMon.println(F(" Is antenna attached?"));
SerialMon.println(F(" Does the SIM card work with your phone?"));
SerialMon.println(F("************************"));
delay(10000);
return;
}
SerialMon.println(F(" [OK]"));
#if TINY_GSM_USE_GPRS
// GPRS connection parameters are usually set after network registration
SerialMon.print("Connecting to ");
SerialMon.print(apn);
if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
SerialMon.println(F(" [fail]"));
SerialMon.println(F("************************"));
SerialMon.println(F(" Is GPRS enabled by network provider?"));
SerialMon.println(F(" Try checking your card balance."));
SerialMon.println(F("************************"));
delay(10000);
return;
}
SerialMon.println(F(" [OK]"));
#endif
IPAddress local = modem.localIP();
SerialMon.print("Local IP: ");
SerialMon.println(local);
SerialMon.print(F("Connecting to "));
SerialMon.print(server);
if (!client.connect(server, port)) {
SerialMon.println(F(" [fail]"));
delay(10000);
return;
}
SerialMon.println(F(" [OK]"));
// Make a HTTP GET request:
client.print(String("GET ") + resource + " HTTP/1.0\r\n");
client.print(String("Host: ") + server + "\r\n");
client.print("Connection: close\r\n\r\n");
// Wait for data to arrive
while (client.connected() && !client.available()) {
delay(100);
SerialMon.print('.');
};
SerialMon.println();
// Skip all headers
client.find("\r\n\r\n");
// Read data
uint32_t timeout = millis();
uint32_t bytesReceived = 0;
while (client.connected() && millis() - timeout < 10000L) {
while (client.available()) {
char c = client.read();
// SerialMon.print(c);
bytesReceived += 1;
timeout = millis();
}
}
client.stop();
SerialMon.println(F("Server disconnected"));
#if TINY_GSM_USE_WIFI
modem.networkDisconnect();
SerialMon.println(F("WiFi disconnected"));
#endif
#if TINY_GSM_USE_GPRS
modem.gprsDisconnect();
SerialMon.println(F("GPRS disconnected"));
#endif
SerialMon.println();
SerialMon.println(F("************************"));
SerialMon.print(F(" Received: "));
SerialMon.print(bytesReceived);
SerialMon.println(F(" bytes"));
SerialMon.print(F(" Test: "));
SerialMon.println((bytesReceived == 121) ? "PASSED" : "FAILED");
SerialMon.println(F("************************"));
// Do nothing forevermore
while (true) { delay(1000); }
}

View file

@ -0,0 +1,84 @@
/**************************************************************
*
* To run this tool you need StreamDebugger library:
* https://github.com/vshymanskyy/StreamDebugger
* or from http://librarymanager/all#StreamDebugger
*
* TinyGSM Getting Started guide:
* https://tiny.cc/tinygsm-readme
*
**************************************************************/
// Select your modem:
#define TINY_GSM_MODEM_SIM800
// #define TINY_GSM_MODEM_SIM808
// #define TINY_GSM_MODEM_SIM868
// #define TINY_GSM_MODEM_SIM900
// #define TINY_GSM_MODEM_SIM5360
// #define TINY_GSM_MODEM_UBLOX
// #define TINY_GSM_MODEM_SARAR4
// #define TINY_GSM_MODEM_M95
// #define TINY_GSM_MODEM_BG95
// #define TINY_GSM_MODEM_BG96
// #define TINY_GSM_MODEM_A6
// #define TINY_GSM_MODEM_A7
// #define TINY_GSM_MODEM_M590
// #define TINY_GSM_MODEM_MC60
// #define TINY_GSM_MODEM_MC60E
// #define TINY_GSM_MODEM_ESP8266
// #define TINY_GSM_MODEM_ESP32
// #define TINY_GSM_MODEM_XBEE
// #define TINY_GSM_MODEM_SEQUANS_MONARCH
#include <TinyGsmClient.h>
// Set serial for debug console (to the Serial Monitor, speed 115200)
#define SerialMon Serial
// Set serial for AT commands (to the module)
// Use Hardware Serial on Mega, Leonardo, Micro
#ifndef __AVR_ATmega328P__
#define SerialAT Serial1
// or Software Serial on Uno, Nano
#else
#include <SoftwareSerial.h>
SoftwareSerial SerialAT(2, 3); // RX, TX
#endif
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
void setup() {
// Set console baud rate
SerialMon.begin(115200);
delay(10);
// Set GSM module baud rate
SerialAT.begin(9600);
delay(6000);
if (!modem.init()) {
SerialMon.println(
F("***********************************************************"));
SerialMon.println(F(" Cannot initialize modem!"));
SerialMon.println(
F(" Use File -> Examples -> TinyGSM -> tools -> AT_Debug"));
SerialMon.println(F(" to find correct configuration"));
SerialMon.println(
F("***********************************************************"));
return;
}
bool ret = modem.factoryDefault();
SerialMon.println(
F("***********************************************************"));
SerialMon.print(F(" Return settings to Factory Defaults: "));
SerialMon.println((ret) ? "OK" : "FAIL");
SerialMon.println(
F("***********************************************************"));
}
void loop() {}

View file

@ -0,0 +1,268 @@
/**************************************************************
*
* DO NOT USE THIS - this is just a compilation test!
* This is NOT an example for use of this library!
*
**************************************************************/
#include <TinyGsmClient.h>
TinyGsm modem(Serial);
void setup() {
Serial.begin(115200);
delay(6000);
}
void loop() {
// Test the basic functions
modem.begin();
modem.begin("1234");
modem.init();
modem.init("1234");
modem.setBaud(115200);
modem.testAT();
modem.getModemInfo();
modem.getModemName();
modem.getModemManufacturer();
modem.getModemModel();
modem.getModemRevision();
modem.factoryDefault();
#if not defined(TINY_GSM_MODEM_ESP8266) && not defined(TINY_GSM_MODEM_ESP32)
modem.getModemSerialNumber();
#endif
// Test Power functions
modem.restart();
// modem.sleepEnable(); // Not available for all modems
// modem.radioOff(); // Not available for all modems
modem.poweroff();
// modem.setPhoneFunctionality(1, 1); // Not available for all modems
// Test generic network functions
modem.getRegistrationStatus();
modem.isNetworkConnected();
modem.waitForNetwork();
modem.waitForNetwork(15000L);
modem.waitForNetwork(15000L, true);
modem.getSignalQuality();
modem.getLocalIP();
modem.localIP();
// Test WiFi Functions
#if defined(TINY_GSM_MODEM_HAS_WIFI)
modem.networkConnect("mySSID", "mySSIDPassword");
modem.networkDisconnect();
#endif
// Test the GPRS and SIM card functions
#if defined(TINY_GSM_MODEM_HAS_GPRS)
modem.simUnlock("1234");
modem.getSimStatus();
modem.gprsConnect("myAPN");
modem.gprsConnect("myAPN", "myUser");
modem.gprsConnect("myAPN", "myAPNUser", "myAPNPass");
modem.gprsDisconnect();
modem.getSimCCID();
modem.getIMEI();
modem.getIMSI();
modem.getOperator();
// modem.getProvider(); // Not available for all modems
#endif
char server[] = "somewhere";
char resource[] = "something";
// Test TCP functions
modem.maintain();
TinyGsmClient client;
TinyGsmClient client2(modem);
TinyGsmClient client3(modem, 1);
client.init(&modem);
client.init(&modem, 1);
client.connect(server, 80);
// Make a HTTP GET request:
client.print(String("GET ") + resource + " HTTP/1.0\r\n");
client.print(String("Host: ") + server + "\r\n");
client.print("Connection: close\r\n\r\n");
uint32_t timeout = millis();
while (client.connected() && millis() - timeout < 10000L) {
while (client.available()) {
client.read();
timeout = millis();
}
}
client.stop();
#if defined(TINY_GSM_MODEM_HAS_SSL)
// modem.addCertificate("certificateName"); // Not available for all modems
// modem.addCertificate(
// String("certificateName")); // Not available for all modems
// modem.addCertificate("certificateName", "certificate_content",
// 20); // Not available for all modems
// modem.addCertificate(String("certificateName"),
// String("certificate_content"),
// 20); // Not available for all
// modems
// modem.deleteCertificate("certificateName"); // Not available for all
// modems
modem.setCertificate("certificateName", 0);
modem.setCertificate(String("certificateName"), 0);
TinyGsmClientSecure client_secure(modem);
TinyGsmClientSecure client_secure2(modem);
TinyGsmClientSecure client_secure3(modem, 1);
client_secure.init(&modem);
client_secure.init(&modem, 1);
client_secure.connect(server, 443);
// Make a HTTP GET request:
client_secure.print(String("GET ") + resource + " HTTP/1.0\r\n");
client_secure.print(String("Host: ") + server + "\r\n");
client_secure.print("Connection: close\r\n\r\n");
timeout = millis();
while (client_secure.connected() && millis() - timeout < 10000L) {
while (client_secure.available()) {
client_secure.read();
timeout = millis();
}
}
client_secure.stop();
#endif
// Test the calling functions
#if defined(TINY_GSM_MODEM_HAS_CALLING) && not defined(__AVR_ATmega32U4__)
modem.callNumber(String("+380000000000"));
modem.callHangup();
#if not defined(TINY_GSM_MODEM_SEQUANS_MONARCH)
modem.callAnswer();
modem.dtmfSend('A', 1000);
#endif
#endif
// Test the SMS functions
#if defined(TINY_GSM_MODEM_HAS_SMS) && not defined(__AVR_ATmega32U4__)
modem.sendSMS(String("+380000000000"), String("Hello from "));
#if not defined(TINY_GSM_MODEM_XBEE) && not defined(TINY_GSM_MODEM_SARAR4)
modem.sendUSSD("*111#");
#endif
#if not defined(TINY_GSM_MODEM_XBEE) && not defined(TINY_GSM_MODEM_M590) && \
not defined(TINY_GSM_MODEM_SARAR4)
modem.sendSMS_UTF16("+380000000000", "Hello", 5);
#endif
#endif
// Test the GSM location functions
#if defined(TINY_GSM_MODEM_HAS_GSM_LOCATION) && not defined(__AVR_ATmega32U4__)
modem.getGsmLocationRaw();
modem.getGsmLocation();
float gsm_latitude = 0;
float gsm_longitude = 0;
float gsm_accuracy = 0;
int gsm_year = 0;
int gsm_month = 0;
int gsm_day = 0;
int gsm_hour = 0;
int gsm_minute = 0;
int gsm_second = 0;
modem.getGsmLocation(&gsm_latitude, &gsm_longitude);
modem.getGsmLocation(&gsm_latitude, &gsm_longitude, &gsm_accuracy, &gsm_year,
&gsm_month, &gsm_day, &gsm_hour, &gsm_minute,
&gsm_second);
modem.getGsmLocationTime(&gsm_year, &gsm_month, &gsm_day, &gsm_hour,
&gsm_minute, &gsm_second);
modem.getGsmLocation();
#endif
// Test the GPS functions
#if defined(TINY_GSM_MODEM_HAS_GPS) && not defined(__AVR_ATmega32U4__)
// modem.setGNSSMode(1, true); // Not available for all modems
// modem.getGNSSMode(); // Not available for all modems
#if !defined(TINY_GSM_MODEM_SARAR5) // not available for this module
modem.enableGPS();
#endif
float gps_latitude = 0;
float gps_longitude = 0;
float gps_speed = 0;
float gps_altitude = 0;
int gps_vsat = 0;
int gps_usat = 0;
float gps_accuracy = 0;
int gps_year = 0;
int gps_month = 0;
int gps_day = 0;
int gps_hour = 0;
int gps_minute = 0;
int gps_second = 0;
modem.getGPS(&gps_latitude, &gps_longitude);
modem.getGPS(&gps_latitude, &gps_longitude, &gps_speed, &gps_altitude,
&gps_vsat, &gps_usat, &gps_accuracy, &gps_year, &gps_month,
&gps_day, &gps_hour, &gps_minute, &gps_second);
modem.getGPSraw();
#if !defined(TINY_GSM_MODEM_SARAR5) // not available for this module
modem.disableGPS();
#endif
#endif
// Test the Network time functions
#if defined(TINY_GSM_MODEM_HAS_NTP) && not defined(__AVR_ATmega32U4__)
modem.NTPServerSync("pool.ntp.org", 3);
// modem.ShowNTPError(1); // Not available for all modems
#endif
// Test the Network time function
#if defined(TINY_GSM_MODEM_HAS_TIME) && not defined(__AVR_ATmega32U4__)
modem.getGSMDateTime(DATE_FULL);
int ntp_year = 0;
int ntp_month = 0;
int ntp_day = 0;
int ntp_hour = 0;
int ntp_min = 0;
int ntp_sec = 0;
float ntp_timezone = 0;
modem.getNetworkTime(&ntp_year, &ntp_month, &ntp_day, &ntp_hour, &ntp_min,
&ntp_sec, &ntp_timezone);
// modem.getNetworkUTCTime(&ntp_year, &ntp_month, &ntp_day, &ntp_hour,
// &ntp_min,
// &ntp_sec,
// &ntp_timezone); // Not available for all modems
#endif
// Test bluetooth functions
#if defined(TINY_GSM_MODEM_HAS_BLUETOOTH)
modem.enableBluetooth();
modem.disableBluetooth();
modem.setBluetoothVisibility(true);
modem.setBluetoothHostName("bluetooth");
#endif
// Test Battery functions
#if defined(TINY_GSM_MODEM_HAS_BATTERY)
// modem.getBattVoltage(); // Not available for all modems
// modem.getBattPercent(); // Not available for all modems
// modem.getBattChargeState(); // Not available for all modems
int8_t chargeState = -99;
int8_t chargePercent = -99;
int16_t milliVolts = -9999;
modem.getBattStats(chargeState, chargePercent, milliVolts);
#endif
// Test temperature functions
#if defined(TINY_GSM_MODEM_HAS_TEMPERATURE)
modem.getTemperature();
#endif
}