MY_GATEWAY_TINYGSM support. GSM gateway using a modem module (#1097)

Added TinyGSM driver and GatewayGSMMQTTClient example
This commit is contained in:
thucar
2018-04-04 17:40:11 +03:00
committed by Patrick Fallberg
parent b5a2fc226f
commit 4d90c00f98
20 changed files with 5862 additions and 6 deletions

165
drivers/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,67 @@
/**
* file TinyGsmClient.h
* author Volodymyr Shymanskyy
* license LGPL-3.0
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
* date Nov 2016
*/
#ifndef TinyGsmClient_h
#define TinyGsmClient_h
#if defined(TINY_GSM_MODEM_SIM800) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_U201) || defined(TINY_GSM_MODEM_ESP8266)
#define TINY_GSM_MODEM_HAS_SSL
#endif
#if defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_A7)
#define TINY_GSM_MODEM_HAS_GPS
#endif
#if defined(TINY_GSM_MODEM_SIM800) || defined(TINY_GSM_MODEM_SIM900)
#define TINY_GSM_MODEM_HAS_GPRS
#include "TinyGsmClientSIM800.h"
typedef TinyGsmSim800 TinyGsm;
typedef TinyGsmSim800::GsmClient TinyGsmClient;
typedef TinyGsmSim800::GsmClientSecure TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868)
#define TINY_GSM_MODEM_HAS_GPRS
#include "TinyGsmClientSIM808.h"
typedef TinyGsmSim808 TinyGsm;
typedef TinyGsmSim808::GsmClient TinyGsmClient;
typedef TinyGsmSim808::GsmClientSecure TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_A6) || defined(TINY_GSM_MODEM_A7)
#define TINY_GSM_MODEM_HAS_GPRS
#include "TinyGsmClientA6.h"
typedef TinyGsm::GsmClient TinyGsmClient;
#elif defined(TINY_GSM_MODEM_M590)
#define TINY_GSM_MODEM_HAS_GPRS
#include "TinyGsmClientM590.h"
typedef TinyGsm::GsmClient TinyGsmClient;
#elif defined(TINY_GSM_MODEM_U201)
#define TINY_GSM_MODEM_HAS_GPRS
#include "TinyGsmClientU201.h"
typedef TinyGsmU201 TinyGsm;
typedef TinyGsmU201::GsmClient TinyGsmClient;
typedef TinyGsmU201::GsmClientSecure TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_ESP8266)
#define TINY_GSM_MODEM_HAS_WIFI
#include "TinyGsmClientESP8266.h"
typedef TinyGsm::GsmClient TinyGsmClient;
typedef TinyGsm::GsmClientSecure TinyGsmClientSecure;
#elif defined(TINY_GSM_MODEM_XBEE)
#define TINY_GSM_MODEM_HAS_GPRS
#define TINY_GSM_MODEM_HAS_WIFI
#include "TinyGsmClientXBee.h"
typedef TinyGsm::GsmClient TinyGsmClient;
#else
#error "Please define GSM modem model"
#endif
#endif

View File

@@ -0,0 +1,843 @@
/**
* file TinyGsmClientA6.h
* author Volodymyr Shymanskyy
* license LGPL-3.0
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
* date Nov 2016
*/
#ifndef TinyGsmClientA6_h
#define TinyGsmClientA6_h
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 256
#endif
#define TINY_GSM_MUX_COUNT 8
#include "TinyGsmCommon.h"
#define GSM_NL "\r\n"
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
enum SimStatus {
SIM_ERROR = 0,
SIM_READY = 1,
SIM_LOCKED = 2,
};
enum RegStatus {
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsm
{
public:
class GsmClient : public Client
{
friend class TinyGsm;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public:
GsmClient() {}
GsmClient(TinyGsm& modem)
{
init(&modem);
}
bool init(TinyGsm* modem)
{
this->at = modem;
this->mux = -1;
sock_connected = false;
return true;
}
public:
virtual int connect(const char *host, uint16_t port)
{
stop();
TINY_GSM_YIELD();
rx.clear();
uint8_t newMux = -1;
sock_connected = at->modemConnect(host, port, &newMux);
if (sock_connected) {
mux = newMux;
at->sockets[mux] = this;
}
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port)
{
String host;
host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
virtual void stop()
{
TINY_GSM_YIELD();
at->sendAT(GF("+CIPCLOSE="), mux);
sock_connected = false;
at->waitResponse();
rx.clear();
}
virtual size_t write(const uint8_t *buf, size_t size)
{
TINY_GSM_YIELD();
//at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c)
{
return write(&c, 1);
}
virtual int available()
{
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
at->maintain();
}
return rx.size();
}
virtual int read(uint8_t *buf, size_t size)
{
TINY_GSM_YIELD();
size_t cnt = 0;
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?
if (!rx.size() && sock_connected) {
at->maintain();
//break;
}
}
return cnt;
}
virtual int read()
{
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
virtual int peek()
{
return -1; //TODO
}
virtual void flush()
{
at->stream.flush();
}
virtual uint8_t connected()
{
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool()
{
return connected();
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
TinyGsm* at;
uint8_t mux;
bool sock_connected;
RxFifo rx;
};
public:
explicit TinyGsm(Stream& stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
bool begin()
{
return init();
}
bool init()
{
if (!testAT()) {
return false;
}
sendAT(GF("&FZE0")); // Factory + Reset + Echo Off
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+CMEE=0"));
waitResponse();
sendAT(GF("+CMER=3,0,0,2"));
waitResponse();
getSimStatus();
return true;
}
void setBaud(unsigned long baud)
{
sendAT(GF("+IPR="), baud);
}
bool testAT(unsigned long timeout = 10000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
delay(100);
}
return false;
}
void maintain()
{
waitResponse(10, NULL, NULL);
}
bool factoryDefault()
{
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
waitResponse();
sendAT(GF("&W")); // Write configuration
return waitResponse() == 1;
}
String getModemInfo()
{
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
bool hasSSL()
{
return false;
}
/*
* Power functions
*/
bool restart()
{
if (!testAT()) {
return false;
}
sendAT(GF("+RST=1"));
delay(3000);
return init();
}
bool poweroff()
{
sendAT(GF("+CPOF"));
return waitResponse() == 1;
}
bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* SIM card functions
*/
bool simUnlock(const char *pin)
{
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
String getSimCCID()
{
sendAT(GF("+CCID"));
if (waitResponse(GF(GSM_NL "+SCID: SIM Card ID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
String getIMEI()
{
sendAT(GF("+GSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
SimStatus getSimStatus(unsigned long timeout = 10000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
continue;
}
int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"));
waitResponse();
switch (status) {
case 2:
case 3:
return SIM_LOCKED;
case 1:
return SIM_READY;
default:
return SIM_ERROR;
}
}
return SIM_ERROR;
}
RegStatus getRegistrationStatus()
{
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
String getOperator()
{
sendAT(GF("+COPS=3,0")); // Set format
waitResponse();
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
/*
* Generic network functions
*/
int getSignalQuality()
{
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
bool isNetworkConnected()
{
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
bool waitForNetwork(unsigned long timeout = 60000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
}
return false;
}
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL)
{
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 gprsDisconnect()
{
// 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;
}
bool isGprsConnected()
{
sendAT(GF("+CGATT?"));
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
return false;
}
int res = stream.readStringUntil('\n').toInt();
waitResponse();
return (res == 1);
}
String getLocalIP()
{
sendAT(GF("+CIFSR"));
String res;
if (waitResponse(10000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, "");
res.trim();
return res;
}
IPAddress localIP()
{
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool callAnswer()
{
sendAT(GF("A"));
return waitResponse() == 1;
}
// Returns true on pick-up, false on error/busy
bool callNumber(const String& number)
{
if (number == GF("last")) {
sendAT(GF("DLST"));
} else {
sendAT(GF("D\""), number, "\";");
}
if (waitResponse(5000L) != 1) {
return false;
}
if (waitResponse(60000L,
GF(GSM_NL "+CIEV: \"CALL\",1"),
GF(GSM_NL "+CIEV: \"CALL\",0"),
GFP(GSM_ERROR)) != 1) {
return false;
}
int rsp = waitResponse(60000L,
GF(GSM_NL "+CIEV: \"SOUNDER\",0"),
GF(GSM_NL "+CIEV: \"CALL\",0"));
int rsp2 = waitResponse(300L, GF(GSM_NL "BUSY" GSM_NL), GF(GSM_NL "NO ANSWER" GSM_NL));
return rsp == 1 && rsp2 == 0;
}
bool callHangup()
{
sendAT(GF("H"));
return waitResponse() == 1;
}
// 0-9,*,#,A,B,C,D
bool dtmfSend(char cmd, unsigned duration_ms = 100)
{
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
*/
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;
}
/*
* Messaging functions
*/
String sendUSSD(const String& code)
{
sendAT(GF("+CMGF=1"));
waitResponse();
sendAT(GF("+CSCS=\"HEX\""));
waitResponse();
sendAT(GF("+CUSD=1,\""), code, GF("\",15"));
if (waitResponse(10000L) != 1) {
return "";
}
if (waitResponse(GF(GSM_NL "+CUSD:")) != 1) {
return "";
}
stream.readStringUntil('"');
String hex = stream.readStringUntil('"');
stream.readStringUntil(',');
int dcs = stream.readStringUntil('\n').toInt();
if (dcs == 15) {
return TinyGsmDecodeHex7bit(hex);
} else if (dcs == 72) {
return TinyGsmDecodeHex16bit(hex);
} else {
return hex;
}
}
bool sendSMS(const String& number, const String& text)
{
sendAT(GF("+CMGF=1"));
waitResponse();
sendAT(GF("+CMGS=\""), number, GF("\""));
if (waitResponse(GF(">")) != 1) {
return false;
}
stream.print(text);
stream.write((char)0x1A);
stream.flush();
return waitResponse(60000L) == 1;
}
/*
* Location functions
*/
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery functions
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int getBattPercent()
{
sendAT(GF("+CBC?"));
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
return false;
}
stream.readStringUntil(',');
int res = stream.readStringUntil('\n').toInt();
waitResponse();
return res;
}
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t* mux)
{
sendAT(GF("+CIPSTART="), GF("\"TCP"), GF("\",\""), host, GF("\","), port);
if (waitResponse(75000L, GF(GSM_NL "+CIPNUM:")) != 1) {
return false;
}
int newMux = stream.readStringUntil('\n').toInt();
int rsp = waitResponse(75000L,
GF("CONNECT OK" GSM_NL),
GF("CONNECT FAIL" GSM_NL),
GF("ALREADY CONNECT" GSM_NL));
if (waitResponse() != 1) {
return false;
}
*mux = newMux;
return (1 == rsp);
}
int modemSend(const void* buff, size_t len, uint8_t mux)
{
sendAT(GF("+CIPSEND="), mux, ',', len);
if (waitResponse(2000L, GF(GSM_NL ">")) != 1) {
return 0;
}
stream.write((uint8_t*)buff, len);
stream.flush();
if (waitResponse(10000L, GFP(GSM_OK), GF(GSM_NL "FAIL")) != 1) {
return 0;
}
return len;
}
bool modemGetConnected(uint8_t mux)
{
sendAT(GF("+CIPSTATUS")); //TODO mux?
int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""),
GF(",\"INITIAL\""));
waitResponse();
return 1 == res;
}
public:
/* Utilities */
template<typename T>
void streamWrite(T last)
{
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail)
{
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(char c) //TODO: timeout
{
while (true) {
while (!stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c) {
return true;
}
}
return false;
}
template<typename... Args>
void sendAT(Args... cmd)
{
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
/*String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
data.reserve(64);
int index = 0;
unsigned long startMillis = millis();
do {
TINY_GSM_YIELD();
while (stream.available() > 0) {
int a = stream.read();
if (a <= 0) {
continue; // Skip 0x00 bytes, just in case
}
data += (char)a;
if (r1 && data.endsWith(r1)) {
index = 1;
goto finish;
} else if (r2 && data.endsWith(r2)) {
index = 2;
goto finish;
} else if (r3 && data.endsWith(r3)) {
index = 3;
goto finish;
} else if (r4 && data.endsWith(r4)) {
index = 4;
goto finish;
} else if (r5 && data.endsWith(r5)) {
index = 5;
goto finish;
} else if (data.endsWith(GF("+CIPRCV:"))) {
int mux = stream.readStringUntil(',').toInt();
int len = stream.readStringUntil(',').toInt();
int len_orig = len;
if (len > sockets[mux]->rx.free()) {
DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
} else {
DBG("### Got: ", len, "->", sockets[mux]->rx.free());
}
while (len--) {
while (!stream.available()) {
TINY_GSM_YIELD();
}
sockets[mux]->rx.put(stream.read());
}
if (len_orig > sockets[mux]->available()) { // TODO
DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
}
data = "";
} else if (data.endsWith(GF("+TCPCLOSED:"))) {
int mux = stream.readStringUntil('\n').toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
finish:
if (!index) {
data.trim();
if (data.length()) {
DBG("### Unhandled:", data);
}
data = "";
}
return index;
}
uint8_t waitResponse(uint32_t timeout,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
return waitResponse(1000, r1, r2, r3, r4, r5);
}
public:
Stream& stream;
protected:
GsmClient* sockets[TINY_GSM_MUX_COUNT];
};
#endif

View File

@@ -0,0 +1,591 @@
/**
* file TinyGsmClientESP8266.h
* author Volodymyr Shymanskyy
* license LGPL-3.0
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
* date Nov 2016
*/
#ifndef TinyGsmClientESP8266_h
#define TinyGsmClientESP8266_h
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 512
#endif
#define TINY_GSM_MUX_COUNT 5
#include "TinyGsmCommon.h"
#define GSM_NL "\r\n"
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
static unsigned TINY_GSM_TCP_KEEP_ALIVE = 120;
class TinyGsm
{
public:
class GsmClient : public Client
{
friend class TinyGsm;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public:
GsmClient() {}
GsmClient(TinyGsm& modem, uint8_t mux = 1)
{
init(&modem, mux);
}
bool init(TinyGsm* modem, uint8_t mux = 1)
{
this->at = modem;
this->mux = mux;
sock_connected = false;
at->sockets[mux] = this;
return true;
}
public:
virtual int connect(const char *host, uint16_t port)
{
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port)
{
String host;
host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
virtual void stop()
{
TINY_GSM_YIELD();
at->sendAT(GF("+CIPCLOSE="), mux);
sock_connected = false;
at->waitResponse();
rx.clear();
}
virtual size_t write(const uint8_t *buf, size_t size)
{
TINY_GSM_YIELD();
//at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c)
{
return write(&c, 1);
}
virtual int available()
{
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
at->maintain();
}
return rx.size();
}
virtual int read(uint8_t *buf, size_t size)
{
TINY_GSM_YIELD();
size_t cnt = 0;
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?
if (!rx.size() && sock_connected) {
at->maintain();
//break;
}
}
return cnt;
}
virtual int read()
{
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
virtual int peek()
{
return -1; //TODO
}
virtual void flush()
{
at->stream.flush();
}
virtual uint8_t connected()
{
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool()
{
return connected();
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
TinyGsm* at;
uint8_t mux;
bool sock_connected;
RxFifo rx;
};
class GsmClientSecure : public GsmClient
{
public:
GsmClientSecure() {}
GsmClientSecure(TinyGsm& modem, uint8_t mux = 1)
: GsmClient(modem, mux)
{}
public:
virtual int connect(const char *host, uint16_t port)
{
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux, true);
return sock_connected;
}
};
public:
explicit TinyGsm(Stream& stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
bool begin()
{
return init();
}
bool init()
{
if (!testAT()) {
return false;
}
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) {
return false;
}
return true;
}
void setBaud(unsigned long baud)
{
sendAT(GF("+IPR="), baud);
}
bool testAT(unsigned long timeout = 10000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
delay(100);
}
return false;
}
void maintain()
{
waitResponse(10, NULL, NULL);
}
bool factoryDefault()
{
sendAT(GF("+RESTORE"));
return waitResponse() == 1;
}
String getModemInfo()
{
sendAT(GF("+GMR"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
bool hasSSL()
{
return true;
}
/*
* Power functions
*/
bool restart()
{
if (!testAT()) {
return false;
}
sendAT(GF("+RST"));
if (waitResponse(10000L) != 1) {
return false;
}
if (waitResponse(10000L, GF(GSM_NL "ready" GSM_NL)) != 1) {
return false;
}
delay(500);
return init();
}
/*
* Generic network functions
*/
int getSignalQuality()
{
sendAT(GF("+CWJAP_CUR?"));
int res1 = waitResponse(GF("No AP"), GF("+CWJAP_CUR:"));
if (res1 != 2) {
waitResponse();
return 0;
}
streamSkipUntil(','); // Skip SSID
streamSkipUntil(','); // Skip BSSID/MAC address
streamSkipUntil(','); // Skip Chanel number
int res2 = stream.parseInt(); // Read RSSI
waitResponse(); // Returns an OK after the value
return res2;
}
bool isNetworkConnected()
{
sendAT(GF("+CIPSTATUS"));
int res1 = waitResponse(3000, GF("STATUS:"));
int res2 = 0;
if (res1 == 1) {
res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
}
// <stat> status of ESP8266 station interface
// 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 (but AP is connected)
// 5 : ESP8266 station did NOT connect to an AP
waitResponse(); // Returns an OK after the status
if (res2 == 2 || res2 == 3 || res2 == 4) {
return true;
} else {
return false;
}
}
bool waitForNetwork(unsigned long timeout = 60000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF("+CIPSTATUS"));
int res1 = waitResponse(3000, GF("busy p..."), GF("STATUS:"));
if (res1 == 2) {
int res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
if (res2 == 2 || res2 == 3 || res2 == 4) {
waitResponse();
return true;
}
}
delay(250);
}
return false;
}
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd)
{
sendAT(GF("+CIPMUX=1"));
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+CWMODE_CUR=1"));
if (waitResponse() != 1) {
return false;
}
sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\""));
if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) {
return false;
}
return true;
}
bool networkDisconnect()
{
sendAT(GF("+CWQAP"));
bool retVal = waitResponse(10000L) == 1;
waitResponse(GF("WIFI DISCONNECT"));
return retVal;
}
String getLocalIP()
{
sendAT(GF("+CIPSTA_CUR??"));
int res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:"));
if (res1 != 2) {
return "";
}
String res2 = stream.readStringUntil('"');
waitResponse();
return res2;
}
IPAddress localIP()
{
return TinyGsmIpFromString(getLocalIP());
}
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false)
{
if (ssl) {
sendAT(GF("+CIPSSLSIZE=4096"));
waitResponse();
}
sendAT(GF("+CIPSTART="), mux, ',', ssl ? GF("\"SSL") : GF("\"TCP"), GF("\",\""), host, GF("\","),
port, GF(","), TINY_GSM_TCP_KEEP_ALIVE);
// TODO: Check mux
int rsp = waitResponse(75000L,
GFP(GSM_OK),
GFP(GSM_ERROR),
GF("ALREADY CONNECT"));
// if (rsp == 3) waitResponse(); // May return "ERROR" after the "ALREADY CONNECT"
return (1 == rsp);
}
int modemSend(const void* buff, size_t len, uint8_t mux)
{
sendAT(GF("+CIPSEND="), mux, ',', len);
if (waitResponse(GF(">")) != 1) {
return 0;
}
stream.write((uint8_t*)buff, len);
stream.flush();
if (waitResponse(10000L, GF(GSM_NL "SEND OK" GSM_NL)) != 1) {
return 0;
}
return len;
}
bool modemGetConnected(uint8_t mux)
{
// TODO: re-check this
sendAT(GF("+CIPSTATUS="), mux);
int res1 = waitResponse(3000, GF("STATUS:"));
int res2;
if (res1 == 1) {
res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
}
// <stat> status of ESP8266 station interface
// 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 (but AP is connected)
// 5 : ESP8266 station did NOT connect to an AP
waitResponse(); // Returns an OK after the status
if (res2 == 2 || res2 == 3 || res2 == 4) {
return true;
} else {
return false;
}
}
public:
/* Utilities */
template<typename T>
void streamWrite(T last)
{
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail)
{
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(char c) //TODO: timeout
{
while (true) {
while (!stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c) {
return true;
}
}
return false;
}
template<typename... Args>
void sendAT(Args... cmd)
{
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
/*String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
data.reserve(64);
int index = 0;
unsigned long startMillis = millis();
do {
TINY_GSM_YIELD();
while (stream.available() > 0) {
int a = stream.read();
if (a <= 0) {
continue; // Skip 0x00 bytes, just in case
}
data += (char)a;
if (r1 && data.endsWith(r1)) {
index = 1;
goto finish;
} else if (r2 && data.endsWith(r2)) {
index = 2;
goto finish;
} else if (r3 && data.endsWith(r3)) {
index = 3;
goto finish;
} else if (r4 && data.endsWith(r4)) {
index = 4;
goto finish;
} else if (r5 && data.endsWith(r5)) {
index = 5;
goto finish;
} else if (data.endsWith(GF(GSM_NL "+IPD,"))) {
int mux = stream.readStringUntil(',').toInt();
int len = stream.readStringUntil(':').toInt();
int len_orig = len;
if (len > sockets[mux]->rx.free()) {
DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
} else {
DBG("### Got: ", len, "->", sockets[mux]->rx.free());
}
while (len--) {
while (!stream.available()) {
TINY_GSM_YIELD();
}
sockets[mux]->rx.put(stream.read());
}
if (len_orig > sockets[mux]->available()) { // TODO
DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
}
data = "";
} else if (data.endsWith(GF("CLOSED"))) {
int muxStart = max(0,data.lastIndexOf(GSM_NL, data.length()-8));
int coma = data.indexOf(',', muxStart);
int mux = data.substring(muxStart, coma).toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
finish:
if (!index) {
data.trim();
if (data.length()) {
DBG("### Unhandled:", data);
}
data = "";
}
return index;
}
uint8_t waitResponse(uint32_t timeout,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
return waitResponse(1000, r1, r2, r3, r4, r5);
}
public:
Stream& stream;
protected:
GsmClient* sockets[TINY_GSM_MUX_COUNT];
};
#endif

View File

@@ -0,0 +1,781 @@
/**
* file TinyGsmClientM590.h
* author Volodymyr Shymanskyy
* license LGPL-3.0
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
* date Nov 2016
*/
#ifndef TinyGsmClientM590_h
#define TinyGsmClientM590_h
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 256
#endif
#define TINY_GSM_MUX_COUNT 2
#include "TinyGsmCommon.h"
#define GSM_NL "\r\n"
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
enum SimStatus {
SIM_ERROR = 0,
SIM_READY = 1,
SIM_LOCKED = 2,
};
enum RegStatus {
REG_UNREGISTERED = 0,
REG_SEARCHING = 3,
REG_DENIED = 2,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsm
{
public:
class GsmClient : public Client
{
friend class TinyGsm;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public:
GsmClient() {}
GsmClient(TinyGsm& modem, uint8_t mux = 1)
{
init(&modem, mux);
}
bool init(TinyGsm* modem, uint8_t mux = 1)
{
this->at = modem;
this->mux = mux;
sock_connected = false;
at->sockets[mux] = this;
return true;
}
public:
virtual int connect(const char *host, uint16_t port)
{
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, mux);
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port)
{
String host;
host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
virtual void stop()
{
TINY_GSM_YIELD();
at->sendAT(GF("+TCPCLOSE="), mux);
sock_connected = false;
at->waitResponse();
rx.clear();
}
virtual size_t write(const uint8_t *buf, size_t size)
{
TINY_GSM_YIELD();
//at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c)
{
return write(&c, 1);
}
virtual int available()
{
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
at->maintain();
}
return rx.size();
}
virtual int read(uint8_t *buf, size_t size)
{
TINY_GSM_YIELD();
size_t cnt = 0;
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?
if (!rx.size() && sock_connected) {
at->maintain();
//break;
}
}
return cnt;
}
virtual int read()
{
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
virtual int peek()
{
return -1; //TODO
}
virtual void flush()
{
at->stream.flush();
}
virtual uint8_t connected()
{
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool()
{
return connected();
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
TinyGsm* at;
uint8_t mux;
bool sock_connected;
RxFifo rx;
};
public:
explicit TinyGsm(Stream& stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
bool begin()
{
return init();
}
bool init()
{
if (!testAT()) {
return false;
}
sendAT(GF("&FZE0")); // Factory + Reset + Echo Off
if (waitResponse() != 1) {
return false;
}
#ifdef TINY_GSM_DEBUG
sendAT(GF("+CMEE=2"));
waitResponse();
#endif
getSimStatus();
return true;
}
void setBaud(unsigned long baud)
{
sendAT(GF("+IPR="), baud);
}
bool testAT(unsigned long timeout = 10000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
delay(100);
}
return false;
}
void maintain()
{
//while (stream.available()) {
waitResponse(10, NULL, NULL);
//}
}
bool factoryDefault()
{
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;
}
String getModemInfo()
{
sendAT(GF("I"));
String res;
if (waitResponse(1000L, res) != 1) {
return "";
}
res.replace(GSM_NL "OK" GSM_NL, "");
res.replace(GSM_NL, " ");
res.trim();
return res;
}
bool hasSSL()
{
return false;
}
/*
* Power functions
*/
bool restart()
{
if (!testAT()) {
return false;
}
sendAT(GF("+CFUN=15"));
if (waitResponse(10000L) != 1) {
return false;
}
//MODEM:STARTUP
waitResponse(60000L, GF(GSM_NL "+PBREADY" GSM_NL));
return init();
}
bool poweroff()
{
sendAT(GF("+CPWROFF"));
return waitResponse(3000L) == 1;
}
bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sleepEnable(bool enable = true)
{
sendAT(GF("+ENPWRSAVE="), enable);
return waitResponse() == 1;
}
/*
* SIM card functions
*/
bool simUnlock(const char *pin)
{
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
String getSimCCID()
{
sendAT(GF("+CCID"));
if (waitResponse(GF(GSM_NL "+CCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
String getIMEI()
{
sendAT(GF("+GSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
SimStatus getSimStatus(unsigned long timeout = 10000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
continue;
}
int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"));
waitResponse();
switch (status) {
case 2:
case 3:
return SIM_LOCKED;
case 1:
return SIM_READY;
default:
return SIM_ERROR;
}
}
return SIM_ERROR;
}
RegStatus getRegistrationStatus()
{
sendAT(GF("+CREG?"));
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
String getOperator()
{
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
/*
* Generic network functions
*/
int getSignalQuality()
{
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
bool isNetworkConnected()
{
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
bool waitForNetwork(unsigned long timeout = 60000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
}
return false;
}
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL)
{
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 unsigned long timeout = 60000L;
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isGprsConnected()) {
//goto set_dns; // TODO
return true;
}
delay(500);
}
return false;
set_dns:
sendAT(GF("+DNSSERVER=1,8.8.8.8"));
waitResponse();
sendAT(GF("+DNSSERVER=2,8.8.4.4"));
waitResponse();
return true;
}
bool gprsDisconnect()
{
// TODO: There is no command in AT command set
// XIIC=0 does not work
return true;
}
bool isGprsConnected()
{
sendAT(GF("+XIIC?"));
if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) {
return false;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res == 1;
}
String getLocalIP()
{
sendAT(GF("+XIIC?"));
if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) {
return "";
}
stream.readStringUntil(',');
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
IPAddress localIP()
{
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
bool callAnswer() TINY_GSM_ATTR_NOT_AVAILABLE;
bool callNumber(const String& number) TINY_GSM_ATTR_NOT_AVAILABLE;
bool callHangup() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Messaging functions
*/
String sendUSSD(const String& code)
{
sendAT(GF("+CMGF=1"));
waitResponse();
sendAT(GF("+CSCS=\"HEX\""));
waitResponse();
sendAT(GF("D"), code);
if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) {
return "";
}
stream.readStringUntil('"');
String hex = stream.readStringUntil('"');
stream.readStringUntil(',');
int dcs = stream.readStringUntil('\n').toInt();
if (waitResponse() != 1) {
return "";
}
if (dcs == 15) {
return TinyGsmDecodeHex8bit(hex);
} else if (dcs == 72) {
return TinyGsmDecodeHex16bit(hex);
} else {
return hex;
}
}
bool sendSMS(const String& number, const String& text)
{
sendAT(GF("+CSCS=\"GSM\""));
waitResponse();
sendAT(GF("+CMGF=1"));
waitResponse();
sendAT(GF("+CMGS=\""), number, GF("\""));
if (waitResponse(GF(">")) != 1) {
return false;
}
stream.print(text);
stream.write((char)0x1A);
stream.flush();
return waitResponse(60000L) == 1;
}
bool sendSMS_UTF16(const String& number, const void* text, size_t len)
TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Location functions
*/
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
/*
* Battery functions
*/
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t mux)
{
for (int i=0; i<3; i++) { // TODO: no need for loop?
String ip = dnsIpQuery(host);
sendAT(GF("+TCPSETUP="), mux, GF(","), ip, GF(","), port);
int rsp = waitResponse(75000L,
GF(",OK" GSM_NL),
GF(",FAIL" GSM_NL),
GF("+TCPSETUP:Error" GSM_NL));
if (1 == rsp) {
return true;
} else if (3 == rsp) {
sendAT(GF("+TCPCLOSE="), mux);
waitResponse();
}
delay(1000);
}
return false;
}
int modemSend(const void* buff, size_t len, uint8_t mux)
{
sendAT(GF("+TCPSEND="), mux, ',', len);
if (waitResponse(GF(">")) != 1) {
return 0;
}
stream.write((uint8_t*)buff, len);
stream.write((char)0x0D);
stream.flush();
if (waitResponse(30000L, GF(GSM_NL "+TCPSEND:")) != 1) {
return 0;
}
stream.readStringUntil('\n');
return len;
}
bool modemGetConnected(uint8_t mux)
{
sendAT(GF("+CIPSTATUS="), mux);
int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""),
GF(",\"INITIAL\""));
waitResponse();
return 1 == res;
}
String dnsIpQuery(const char* host)
{
sendAT(GF("+DNS=\""), host, GF("\""));
if (waitResponse(10000L, GF(GSM_NL "+DNS:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse(GF("+DNS:OK" GSM_NL));
res.trim();
return res;
}
public:
/* Utilities */
template<typename T>
void streamWrite(T last)
{
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail)
{
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(char c) //TODO: timeout
{
while (true) {
while (!stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c) {
return true;
}
}
return false;
}
template<typename... Args>
void sendAT(Args... cmd)
{
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
/*String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
data.reserve(64);
int index = 0;
unsigned long startMillis = millis();
do {
TINY_GSM_YIELD();
while (stream.available() > 0) {
int a = stream.read();
if (a <= 0) {
continue; // Skip 0x00 bytes, just in case
}
data += (char)a;
if (r1 && data.endsWith(r1)) {
index = 1;
goto finish;
} else if (r2 && data.endsWith(r2)) {
index = 2;
goto finish;
} else if (r3 && data.endsWith(r3)) {
index = 3;
goto finish;
} else if (r4 && data.endsWith(r4)) {
index = 4;
goto finish;
} else if (r5 && data.endsWith(r5)) {
index = 5;
goto finish;
} else if (data.endsWith(GF("+TCPRECV:"))) {
int mux = stream.readStringUntil(',').toInt();
int len = stream.readStringUntil(',').toInt();
int len_orig = len;
if (len > sockets[mux]->rx.free()) {
DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
} else {
DBG("### Got: ", len, "->", sockets[mux]->rx.free());
}
while (len--) {
while (!stream.available()) {
TINY_GSM_YIELD();
}
sockets[mux]->rx.put(stream.read());
}
if (len_orig > sockets[mux]->available()) { // TODO
DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
}
data = "";
} else if (data.endsWith(GF("+TCPCLOSE:"))) {
int mux = stream.readStringUntil(',').toInt();
stream.readStringUntil('\n');
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
finish:
if (!index) {
data.trim();
if (data.length()) {
DBG("### Unhandled:", data);
}
data = "";
}
return index;
}
uint8_t waitResponse(uint32_t timeout,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
return waitResponse(1000, r1, r2, r3, r4, r5);
}
public:
Stream& stream;
protected:
GsmClient* sockets[TINY_GSM_MUX_COUNT];
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,163 @@
/**
* file TinyGsmClientSIM808.h
* author Volodymyr Shymanskyy
* license LGPL-3.0
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
* date Nov 2016
*/
#ifndef TinyGsmClientSIM808_h
#define TinyGsmClientSIM808_h
#include "TinyGsmClientSIM800.h"
class TinyGsmSim808: public TinyGsmSim800
{
public:
explicit TinyGsmSim808(Stream& stream)
: TinyGsmSim800(stream)
{}
/*
* GPS location functions
*/
// enable GPS
bool enableGPS()
{
uint16_t state;
sendAT(GF("+CGNSPWR=1"));
if (waitResponse() != 1) {
return false;
}
return true;
}
bool disableGPS()
{
uint16_t state;
sendAT(GF("+CGNSPWR=0"));
if (waitResponse() != 1) {
return false;
}
return true;
}
// get the RAW GPS output
// works only with ans SIM808 V2
String getGPSraw()
{
sendAT(GF("+CGNSINF"));
if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
// get GPS informations
// works only with ans SIM808 V2
bool getGPS(float *lat, float *lon, float *speed=0, int *alt=0, int *vsat=0, int *usat=0)
{
//String buffer = "";
char chr_buffer[12];
bool fix = false;
sendAT(GF("+CGNSINF"));
if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) {
return false;
}
stream.readStringUntil(','); // mode
if ( stream.readStringUntil(',').toInt() == 1 ) {
fix = true;
}
stream.readStringUntil(','); //utctime
*lat = stream.readStringUntil(',').toFloat(); //lat
*lon = stream.readStringUntil(',').toFloat(); //lon
if (alt != NULL) {
*alt = stream.readStringUntil(',').toFloat(); //lon
}
if (speed != NULL) {
*speed = stream.readStringUntil(',').toFloat(); //speed
}
stream.readStringUntil(',');
stream.readStringUntil(',');
stream.readStringUntil(',');
stream.readStringUntil(',');
stream.readStringUntil(',');
stream.readStringUntil(',');
stream.readStringUntil(',');
if (vsat != NULL) {
*vsat = stream.readStringUntil(',').toInt(); //viewed satelites
}
if (usat != NULL) {
*usat = stream.readStringUntil(',').toInt(); //used satelites
}
stream.readStringUntil('\n');
waitResponse();
return fix;
}
// get GPS time
// works only with SIM808 V2
bool getGPSTime(int *year, int *month, int *day, int *hour, int *minute, int *second)
{
bool fix = false;
char chr_buffer[12];
sendAT(GF("+CGNSINF"));
if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) {
return false;
}
for (int i = 0; i < 3; i++) {
String buffer = stream.readStringUntil(',');
buffer.toCharArray(chr_buffer, sizeof(chr_buffer));
switch (i) {
case 0:
//mode
break;
case 1:
//fixstatus
if ( buffer.toInt() == 1 ) {
fix = buffer.toInt();
}
break;
case 2:
*year = buffer.substring(0,4).toInt();
*month = buffer.substring(4,6).toInt();
*day = buffer.substring(6,8).toInt();
*hour = buffer.substring(8,10).toInt();
*minute = buffer.substring(10,12).toInt();
*second = buffer.substring(12,14).toInt();
break;
default:
// if nothing else matches, do the default
// default is optional
break;
}
}
String res = stream.readStringUntil('\n');
waitResponse();
if (fix) {
return true;
} else {
return false;
}
}
};
#endif

View File

@@ -0,0 +1,789 @@
/**
* file TinyGsmClientU201.h
* author Volodymyr Shymanskyy
* license LGPL-3.0
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
* date Nov 2016
*/
#ifndef TinyGsmClientU201_h
#define TinyGsmClientU201_h
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 64
#endif
#define TINY_GSM_MUX_COUNT 5
#include "TinyGsmCommon.h"
#define GSM_NL "\r\n"
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
enum SimStatus {
SIM_ERROR = 0,
SIM_READY = 1,
SIM_LOCKED = 2,
};
enum RegStatus {
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsmU201
{
public:
class GsmClient : public Client
{
friend class TinyGsmU201;
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
public:
GsmClient() {}
GsmClient(TinyGsmU201& modem, uint8_t mux = 1)
{
init(&modem, mux);
}
bool init(TinyGsmU201* modem, uint8_t mux = 1)
{
this->at = modem;
this->mux = mux;
sock_available = 0;
sock_connected = false;
got_data = false;
return true;
}
public:
virtual int connect(const char *host, uint16_t port)
{
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, &mux);
at->sockets[mux] = this;
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port)
{
String host;
host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
return connect(host.c_str(), port);
}
virtual void stop()
{
TINY_GSM_YIELD();
at->sendAT(GF("+USOCL="), mux);
sock_connected = false;
at->waitResponse();
rx.clear();
}
virtual size_t write(const uint8_t *buf, size_t size)
{
TINY_GSM_YIELD();
at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c)
{
return write(&c, 1);
}
virtual int available()
{
TINY_GSM_YIELD();
if (!rx.size() && sock_connected) {
at->maintain();
}
return rx.size() + sock_available;
}
virtual int read(uint8_t *buf, size_t size)
{
TINY_GSM_YIELD();
at->maintain();
size_t cnt = 0;
while (cnt < size) {
size_t chunk = TinyGsmMin(size-cnt, rx.size());
if (chunk > 0) {
rx.get(buf, chunk);
buf += chunk;
cnt += chunk;
continue;
}
// TODO: Read directly into user buffer?
at->maintain();
if (sock_available > 0) {
at->modemRead(rx.free(), mux);
} else {
break;
}
}
return cnt;
}
virtual int read()
{
uint8_t c;
if (read(&c, 1) == 1) {
return c;
}
return -1;
}
virtual int peek()
{
return -1; //TODO
}
virtual void flush()
{
at->stream.flush();
}
virtual uint8_t connected()
{
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool()
{
return connected();
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
TinyGsmU201* at;
uint8_t mux;
uint16_t sock_available;
bool sock_connected;
bool got_data;
RxFifo rx;
};
class GsmClientSecure : public GsmClient
{
public:
GsmClientSecure() {}
GsmClientSecure(TinyGsmU201& modem, uint8_t mux = 1)
: GsmClient(modem, mux)
{}
public:
virtual int connect(const char *host, uint16_t port)
{
stop();
TINY_GSM_YIELD();
rx.clear();
sock_connected = at->modemConnect(host, port, &mux, true);
at->sockets[mux] = this;
return sock_connected;
}
};
public:
#ifdef GSM_DEFAULT_STREAM
explicit TinyGsmU201(Stream& stream = GSM_DEFAULT_STREAM)
#else
explicit TinyGsmU201(Stream& stream)
#endif
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
bool begin(const char* pin = NULL)
{
return init(pin);
}
bool init(const char* pin = NULL)
{
if (!testAT()) {
return false;
}
sendAT(GF("E0")); // Echo Off
if (waitResponse() != 1) {
return false;
}
int ret = getSimStatus();
if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
simUnlock(pin);
}
return (getSimStatus() == SIM_READY);
}
void setBaud(unsigned long baud)
{
sendAT(GF("+IPR="), baud);
}
bool testAT(unsigned long timeout = 10000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF(""));
if (waitResponse(200) == 1) {
delay(100);
return true;
}
delay(100);
}
return false;
}
void maintain()
{
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
GsmClient* sock = sockets[mux];
if (sock && sock->got_data) {
sock->got_data = false;
sock->sock_available = modemGetAvailable(mux);
}
}
while (stream.available()) {
waitResponse(10, NULL, NULL);
}
}
bool factoryDefault()
{
sendAT(GF("+UFACTORY=0,1")); // Factory + Reset + Echo Off
waitResponse();
sendAT(GF("+CFUN=16")); // Auto-baud
return waitResponse() == 1;
}
String getModemInfo() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool hasSSL()
{
return true;
}
/*
* Power functions
*/
bool restart()
{
if (!testAT()) {
return false;
}
sendAT(GF("+CFUN=16"));
if (waitResponse(10000L) != 1) {
return false;
}
delay(3000);
return init();
}
bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* SIM card functions
*/
bool simUnlock(const char *pin)
{
sendAT(GF("+CPIN=\""), pin, GF("\""));
return waitResponse() == 1;
}
String getSimCCID()
{
sendAT(GF("+CCID"));
if (waitResponse(GF(GSM_NL "+CCID:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
String getIMEI()
{
sendAT(GF("+CGSN"));
if (waitResponse(GF(GSM_NL)) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
SimStatus getSimStatus(unsigned long timeout = 10000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
sendAT(GF("+CPIN?"));
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
delay(1000);
continue;
}
int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED"));
waitResponse();
switch (status) {
case 2:
case 3:
return SIM_LOCKED;
case 1:
return SIM_READY;
default:
return SIM_ERROR;
}
}
return SIM_ERROR;
}
RegStatus getRegistrationStatus()
{
sendAT(GF("+CGREG?"));
if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) {
return REG_UNKNOWN;
}
streamSkipUntil(','); // Skip format (0)
int status = stream.readStringUntil('\n').toInt();
waitResponse();
return (RegStatus)status;
}
String getOperator()
{
sendAT(GF("+COPS?"));
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
return "";
}
streamSkipUntil('"'); // Skip mode and format
String res = stream.readStringUntil('"');
waitResponse();
return res;
}
/*
* Generic network functions
*/
int getSignalQuality()
{
sendAT(GF("+CSQ"));
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
return 99;
}
int res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
bool isNetworkConnected()
{
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
bool waitForNetwork(unsigned long timeout = 60000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
}
return false;
}
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL)
{
gprsDisconnect();
sendAT(GF("+CGATT=1"));
waitResponse(5000L);
sendAT(GF("+UPSD=0,1,\""), apn, '"');
waitResponse();
if (user && strlen(user) > 0) {
sendAT(GF("+UPSD=0,2,\""), user, '"');
waitResponse();
}
if (pwd && strlen(pwd) > 0) {
sendAT(GF("+UPSD=0,3,\""), pwd, '"');
waitResponse();
}
sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP
waitResponse();
sendAT(GF("+UPSDA=0,3"));
waitResponse(6000L);
// Open a GPRS context
sendAT(GF("+UPSND=0,8"));
if (waitResponse(GF(",8,1")) != 1) {
return false;
}
return true;
}
bool gprsDisconnect()
{
sendAT(GF("+UPSDA=0,4"));
if (waitResponse(60000L) != 1) {
return false;
}
sendAT(GF("+CGATT=0"));
if (waitResponse(60000L) != 1) {
return false;
}
return true;
}
bool isGprsConnected()
{
sendAT(GF("+CGATT?"));
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
return false;
}
int res = stream.readStringUntil('\n').toInt();
waitResponse();
if (res != 1) {
return false;
}
sendAT(GF("+CIFSR"));
if (waitResponse() != 1) {
return false;
}
return true;
}
String getLocalIP()
{
sendAT(GF("+CIFSR;E0"));
String res;
if (waitResponse(10000L, res) != 1) {
return "";
}
res.trim();
return res;
}
IPAddress localIP()
{
return TinyGsmIpFromString(getLocalIP());
}
/*
* Phone Call functions
*/
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Messaging functions
*/
String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sendSMS(const String& number, const String& text) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sendSMS_UTF16(const String& number, const void* text,
size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED;
/*
* Location functions
*/
String getGsmLocation()
{
sendAT(GF("+ULOC=2,3,0,120,1"));
if (waitResponse(GF(GSM_NL "+UULOC:")) != 1) {
return "";
}
String res = stream.readStringUntil('\n');
waitResponse();
res.trim();
return res;
}
/*
* Battery functions
*/
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
uint16_t getBattVoltage()
{
sendAT(GF("+CIND"));
if (waitResponse(GF(GSM_NL "+CIND:")) != 1) {
return 0;
}
uint16_t res = stream.readStringUntil(',').toInt();
waitResponse();
return res;
}
int getBattPercent() TINY_GSM_ATTR_NOT_IMPLEMENTED;
protected:
bool modemConnect(const char* host, uint16_t port, uint8_t* mux, bool ssl = false)
{
sendAT(GF("+USOCR=6"));
if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) {
return false;
}
*mux = stream.readStringUntil('\n').toInt();
waitResponse();
if (ssl) {
sendAT(GF("+USOSEC="), *mux, ",1");
waitResponse();
}
sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port);
int rsp = waitResponse(75000L);
return (1 == rsp);
}
int modemSend(const void* buff, size_t len, uint8_t mux)
{
sendAT(GF("+USOWR="), mux, ',', len);
if (waitResponse(GF("@")) != 1) {
return -1;
}
// 50ms delay, see AT manual section 25.10.4
delay(50);
stream.write((uint8_t*)buff, len);
stream.flush();
if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) {
return -1;
}
streamSkipUntil(','); // Skip mux
return stream.readStringUntil('\n').toInt();
}
size_t modemRead(size_t size, uint8_t mux)
{
sendAT(GF("+USORD="), mux, ',', size);
if (waitResponse(GF(GSM_NL "+USORD:")) != 1) {
return 0;
}
streamSkipUntil(','); // Skip mux
size_t len = stream.readStringUntil(',').toInt();
streamSkipUntil('\"');
for (size_t i=0; i<len; i++) {
while (!stream.available()) {
TINY_GSM_YIELD();
}
char c = stream.read();
sockets[mux]->rx.put(c);
}
streamSkipUntil('\"');
waitResponse();
return len;
}
size_t modemGetAvailable(uint8_t mux)
{
sendAT(GF("+USORD="), mux, ',', 0);
size_t result = 0;
if (waitResponse(GF(GSM_NL "+USORD:")) == 1) {
streamSkipUntil(','); // Skip mux
result = stream.readStringUntil('\n').toInt();
waitResponse();
}
if (!result) {
sockets[mux]->sock_connected = modemGetConnected(mux);
}
return result;
}
bool modemGetConnected(uint8_t mux)
{
sendAT(GF("+USOCTL="), mux, ",10");
if (waitResponse(GF(GSM_NL "+USOCTL:")) != 1) {
return false;
}
streamSkipUntil(','); // Skip mux
streamSkipUntil(','); // Skip type
int result = stream.readStringUntil('\n').toInt();
return result != 0;
}
public:
/* Utilities */
template<typename T>
void streamWrite(T last)
{
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail)
{
stream.print(head);
streamWrite(tail...);
}
bool streamSkipUntil(char c) //TODO: timeout
{
while (true) {
while (!stream.available()) {
TINY_GSM_YIELD();
}
if (stream.read() == c) {
return true;
}
}
return false;
}
template<typename... Args>
void sendAT(Args... cmd)
{
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
/*String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
data.reserve(64);
int index = 0;
unsigned long startMillis = millis();
do {
TINY_GSM_YIELD();
while (stream.available() > 0) {
int a = stream.read();
if (a < 0) {
continue;
}
data += (char)a;
if (r1 && data.endsWith(r1)) {
index = 1;
goto finish;
} else if (r2 && data.endsWith(r2)) {
index = 2;
goto finish;
} else if (r3 && data.endsWith(r3)) {
index = 3;
goto finish;
} else if (r4 && data.endsWith(r4)) {
index = 4;
goto finish;
} else if (r5 && data.endsWith(r5)) {
index = 5;
goto finish;
} else if (data.endsWith(GF(GSM_NL "+UUSORD:"))) {
int mux = stream.readStringUntil(',').toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->got_data = true;
}
data = "";
} else if (data.endsWith(GF(GSM_NL "+UUSOCL:"))) {
int mux = stream.readStringUntil('\n').toInt();
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
sockets[mux]->sock_connected = false;
}
data = "";
DBG("### Closed: ", mux);
}
}
} while (millis() - startMillis < timeout);
finish:
if (!index) {
data.trim();
if (data.length()) {
DBG("### Unhandled:", data);
}
data = "";
}
return index;
}
uint8_t waitResponse(uint32_t timeout,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
return waitResponse(1000, r1, r2, r3, r4, r5);
}
public:
Stream& stream;
protected:
GsmClient* sockets[TINY_GSM_MUX_COUNT];
};
#endif

View File

@@ -0,0 +1,752 @@
/**
* file TinyGsmClientXBee.h
* author Volodymyr Shymanskyy
* license LGPL-3.0
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
* date Nov 2016
*/
#ifndef TinyGsmClientXBee_h
#define TinyGsmClientXBee_h
//#define TINY_GSM_DEBUG Serial
#if !defined(TINY_GSM_RX_BUFFER)
#define TINY_GSM_RX_BUFFER 256
#endif
#define TINY_GSM_MUX_COUNT 1 // Multi-plexing isn't supported using command mode
#include "TinyGsmCommon.h"
#define GSM_NL "\r"
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
enum SimStatus {
SIM_ERROR = 0,
SIM_READY = 1,
SIM_LOCKED = 2,
};
enum XBeeType {
S6B = 0,
LTEC1 = 1,
};
enum RegStatus {
REG_UNREGISTERED = 0,
REG_SEARCHING = 2,
REG_DENIED = 3,
REG_OK_HOME = 1,
REG_OK_ROAMING = 5,
REG_UNKNOWN = 4,
};
class TinyGsm
{
public:
class GsmClient : public Client
{
friend class TinyGsm;
public:
GsmClient() {}
GsmClient(TinyGsm& modem, uint8_t mux = 0)
{
init(&modem, mux);
}
bool init(TinyGsm* modem, uint8_t mux = 0)
{
this->at = modem;
this->mux = mux;
sock_connected = false;
at->sockets[mux] = this;
return true;
}
public:
virtual int connect(const char *host, uint16_t port)
{
at->streamClear(); // Empty anything remaining in the buffer;
at->commandMode();
sock_connected = at->modemConnect(host, port, mux, false);
at->writeChanges();
at->exitCommand();
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port)
{
at->streamClear(); // Empty anything remaining in the buffer;
at->commandMode();
sock_connected = at->modemConnect(ip, port, mux, false);
at->writeChanges();
at->exitCommand();
return sock_connected;
}
// This is a hack to shut the socket by setting the timeout to zero and
// then sending an empty line to the server.
virtual void stop()
{
at->streamClear(); // Empty anything remaining in the buffer;
at->commandMode();
at->sendAT(GF("TM0")); // Set socket timeout to 0;
at->waitResponse();
at->writeChanges();
at->exitCommand();
at->modemSend("", 1, mux);
at->commandMode();
at->sendAT(GF("TM64")); // Set socket timeout back to 10seconds;
at->waitResponse();
at->writeChanges();
at->exitCommand();
at->streamClear(); // Empty anything remaining in the buffer;
sock_connected = false;
}
virtual size_t write(const uint8_t *buf, size_t size)
{
TINY_GSM_YIELD();
//at->maintain();
return at->modemSend(buf, size, mux);
}
virtual size_t write(uint8_t c)
{
return write(&c, 1);
}
virtual int available()
{
TINY_GSM_YIELD();
return at->stream.available();
}
virtual int read(uint8_t *buf, size_t size)
{
TINY_GSM_YIELD();
return at->stream.readBytes(buf, size);
}
virtual int read()
{
TINY_GSM_YIELD();
return at->stream.read();
}
virtual int peek()
{
return at->stream.peek();
}
virtual void flush()
{
at->stream.flush();
}
virtual uint8_t connected()
{
if (available()) {
return true;
}
return sock_connected;
}
virtual operator bool()
{
return connected();
}
/*
* Extended API
*/
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
private:
TinyGsm* at;
uint8_t mux;
bool sock_connected;
};
class GsmClientSecure : public GsmClient
{
public:
GsmClientSecure() {}
GsmClientSecure(TinyGsm& modem, uint8_t mux = 1)
: GsmClient(modem, mux)
{}
public:
virtual int connect(const char *host, uint16_t port)
{
at->streamClear(); // Empty anything remaining in the buffer;
at->commandMode();
sock_connected = at->modemConnect(host, port, mux, true);
at->writeChanges();
at->exitCommand();
return sock_connected;
}
virtual int connect(IPAddress ip, uint16_t port)
{
at->streamClear(); // Empty anything remaining in the buffer;
at->commandMode();
sock_connected = at->modemConnect(ip, port, mux, true);
at->writeChanges();
at->exitCommand();
return sock_connected;
}
};
public:
explicit TinyGsm(Stream& stream)
: stream(stream)
{
memset(sockets, 0, sizeof(sockets));
}
/*
* Basic functions
*/
bool begin()
{
return init();
}
bool init()
{
guardTime = 1100;
commandMode();
sendAT(GF("AP0")); // Put in transparent mode
waitResponse();
sendAT(GF("GT64")); // shorten the guard time to 100ms
waitResponse();
writeChanges();
sendAT(GF("HS")); // Get the "Hardware Series"; 0x601 for S6B (Wifi)
// wait for the response
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < 1000) {};
String res = streamReadUntil('\r'); // Does not send an OK, just the result
exitCommand();
if (res == "601") {
beeType = S6B;
} else {
beeType = LTEC1;
}
guardTime = 125;
return true;
}
bool testAT(unsigned long timeout = 10000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (commandMode()) {
sendAT();
if (waitResponse(200) == 1) {
return true;
}
exitCommand();
}
delay(100);
}
return false;
}
void maintain() {}
bool factoryDefault()
{
commandMode();
sendAT(GF("RE"));
bool ret_val = waitResponse() == 1;
writeChanges();
exitCommand();
return ret_val;
}
bool hasSSL()
{
if (beeType == S6B) {
return false;
} else {
return true;
}
}
/*
* Power functions
*/
bool restart()
{
commandMode();
sendAT(GF("FR"));
if (waitResponse() != 1) {
return false;
}
delay (2000); // Actually resets about 2 seconds later
for (unsigned long start = millis(); millis() - start < 60000L; ) {
if (commandMode()) {
exitCommand();
return true;
}
}
exitCommand();
return false;;
}
void setupPinSleep()
{
commandMode();
sendAT(GF("SM"),1);
waitResponse();
if (beeType == S6B) {
sendAT(GF("SO"),200);
waitResponse();
}
writeChanges();
exitCommand();
}
/*
* SIM card functions
*/
bool simUnlock(const char *pin) // Not supported
{
return false;
}
String getSimCCID()
{
commandMode();
sendAT(GF("S#"));
// wait for the response
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < 1000) {};
String res = streamReadUntil('\r'); // Does not send an OK, just the result
exitCommand();
return res;
}
String getIMEI()
{
commandMode();
sendAT(GF("IM"));
// wait for the response
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < 1000) {};
String res = streamReadUntil('\r'); // Does not send an OK, just the result
exitCommand();
return res;
}
SimStatus getSimStatus(unsigned long timeout = 10000L)
{
return SIM_READY; // unsupported
}
RegStatus getRegistrationStatus()
{
commandMode();
sendAT(GF("AI"));
// wait for the response
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < 1000) {};
String res = streamReadUntil('\r'); // Does not send an OK, just the result
exitCommand();
if(res == GF("0")) {
return REG_OK_HOME;
}
else if(res == GF("13") || res == GF("2A")) {
return REG_UNREGISTERED;
}
else if(res == GF("FF") || res == GF("22") || res == GF("23") ||
res == GF("40") || res == GF("41") || res == GF("42")) {
return REG_SEARCHING;
}
else if(res == GF("24") || res == GF("25") || res == GF("27")) {
return REG_DENIED;
}
else {
return REG_UNKNOWN;
}
}
String getOperator()
{
commandMode();
sendAT(GF("MN"));
// wait for the response
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < 1000) {};
String res = streamReadUntil('\r'); // Does not send an OK, just the result
exitCommand();
return res;
}
/*
* Generic network functions
*/
int getSignalQuality()
{
commandMode();
if (beeType == S6B) {
sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity
} else {
sendAT(GF("DB")); // ask for the cell strength in dBm
}
// wait for the response
unsigned long startMillis = millis();
while (!stream.available() && millis() - startMillis < 1000) {};
char buf[2] = {0}; // Set up buffer for response
buf[0] = streamRead();
buf[1] = streamRead();
// DBG(buf[0], buf[1], "\n");
exitCommand();
int intr = strtol(buf, 0, 16);
if (beeType == S6B) {
return -93 + intr; // the maximum sensitivity is -93dBm
} else {
return -1*intr; // need to convert to negative number
}
}
bool isNetworkConnected()
{
RegStatus s = getRegistrationStatus();
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
}
bool waitForNetwork(unsigned long timeout = 60000L)
{
for (unsigned long start = millis(); millis() - start < timeout; ) {
if (isNetworkConnected()) {
return true;
}
delay(250);
}
return false;
}
/*
* WiFi functions
*/
bool networkConnect(const char* ssid, const char* pwd)
{
commandMode();
sendAT(GF("EE"), 2); // Set security to WPA2
waitResponse();
sendAT(GF("ID"), ssid);
if (waitResponse() != 1) {
goto fail;
}
sendAT(GF("PK"), pwd);
if (waitResponse() != 1) {
goto fail;
}
writeChanges();
exitCommand();
return true;
fail:
exitCommand();
return false;
}
bool networkDisconnect()
{
return false; // Doesn't support disconnecting
}
String getLocalIP()
{
commandMode();
sendAT(GF("MY"));
String IPaddr;
IPaddr.reserve(16);
// wait for the response
unsigned long startMillis = millis();
while (stream.available() < 8 && millis() - startMillis < 30000) {};
IPaddr = streamReadUntil('\r'); // read result
return IPaddr;
}
IPAddress localIP()
{
return TinyGsmIpFromString(getLocalIP());
}
/*
* GPRS functions
*/
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL)
{
commandMode();
sendAT(GF("AN"), apn); // Set the APN
waitResponse();
writeChanges();
exitCommand();
return true;
}
bool gprsDisconnect() // TODO
{
return false;
}
/*
* Messaging functions
*/
String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
bool sendSMS(const String& number, const String& text)
{
commandMode();
sendAT(GF("IP"), 2); // Put in text messaging mode
waitResponse();
sendAT(GF("PH"), number); // Set the phone number
waitResponse();
sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriabe return)
waitResponse();
writeChanges();
exitCommand();
stream.print(text);
stream.write((char)0x0D); // close off with the carriage return
return true;
}
private:
int modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false)
{
sendAT(GF("LA"), host);
String strIP;
strIP.reserve(16);
// wait for the response
unsigned long startMillis = millis();
while (stream.available() < 8 && millis() - startMillis < 30000) {};
strIP = streamReadUntil('\r'); // read result
IPAddress ip = TinyGsmIpFromString(strIP);
return modemConnect(ip, port, mux, ssl);
}
int modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false)
{
String host;
host.reserve(16);
host += ip[0];
host += ".";
host += ip[1];
host += ".";
host += ip[2];
host += ".";
host += ip[3];
if (ssl) {
sendAT(GF("IP"), 4); // Put in TCP mode
waitResponse();
} else {
sendAT(GF("IP"), 1); // Put in TCP mode
waitResponse();
}
sendAT(GF("DL"), host); // Set the "Destination Address Low"
waitResponse();
sendAT(GF("DE"), String(port, HEX)); // Set the destination port
int rsp = waitResponse();
return rsp;
}
int modemSend(const void* buff, size_t len, uint8_t mux = 0)
{
stream.write((uint8_t*)buff, len);
stream.flush();
return len;
}
bool modemGetConnected(uint8_t mux = 0)
{
commandMode();
sendAT(GF("AI"));
int res = waitResponse(GF("0"));
exitCommand();
return 1 == res;
}
public:
/* Utilities */
template<typename T>
void streamWrite(T last)
{
stream.print(last);
}
template<typename T, typename... Args>
void streamWrite(T head, Args... tail)
{
stream.print(head);
streamWrite(tail...);
}
int streamRead()
{
return stream.read();
}
String streamReadUntil(char c)
{
TINY_GSM_YIELD();
String return_string = stream.readStringUntil(c);
return_string.trim();
// DBG(return_string, c);
return return_string;
}
void streamClear(void)
{
while (stream.available()) {
streamRead();
}
}
bool commandMode(void)
{
delay(guardTime); // cannot send anything for 1 second before entering command mode
streamWrite(GF("+++")); // enter command mode
// DBG("\r\n+++\r\n");
return 1 == waitResponse(guardTime*2);
}
void writeChanges(void)
{
sendAT(GF("WR")); // Write changes to flash
waitResponse();
sendAT(GF("AC")); // Apply changes
waitResponse();
}
void exitCommand(void)
{
sendAT(GF("CN")); // Exit command mode
waitResponse();
}
template<typename... Args>
void sendAT(Args... cmd)
{
streamWrite("AT", cmd..., GSM_NL);
stream.flush();
TINY_GSM_YIELD();
//DBG("### AT:", cmd...);
}
// TODO: Optimize this!
uint8_t waitResponse(uint32_t timeout, String& data,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
/*String r1s(r1); r1s.trim();
String r2s(r2); r2s.trim();
String r3s(r3); r3s.trim();
String r4s(r4); r4s.trim();
String r5s(r5); r5s.trim();
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
data.reserve(64);
int index = 0;
unsigned long startMillis = millis();
do {
TINY_GSM_YIELD();
while (stream.available() > 0) {
int a = streamRead();
if (a <= 0) {
continue; // Skip 0x00 bytes, just in case
}
data += (char)a;
if (r1 && data.endsWith(r1)) {
index = 1;
goto finish;
} else if (r2 && data.endsWith(r2)) {
index = 2;
goto finish;
} else if (r3 && data.endsWith(r3)) {
index = 3;
goto finish;
} else if (r4 && data.endsWith(r4)) {
index = 4;
goto finish;
} else if (r5 && data.endsWith(r5)) {
index = 5;
goto finish;
}
}
} while (millis() - startMillis < timeout);
finish:
if (!index) {
data.trim();
data.replace(GSM_NL GSM_NL, GSM_NL);
data.replace(GSM_NL, "\r\n" " ");
if (data.length()) {
DBG("### Unhandled:", data, "\r\n");
} else {
DBG("### NO RESPONSE!\r\n");
}
} else {
data.trim();
data.replace(GSM_NL GSM_NL, GSM_NL);
data.replace(GSM_NL, "\r\n ");
if (data.length()) {
// DBG("<<< ", data);
}
}
return index;
}
uint8_t waitResponse(uint32_t timeout,
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
String data;
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
}
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
{
return waitResponse(1000, r1, r2, r3, r4, r5);
}
public:
Stream& stream;
protected:
int guardTime;
XBeeType beeType;
GsmClient* sockets[TINY_GSM_MUX_COUNT];
};
#endif

View File

@@ -0,0 +1,195 @@
/**
* file TinyGsmCommon.h
* author Volodymyr Shymanskyy
* license LGPL-3.0
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
* date Nov 2016
*/
#ifndef TinyGsmCommon_h
#define TinyGsmCommon_h
#if defined(SPARK) || defined(PARTICLE)
#include "Particle.h"
#elif defined(ARDUINO)
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#endif
#include <Client.h>
#include "TinyGsmFifo.h"
#ifndef TINY_GSM_YIELD
#define TINY_GSM_YIELD() { delay(0); }
#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__)
#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(T last)
{
TINY_GSM_DEBUG.println(last);
}
template<typename T, typename... Args>
static void DBG(T head, Args... tail)
{
TINY_GSM_DEBUG.print(head);
TINY_GSM_DEBUG.print(' ');
DBG(tail...);
}
}
#else
#define DBG(...)
#endif
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;
}
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 (unsigned i = 0; i < sizeof(rates)/sizeof(rates[0]); i++) {
uint32_t rate = rates[i];
if (rate < minimum || rate > maximum) {
continue;
}
DBG("Trying baud rate", rate, "...");
SerialAT.begin(rate);
delay(10);
for (int i=0; i<3; i++) {
SerialAT.print("AT\r\n");
String input = SerialAT.readString();
if (input.indexOf("OK") >= 0) {
DBG("Modem responded at rate", rate);
return rate;
}
}
}
return 0;
}
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]);
}
static inline
String TinyGsmDecodeHex7bit(String &instr)
{
String result;
byte reminder = 0;
int bitstate = 7;
for (unsigned i=0; i<instr.length(); i+=2) {
char buf[4] = { 0, };
buf[0] = instr[i];
buf[1] = instr[i+1];
byte b = strtol(buf, NULL, 16);
byte bb = b << (7 - bitstate);
char c = (bb + reminder) & 0x7F;
result += c;
reminder = b >> bitstate;
bitstate--;
if (bitstate == 0) {
char c = reminder;
result += c;
reminder = 0;
bitstate = 7;
}
}
return result;
}
static inline
String TinyGsmDecodeHex8bit(String &instr)
{
String result;
for (unsigned i=0; i<instr.length(); i+=2) {
char buf[4] = { 0, };
buf[0] = instr[i];
buf[1] = instr[i+1];
char b = strtol(buf, NULL, 16);
result += b;
}
return result;
}
static inline
String TinyGsmDecodeHex16bit(String &instr)
{
String result;
for (unsigned i=0; i<instr.length(); i+=4) {
char buf[4] = { 0, };
buf[0] = instr[i];
buf[1] = instr[i+1];
char b = strtol(buf, NULL, 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, NULL, 16);
result += b;
}
}
return result;
}
#endif

View File

@@ -0,0 +1,152 @@
#ifndef TinyGsmFifo_h
#define TinyGsmFifo_h
template <class T, unsigned N>
class TinyGsmFifo
{
public:
TinyGsmFifo()
{
clear();
}
void clear()
{
_r = 0;
_w = 0;
}
// writing thread/context API
//-------------------------------------------------------------
bool writeable(void)
{
return free() > 0;
}
int free(void)
{
int s = _r - _w;
if (s <= 0) {
s += N;
}
return s - 1;
}
bool put(const T& c)
{
int i = _w;
int j = i;
i = _inc(i);
if (i == _r) { // !writeable()
return false;
}
_b[j] = c;
_w = i;
return true;
}
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;
}
private:
int _inc(int i, int n = 1)
{
return (i + n) % N;
}
T _b[N];
int _w;
int _r;
};
#endif

View File

@@ -0,0 +1,26 @@
#######################################
# 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)
#######################################