@ -0,0 +1,98 @@ | |||
/** | |||
* @file TinyGsmBattery.tpp | |||
* @author Volodymyr Shymanskyy | |||
* @license LGPL-3.0 | |||
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy | |||
* @date Nov 2016 | |||
*/ | |||
#ifndef SRC_TINYGSMBATTERY_H_ | |||
#define SRC_TINYGSMBATTERY_H_ | |||
#include "TinyGsmCommon.h" | |||
#define TINY_GSM_MODEM_HAS_BATTERY | |||
template <class modemType> | |||
class TinyGsmBattery { | |||
public: | |||
/* | |||
* Battery functions | |||
*/ | |||
uint16_t getBattVoltage() { | |||
return thisModem().getBattVoltageImpl(); | |||
} | |||
int8_t getBattPercent() { | |||
return thisModem().getBattPercentImpl(); | |||
} | |||
uint8_t getBattChargeState() { | |||
return thisModem().getBattChargeStateImpl(); | |||
} | |||
bool getBattStats(uint8_t& chargeState, int8_t& percent, | |||
uint16_t& milliVolts) { | |||
return thisModem().getBattStatsImpl(chargeState, percent, milliVolts); | |||
} | |||
/* | |||
* CRTP Helper | |||
*/ | |||
protected: | |||
inline const modemType& thisModem() const { | |||
return static_cast<const modemType&>(*this); | |||
} | |||
inline modemType& thisModem() { | |||
return static_cast<modemType&>(*this); | |||
} | |||
/* | |||
* Battery functions | |||
*/ | |||
protected: | |||
// Use: float vBatt = modem.getBattVoltage() / 1000.0; | |||
uint16_t getBattVoltageImpl() { | |||
thisModem().sendAT(GF("+CBC")); | |||
if (thisModem().waitResponse(GF("+CBC:")) != 1) { return 0; } | |||
thisModem().streamSkipUntil(','); // Skip battery charge status | |||
thisModem().streamSkipUntil(','); // Skip battery charge level | |||
// return voltage in mV | |||
uint16_t res = thisModem().streamGetInt(','); | |||
// 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 | |||
int res = thisModem().streamGetInt(','); | |||
// Wait for final OK | |||
thisModem().waitResponse(); | |||
return res; | |||
} | |||
uint8_t getBattChargeStateImpl() { | |||
thisModem().sendAT(GF("+CBC")); | |||
if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; } | |||
// Read battery charge status | |||
int res = thisModem().streamGetInt(','); | |||
// Wait for final OK | |||
thisModem().waitResponse(); | |||
return res; | |||
} | |||
bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, | |||
uint16_t& milliVolts) { | |||
thisModem().sendAT(GF("+CBC")); | |||
if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; } | |||
chargeState = thisModem().streamGetInt(','); | |||
percent = thisModem().streamGetInt(','); | |||
milliVolts = thisModem().streamGetInt('\n'); | |||
// Wait for final OK | |||
thisModem().waitResponse(); | |||
return true; | |||
} | |||
}; | |||
#endif // SRC_TINYGSMBATTERY_H_ |
@ -0,0 +1,90 @@ | |||
/** | |||
* @file TinyGsmCalling.tpp | |||
* @author Volodymyr Shymanskyy | |||
* @license LGPL-3.0 | |||
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy | |||
* @date Nov 2016 | |||
*/ | |||
#ifndef SRC_TINYGSMCALLING_H_ | |||
#define SRC_TINYGSMCALLING_H_ | |||
#include "TinyGsmCommon.h" | |||
#define TINY_GSM_MODEM_HAS_CALLING | |||
template <class modemType> | |||
class TinyGsmCalling { | |||
public: | |||
/* | |||
* Phone Call functions | |||
*/ | |||
bool callAnswer() { | |||
return thisModem().callAnswerImpl(); | |||
} | |||
bool callNumber(const String& number) { | |||
return thisModem().callNumberImpl(number); | |||
} | |||
bool callHangup() { | |||
return thisModem().callHangupImpl(); | |||
} | |||
bool dtmfSend(char cmd, int duration_ms = 100) { | |||
return thisModem().dtmfSendImpl(cmd, duration_ms); | |||
} | |||
/* | |||
* CRTP Helper | |||
*/ | |||
protected: | |||
inline const modemType& thisModem() const { | |||
return static_cast<const modemType&>(*this); | |||
} | |||
inline modemType& thisModem() { | |||
return static_cast<modemType&>(*this); | |||
} | |||
/* | |||
* Phone Call functions | |||
*/ | |||
protected: | |||
bool callAnswerImpl() { | |||
thisModem().sendAT(GF("A")); | |||
return thisModem().waitResponse() == 1; | |||
} | |||
// Returns true on pick-up, false on error/busy | |||
bool callNumberImpl(const String& number) { | |||
if (number == GF("last")) { | |||
thisModem().sendAT(GF("DL")); | |||
} else { | |||
thisModem().sendAT(GF("D"), number, ";"); | |||
} | |||
int 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_ |
@ -0,0 +1,158 @@ | |||
/** | |||
* @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 { | |||
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(); | |||
} | |||
SimStatus getSimStatus(uint32_t timeout_ms = 10000L) { | |||
return thisModem().getSimStatusImpl(timeout_ms); | |||
} | |||
/* | |||
* GPRS functions | |||
*/ | |||
bool gprsConnect(const char* apn, const char* user = NULL, | |||
const char* pwd = NULL) { | |||
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(); | |||
} | |||
/* | |||
* CRTP Helper | |||
*/ | |||
protected: | |||
inline const modemType& thisModem() const { | |||
return static_cast<const modemType&>(*this); | |||
} | |||
inline modemType& thisModem() { | |||
return static_cast<modemType&>(*this); | |||
} | |||
/* | |||
* 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")); | |||
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; | |||
} | |||
int 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: | |||
bool thisHasGPRS() { | |||
return true; | |||
} | |||
// Checks if current attached to GPRS/EPS service | |||
bool isGprsConnectedImpl() { | |||
thisModem().sendAT(GF("+CGATT?")); | |||
if (thisModem().waitResponse(GF("+CGATT:")) != 1) { return false; } | |||
int res = thisModem().streamGetInt('\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; | |||
} | |||
}; | |||
#endif // SRC_TINYGSMGPRS_H_ |
@ -0,0 +1,64 @@ | |||
/** | |||
* @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 { | |||
public: | |||
/* | |||
* GPS location functions | |||
*/ | |||
bool enableGPS() { | |||
return thisModem().enableGPSImpl(); | |||
} | |||
bool disableGPS() { | |||
return thisModem().disableGPSImpl(); | |||
} | |||
String getGPSraw() { | |||
return thisModem().getGPSrawImpl(); | |||
} | |||
bool getGPSTime(int* year, int* month, int* day, int* hour, int* minute, | |||
int* second) { | |||
return thisModem().getGPSTimeImpl(year, month, day, hour, minute, second); | |||
} | |||
bool getGPS(float* lat, float* lon, float* speed = 0, int* alt = 0) { | |||
return thisModem().getGPSImpl(lat, lon, speed, alt); | |||
} | |||
/* | |||
* CRTP Helper | |||
*/ | |||
protected: | |||
inline const modemType& thisModem() const { | |||
return static_cast<const modemType&>(*this); | |||
} | |||
inline modemType& thisModem() { | |||
return static_cast<modemType&>(*this); | |||
} | |||
/* | |||
* GPS location functions | |||
*/ | |||
bool enableGPSImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
bool disableGPSImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
String getGPSrawImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
bool getGPSTimeImpl(int* year, int* month, int* day, int* hour, int* minute, | |||
int* second) TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
bool getGPSImpl(float* lat, float* lon, float* speed = 0, | |||
int* alt = 0) TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
}; | |||
#endif // SRC_TINYGSMGPS_H_ |
@ -0,0 +1,51 @@ | |||
/** | |||
* @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 { | |||
public: | |||
/* | |||
* Location functions | |||
*/ | |||
String getGsmLocation() { | |||
return thisModem().getGsmLocationImpl(); | |||
} | |||
/* | |||
* CRTP Helper | |||
*/ | |||
protected: | |||
inline const modemType& thisModem() const { | |||
return static_cast<const modemType&>(*this); | |||
} | |||
inline modemType& thisModem() { | |||
return static_cast<modemType&>(*this); | |||
} | |||
/* | |||
* Location functions | |||
*/ | |||
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; | |||
} | |||
}; | |||
#endif // SRC_TINYGSMGSMLOCATION_H_ |
@ -0,0 +1,301 @@ | |||
/** | |||
* @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" | |||
template <class modemType> | |||
class TinyGsmModem { | |||
public: | |||
/* | |||
* Basic functions | |||
*/ | |||
bool begin(const char* pin = NULL) { | |||
return thisModem().initImpl(pin); | |||
} | |||
bool init(const char* pin = NULL) { | |||
return thisModem().initImpl(pin); | |||
} | |||
template <typename... Args> | |||
void sendAT(Args... cmd) { | |||
thisModem().streamWrite("AT", cmd..., thisModem().gsmNL); | |||
thisModem().stream.flush(); | |||
TINY_GSM_YIELD(); /* DBG("### AT:", cmd...); */ | |||
} | |||
void setBaud(uint32_t baud) { | |||
thisModem().setBaudImpl(baud); | |||
} | |||
// Test response to AT commands | |||
bool testAT(uint32_t timeout_ms = 10000L) { | |||
return thisModem().testATImpl(timeout_ms); | |||
} | |||
// Asks for modem information via the V.25TER standard ATI command | |||
// NOTE: The actual value and style of the response is quite varied | |||
String getModemInfo() { | |||
return thisModem().getModemInfoImpl(); | |||
} | |||
// Gets the modem name (as it calls itself) | |||
String getModemName() { | |||
return thisModem().getModemNameImpl(); | |||
} | |||
bool factoryDefault() { | |||
return thisModem().factoryDefaultImpl(); | |||
} | |||
/* | |||
* Power functions | |||
*/ | |||
bool restart() { | |||
return thisModem().restartImpl(); | |||
} | |||
bool poweroff() { | |||
return thisModem().powerOffImpl(); | |||
} | |||
bool radioOff() { | |||
return thisModem().radioOffImpl(); | |||
} | |||
bool sleepEnable(bool enable = true) { | |||
return thisModem().sleepEnableImpl(enable); | |||
} | |||
/* | |||
* Generic network functions | |||
*/ | |||
// RegStatus getRegistrationStatus() {} | |||
bool isNetworkConnected() { | |||
return thisModem().isNetworkConnectedImpl(); | |||
} | |||
// Waits for network attachment | |||
bool waitForNetwork(uint32_t timeout_ms = 60000L) { | |||
return thisModem().waitForNetworkImpl(timeout_ms); | |||
} | |||
// Gets signal quality report | |||
int16_t getSignalQuality() { | |||
return thisModem().getSignalQualityImpl(); | |||
} | |||
String getLocalIP() { | |||
return thisModem().getLocalIPImpl(); | |||
} | |||
IPAddress localIP() { | |||
return thisModem().TinyGsmIpFromString(thisModem().getLocalIP()); | |||
} | |||
/* | |||
* CRTP Helper | |||
*/ | |||
protected: | |||
inline const modemType& thisModem() const { | |||
return static_cast<const modemType&>(*this); | |||
} | |||
inline modemType& thisModem() { | |||
return static_cast<modemType&>(*this); | |||
} | |||
/* | |||
* Basic functions | |||
*/ | |||
protected: | |||
void setBaudImpl(uint32_t baud) { | |||
thisModem().sendAT(GF("+IPR="), baud); | |||
thisModem().waitResponse(); | |||
} | |||
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; | |||
} | |||
String getModemInfoImpl() { | |||
thisModem().sendAT(GF("I")); | |||
String res; | |||
if (thisModem().waitResponse(1000L, res) != 1) { return ""; } | |||
// 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(); | |||
return res; | |||
} | |||
String getModemNameImpl() { | |||
thisModem().sendAT(GF("+CGMI")); | |||
String res1; | |||
if (thisModem().waitResponse(1000L, res1) != 1) { return "unknown"; } | |||
res1.replace("\r\nOK\r\n", ""); | |||
res1.replace("\rOK\r", ""); | |||
res1.trim(); | |||
thisModem().sendAT(GF("+GMM")); | |||
String res2; | |||
if (thisModem().waitResponse(1000L, res2) != 1) { return "unknown"; } | |||
res1.replace("\r\nOK\r\n", ""); | |||
res1.replace("\rOK\r", ""); | |||
res2.trim(); | |||
String name = res1 + String(' ') + res2; | |||
DBG("### Modem:", name); | |||
return name; | |||
} | |||
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() { | |||
thisModem().sendAT(GF("+CFUN=0")); | |||
if (thisModem().waitResponse(10000L) != 1) { return false; } | |||
delay(3000); | |||
return true; | |||
} | |||
bool sleepEnableImpl(bool enable = true) 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 | |||
int getRegistrationStatusXREG(const char* regCommand) { | |||
thisModem().sendAT('+', regCommand, '?'); | |||
// check for any of the three for simplicity | |||
int 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().streamGetInt('\n'); | |||
thisModem().waitResponse(); | |||
return status; | |||
} | |||
bool waitForNetworkImpl(uint32_t timeout_ms = 60000L) { | |||
for (uint32_t start = millis(); millis() - start < timeout_ms;) { | |||
if (thisModem().isNetworkConnected()) { return true; } | |||
delay(250); | |||
} | |||
return false; | |||
} | |||
// Gets signal quality report according to 3GPP TS command AT+CSQ | |||
int16_t getSignalQualityImpl() { | |||
thisModem().sendAT(GF("+CSQ")); | |||
if (thisModem().waitResponse(GF("+CSQ:")) != 1) { return 99; } | |||
int res = thisModem().streamGetInt(','); | |||
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; | |||
} | |||
static 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]); | |||
} | |||
/* | |||
Utilities | |||
*/ | |||
protected: | |||
// Utility templates for writing/skipping characters on a stream | |||
template <typename T> | |||
void inline streamWrite(T last) { | |||
thisModem().stream.print(last); | |||
} | |||
template <typename T, typename... Args> | |||
void inline streamWrite(T head, Args... tail) { | |||
thisModem().stream.print(head); | |||
thisModem().streamWrite(tail...); | |||
} | |||
int16_t inline streamGetInt(char lastChar) { | |||
char buf[6]; | |||
size_t bytesRead = thisModem().stream.readBytesUntil( | |||
lastChar, buf, static_cast<size_t>(6)); | |||
if (bytesRead) { | |||
int16_t res = atoi(buf); | |||
return res; | |||
} else { | |||
return -9999; | |||
} | |||
} | |||
float inline streamGetFloat(char lastChar) { | |||
char buf[12]; | |||
size_t bytesRead = thisModem().stream.readBytesUntil( | |||
lastChar, buf, static_cast<size_t>(12)); | |||
if (bytesRead) { | |||
float res = atof(buf); | |||
return res; | |||
} else { | |||
return static_cast<float>(-9999); | |||
} | |||
} | |||
bool inline 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; | |||
} | |||
void inline streamClear() { | |||
while (thisModem().stream.available()) { | |||
thisModem().waitResponse(50, NULL, NULL); | |||
} | |||
} | |||
}; | |||
#endif // SRC_TINYGSMMODEM_H_ |
@ -0,0 +1,224 @@ | |||
/** | |||
* @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 { | |||
public: | |||
/* | |||
* Messaging 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); | |||
} | |||
/* | |||
* Messaging functions | |||
*/ | |||
protected: | |||
static inline String TinyGsmDecodeHex7bit(String& instr) { | |||
String result; | |||
byte reminder = 0; | |||
int 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, NULL, 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 (uint8_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, NULL, 16); | |||
result += b; | |||
} | |||
return result; | |||
} | |||
static inline String TinyGsmDecodeHex16bit(String& instr) { | |||
String result; | |||
for (uint8_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, 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; | |||
} | |||
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(','); | |||
int dcs = thisModem().streamGetInt('\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_ |
@ -0,0 +1,71 @@ | |||
/** | |||
* @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> | |||
class TinyGsmSSL { | |||
public: | |||
/* | |||
* SSL functions | |||
*/ | |||
bool addCertificate(const char* filename) { | |||
return thisModem().addCertificateImpl(filename); | |||
} | |||
bool deleteCertificate() { | |||
return thisModem().deleteCertificateImpl(); | |||
} | |||
/* | |||
* CRTP Helper | |||
*/ | |||
protected: | |||
inline const modemType& thisModem() const { | |||
return static_cast<const modemType&>(*this); | |||
} | |||
inline modemType& thisModem() { | |||
return static_cast<modemType&>(*this); | |||
} | |||
/* | |||
* Inner Secure Client | |||
*/ | |||
/* | |||
public: | |||
class GsmClientSecure : public GsmClient { | |||
public: | |||
GsmClientSecureSim800() {} | |||
explicit GsmClientSecureSim800(TinyGsmSim800& modem, uint8_t mux = 1) | |||
: GsmClientSim800(modem, mux) {} | |||
public: | |||
int connect(const char* host, uint16_t port, int timeout_s) { | |||
stop(); | |||
TINY_GSM_YIELD(); | |||
rx.clear(); | |||
sock_connected = at->modemConnect(host, port, mux, true, timeout_s); | |||
return sock_connected; | |||
} | |||
};*/ | |||
/* | |||
* SSL functions | |||
*/ | |||
protected: | |||
bool addCertificateImpl(const char* filename) TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
bool deleteCertificateImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
}; | |||
#endif // SRC_TINYGSMSSL_H_ |
@ -0,0 +1,40 @@ | |||
/** | |||
* @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 { | |||
public: | |||
/* | |||
* Temperature functions | |||
*/ | |||
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); | |||
} | |||
float getTemperatureImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
}; | |||
#endif // SRC_TINYGSMTEMPERATURE_H_ |
@ -0,0 +1,61 @@ | |||
/** | |||
* @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 { | |||
public: | |||
/* | |||
* Time functions | |||
*/ | |||
String getGSMDateTime(TinyGSMDateTimeFormat format) { | |||
return thisModem().getGSMDateTimeImpl(format); | |||
} | |||
/* | |||
* CRTP Helper | |||
*/ | |||
protected: | |||
inline const modemType& thisModem() const { | |||
return static_cast<const modemType&>(*this); | |||
} | |||
inline modemType& thisModem() { | |||
return static_cast<modemType&>(*this); | |||
} | |||
/* | |||
* 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; | |||
} | |||
return res; | |||
} | |||
}; | |||
#endif // SRC_TINYGSMTIME_H_ |
@ -0,0 +1,49 @@ | |||
/** | |||
* @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 { | |||
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); | |||
} | |||
/* | |||
* 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_ |