@ -0,0 +1,112 @@ | |||
--- | |||
Language: Cpp | |||
# BasedOnStyle: Google | |||
AccessModifierOffset: -1 | |||
AlignAfterOpenBracket: Align | |||
AlignConsecutiveMacros: false | |||
AlignConsecutiveAssignments: true | |||
AlignConsecutiveDeclarations: true | |||
AlignEscapedNewlines: Left | |||
AlignOperands: false | |||
AlignTrailingComments: true | |||
AllowAllArgumentsOnNextLine: true | |||
AllowAllConstructorInitializersOnNextLine: false | |||
AllowAllParametersOfDeclarationOnNextLine: false | |||
AllowShortBlocksOnASingleLine: true | |||
AllowShortCaseLabelsOnASingleLine: true | |||
AllowShortFunctionsOnASingleLine: Empty | |||
AllowShortLambdasOnASingleLine: All | |||
AllowShortIfStatementsOnASingleLine: WithoutElse | |||
AllowShortLoopsOnASingleLine: true | |||
AlwaysBreakAfterDefinitionReturnType: None | |||
AlwaysBreakAfterReturnType: None | |||
AlwaysBreakBeforeMultilineStrings: false | |||
AlwaysBreakTemplateDeclarations: Yes | |||
BinPackArguments: true | |||
BinPackParameters: true | |||
BreakBeforeBinaryOperators: None | |||
BreakBeforeBraces: Attach | |||
BreakBeforeInheritanceComma: false | |||
BreakInheritanceList: BeforeColon | |||
BreakBeforeTernaryOperators: true | |||
BreakConstructorInitializersBeforeComma: false | |||
BreakConstructorInitializers: BeforeColon | |||
BreakAfterJavaFieldAnnotations: false | |||
BreakStringLiterals: true | |||
ColumnLimit: 80 | |||
CommentPragmas: "^ IWYU pragma:" | |||
CompactNamespaces: false | |||
ConstructorInitializerAllOnOneLineOrOnePerLine: true | |||
ConstructorInitializerIndentWidth: 4 | |||
ContinuationIndentWidth: 4 | |||
Cpp11BracedListStyle: true | |||
DerivePointerAlignment: false | |||
DisableFormat: false | |||
ExperimentalAutoDetectBinPacking: false | |||
FixNamespaceComments: true | |||
ForEachMacros: | |||
- foreach | |||
- Q_FOREACH | |||
- BOOST_FOREACH | |||
IncludeBlocks: Preserve | |||
IncludeCategories: | |||
- Regex: '^<ext/.*\.h>' | |||
Priority: 2 | |||
- Regex: '^<.*\.h>' | |||
Priority: 1 | |||
- Regex: '^<.*' | |||
Priority: 2 | |||
- Regex: '.*' | |||
Priority: 3 | |||
- Regex: '.*.tpp' | |||
Priority: 4 | |||
IncludeIsMainRegex: '([-_](test|unittest))?$' | |||
IndentCaseLabels: true | |||
IndentPPDirectives: None | |||
IndentWidth: 2 | |||
IndentWrappedFunctionNames: false | |||
JavaScriptQuotes: Leave | |||
JavaScriptWrapImports: true | |||
KeepEmptyLinesAtTheStartOfBlocks: false | |||
MacroBlockBegin: "" | |||
MacroBlockEnd: "" | |||
MaxEmptyLinesToKeep: 2 | |||
NamespaceIndentation: None | |||
# ObjCBinPackProtocolList: Auto | |||
ObjCBlockIndentWidth: 2 | |||
ObjCSpaceAfterProperty: false | |||
ObjCSpaceBeforeProtocolList: true | |||
PenaltyBreakAssignment: 25 | |||
PenaltyBreakBeforeFirstCallParameter: 19 | |||
PenaltyBreakComment: 300 | |||
PenaltyBreakFirstLessLess: 120 | |||
PenaltyBreakString: 1000 | |||
PenaltyBreakTemplateDeclaration: 10 | |||
PenaltyExcessCharacter: 600 | |||
PenaltyReturnTypeOnItsOwnLine: 50 | |||
PointerAlignment: Left | |||
PointerBindsToType: true | |||
ReflowComments: true | |||
SortIncludes: false | |||
SortUsingDeclarations: true | |||
SpaceAfterCStyleCast: false | |||
SpaceAfterLogicalNot: false | |||
SpaceAfterTemplateKeyword: true | |||
SpaceBeforeAssignmentOperators: true | |||
SpaceBeforeCpp11BracedList: false | |||
SpaceBeforeCtorInitializerColon: true | |||
SpaceBeforeInheritanceColon: true | |||
SpaceBeforeParens: ControlStatements | |||
SpaceBeforeRangeBasedForLoopColon: true | |||
SpaceInEmptyParentheses: false | |||
SpacesBeforeTrailingComments: 2 | |||
SpacesInAngles: false | |||
SpacesInCStyleCastParentheses: false | |||
SpacesInContainerLiterals: true | |||
SpacesInCStyleCastParentheses: false | |||
SpacesInParentheses: false | |||
SpacesInSquareBrackets: false | |||
Standard: Cpp11 | |||
TabWidth: 2 | |||
UseTab: Never | |||
--- |
@ -0,0 +1,3 @@ | |||
# Allow references to be used to change values | |||
filter=-runtime/references | |||
filter=-build/namespaces |
@ -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().streamGetIntBefore(','); | |||
// Wait for final OK | |||
thisModem().waitResponse(); | |||
return res; | |||
} | |||
int8_t getBattPercentImpl() { | |||
thisModem().sendAT(GF("+CBC")); | |||
if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; } | |||
thisModem().streamSkipUntil(','); // Skip battery charge status | |||
// Read battery charge level | |||
int8_t res = thisModem().streamGetIntBefore(','); | |||
// Wait for final OK | |||
thisModem().waitResponse(); | |||
return res; | |||
} | |||
uint8_t getBattChargeStateImpl() { | |||
thisModem().sendAT(GF("+CBC")); | |||
if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; } | |||
// Read battery charge status | |||
int8_t res = thisModem().streamGetIntBefore(','); | |||
// Wait for final OK | |||
thisModem().waitResponse(); | |||
return res; | |||
} | |||
bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent, | |||
uint16_t& milliVolts) { | |||
thisModem().sendAT(GF("+CBC")); | |||
if (thisModem().waitResponse(GF("+CBC:")) != 1) { return false; } | |||
chargeState = thisModem().streamGetIntBefore(','); | |||
percent = thisModem().streamGetIntBefore(','); | |||
milliVolts = thisModem().streamGetIntBefore('\n'); | |||
// Wait for final OK | |||
thisModem().waitResponse(); | |||
return true; | |||
} | |||
}; | |||
#endif // SRC_TINYGSMBATTERY_H_ |
@ -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, ";"); | |||
} | |||
int8_t status = thisModem().waitResponse(60000L, GF("OK"), GF("BUSY"), | |||
GF("NO ANSWER"), GF("NO CARRIER")); | |||
switch (status) { | |||
case 1: return true; | |||
case 2: | |||
case 3: return false; | |||
default: return false; | |||
} | |||
} | |||
bool callHangupImpl() { | |||
thisModem().sendAT(GF("H")); | |||
return thisModem().waitResponse() == 1; | |||
} | |||
// 0-9,*,#,A,B,C,D | |||
bool dtmfSendImpl(char cmd, int duration_ms = 100) { | |||
duration_ms = constrain(duration_ms, 100, 1000); | |||
thisModem().sendAT(GF("+VTD="), | |||
duration_ms / 100); // VTD accepts in 1/10 of a second | |||
thisModem().waitResponse(); | |||
thisModem().sendAT(GF("+VTS="), cmd); | |||
return thisModem().waitResponse(10000L) == 1; | |||
} | |||
}; | |||
#endif // SRC_TINYGSMCALLING_H_ |
@ -0,0 +1,171 @@ | |||
/** | |||
* @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(); | |||
} | |||
// Asks for International Mobile Subscriber Identity IMSI | |||
String getIMSI() { | |||
return thisModem().getIMSIImpl(); | |||
} | |||
SimStatus getSimStatus(uint32_t timeout_ms = 10000L) { | |||
return thisModem().getSimStatusImpl(timeout_ms); | |||
} | |||
/* | |||
* GPRS functions | |||
*/ | |||
bool gprsConnect(const char* apn, const char* user = 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")); | |||
thisModem().streamSkipUntil('\n'); // skip first newline | |||
String res = thisModem().stream.readStringUntil('\n'); | |||
thisModem().waitResponse(); | |||
res.trim(); | |||
return res; | |||
} | |||
// Asks for International Mobile Subscriber Identity IMSI via the AT+CIMI | |||
// command | |||
String getIMSIImpl() { | |||
thisModem().sendAT(GF("+CIMI")); | |||
thisModem().streamSkipUntil('\n'); // skip first newline | |||
String res = thisModem().stream.readStringUntil('\n'); | |||
thisModem().waitResponse(); | |||
res.trim(); | |||
return res; | |||
} | |||
SimStatus getSimStatusImpl(uint32_t timeout_ms = 10000L) { | |||
for (uint32_t start = millis(); millis() - start < timeout_ms;) { | |||
thisModem().sendAT(GF("+CPIN?")); | |||
if (thisModem().waitResponse(GF("+CPIN:")) != 1) { | |||
delay(1000); | |||
continue; | |||
} | |||
int8_t status = | |||
thisModem().waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), | |||
GF("NOT INSERTED"), GF("NOT READY")); | |||
thisModem().waitResponse(); | |||
switch (status) { | |||
case 2: | |||
case 3: return SIM_LOCKED; | |||
case 1: return SIM_READY; | |||
default: return SIM_ERROR; | |||
} | |||
} | |||
return SIM_ERROR; | |||
} | |||
/* | |||
* GPRS functions | |||
*/ | |||
protected: | |||
// Checks if current attached to GPRS/EPS service | |||
bool isGprsConnectedImpl() { | |||
thisModem().sendAT(GF("+CGATT?")); | |||
if (thisModem().waitResponse(GF("+CGATT:")) != 1) { return false; } | |||
int8_t res = thisModem().streamGetIntBefore('\n'); | |||
thisModem().waitResponse(); | |||
if (res != 1) { return false; } | |||
return thisModem().localIP() != IPAddress(0, 0, 0, 0); | |||
} | |||
// Gets the current network operator via the 3GPP TS command AT+COPS | |||
String getOperatorImpl() { | |||
thisModem().sendAT(GF("+COPS?")); | |||
if (thisModem().waitResponse(GF("+COPS:")) != 1) { return ""; } | |||
thisModem().streamSkipUntil('"'); /* Skip mode and format */ | |||
String res = thisModem().stream.readStringUntil('"'); | |||
thisModem().waitResponse(); | |||
return res; | |||
} | |||
}; | |||
#endif // SRC_TINYGSMGPRS_H_ |
@ -0,0 +1,72 @@ | |||
/** | |||
* @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/GNSS/GLONASS location functions | |||
*/ | |||
bool enableGPS() { | |||
return thisModem().enableGPSImpl(); | |||
} | |||
bool disableGPS() { | |||
return thisModem().disableGPSImpl(); | |||
} | |||
String getGPSraw() { | |||
return thisModem().getGPSrawImpl(); | |||
} | |||
bool getGPS(float* lat, float* lon, float* speed = 0, float* alt = 0, | |||
int* vsat = 0, int* usat = 0, float* accuracy = 0, int* year = 0, | |||
int* month = 0, int* day = 0, int* hour = 0, int* minute = 0, | |||
int* second = 0) { | |||
return thisModem().getGPSImpl(lat, lon, speed, alt, vsat, usat, accuracy, | |||
year, month, day, hour, minute, second); | |||
} | |||
bool getGPSTime(int* year, int* month, int* day, int* hour, int* minute, | |||
int* second) { | |||
float lat = 0; | |||
float lon = 0; | |||
return thisModem().getGPSImpl(lat, lon, 0, 0, 0, 0, 0, year, month, day, | |||
hour, minute, second); | |||
} | |||
/* | |||
* CRTP Helper | |||
*/ | |||
protected: | |||
inline const modemType& thisModem() const { | |||
return static_cast<const modemType&>(*this); | |||
} | |||
inline modemType& thisModem() { | |||
return static_cast<modemType&>(*this); | |||
} | |||
/* | |||
* GPS/GNSS/GLONASS location functions | |||
*/ | |||
bool enableGPSImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
bool disableGPSImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
String getGPSrawImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0, | |||
int* vsat = 0, int* usat = 0, float* accuracy = 0, | |||
int* year = 0, int* month = 0, int* day = 0, int* hour = 0, | |||
int* minute = 0, | |||
int* second = 0) TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
}; | |||
#endif // SRC_TINYGSMGPS_H_ |
@ -0,0 +1,149 @@ | |||
/** | |||
* @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: | |||
/* | |||
* GSM Location functions | |||
*/ | |||
String getGsmLocationRaw() { | |||
return thisModem().getGsmLocationRawImpl(); | |||
} | |||
String getGsmLocation() { | |||
return thisModem().getGsmLocationRawImpl(); | |||
} | |||
bool getGsmLocation(float* lat, float* lon, float* accuracy = 0, | |||
int* year = 0, int* month = 0, int* day = 0, | |||
int* hour = 0, int* minute = 0, int* second = 0) { | |||
return thisModem().getGsmLocationImpl(lat, lon, accuracy, year, month, day, | |||
hour, minute, second); | |||
}; | |||
bool getGsmLocationTime(int* year, int* month, int* day, int* hour, | |||
int* minute, int* second) { | |||
float lat = 0; | |||
float lon = 0; | |||
float accuracy = 0; | |||
return thisModem().getGsmLocation(&lat, &lon, &accuracy, year, month, day, | |||
hour, minute, second); | |||
} | |||
/* | |||
* CRTP Helper | |||
*/ | |||
protected: | |||
inline const modemType& thisModem() const { | |||
return static_cast<const modemType&>(*this); | |||
} | |||
inline modemType& thisModem() { | |||
return static_cast<modemType&>(*this); | |||
} | |||
/* | |||
* GSM Location functions | |||
* Template is based on SIMCOM commands | |||
*/ | |||
protected: | |||
// String getGsmLocationImpl() { | |||
// thisModem().sendAT(GF("+CIPGSMLOC=1,1")); | |||
// if (thisModem().waitResponse(10000L, GF("+CIPGSMLOC:")) != 1) { return | |||
// ""; } String res = thisModem().stream.readStringUntil('\n'); | |||
// thisModem().waitResponse(); | |||
// res.trim(); | |||
// return res; | |||
// } | |||
String getGsmLocationRawImpl() { | |||
// AT+CLBS=<type>,<cid> | |||
// <type> 1 = location using 3 cell's information | |||
// 3 = get number of times location has been accessed | |||
// 4 = Get longitude latitude and date time | |||
thisModem().sendAT(GF("+CLBS=1,1")); | |||
// Should get a location code of "0" indicating success | |||
if (thisModem().waitResponse(120000L, GF("+CLBS: ")) != 1) { return ""; } | |||
int8_t locationCode = thisModem().streamGetIntLength(2); | |||
// 0 = success, else, error | |||
if (locationCode != 0) { | |||
thisModem().waitResponse(); // should be an ok after the error | |||
return ""; | |||
} | |||
String res = thisModem().stream.readStringUntil('\n'); | |||
thisModem().waitResponse(); | |||
res.trim(); | |||
return res; | |||
} | |||
bool getGsmLocationImpl(float* lat, float* lon, float* accuracy = 0, | |||
int* year = 0, int* month = 0, int* day = 0, | |||
int* hour = 0, int* minute = 0, int* second = 0) { | |||
// AT+CLBS=<type>,<cid> | |||
// <type> 1 = location using 3 cell's information | |||
// 3 = get number of times location has been accessed | |||
// 4 = Get longitude latitude and date time | |||
thisModem().sendAT(GF("+CLBS=4,1")); | |||
// Should get a location code of "0" indicating success | |||
if (thisModem().waitResponse(120000L, GF("+CLBS: ")) != 1) { return false; } | |||
int8_t locationCode = thisModem().streamGetIntLength(2); | |||
// 0 = success, else, error | |||
if (locationCode != 0) { | |||
thisModem().waitResponse(); // should be an ok after the error | |||
return false; | |||
} | |||
// init variables | |||
float ilat = 0; | |||
float ilon = 0; | |||
float iaccuracy = 0; | |||
int iyear = 0; | |||
int imonth = 0; | |||
int iday = 0; | |||
int ihour = 0; | |||
int imin = 0; | |||
int isec = 0; | |||
ilat = thisModem().streamGetFloatBefore(','); // Latitude | |||
ilon = thisModem().streamGetFloatBefore(','); // Longitude | |||
iaccuracy = thisModem().streamGetIntBefore(','); // Positioning accuracy | |||
// Date & Time | |||
iyear = thisModem().streamGetIntBefore('/'); | |||
imonth = thisModem().streamGetIntBefore('/'); | |||
iday = thisModem().streamGetIntBefore(','); | |||
ihour = thisModem().streamGetIntBefore(':'); | |||
imin = thisModem().streamGetIntBefore(':'); | |||
isec = thisModem().streamGetIntBefore('\n'); | |||
// Set pointers | |||
if (lat != NULL) *lat = ilat; | |||
if (lon != NULL) *lon = ilon; | |||
if (accuracy != NULL) *accuracy = iaccuracy; | |||
if (iyear < 2000) iyear += 2000; | |||
if (year != NULL) *year = iyear; | |||
if (month != NULL) *month = imonth; | |||
if (day != NULL) *day = iday; | |||
if (hour != NULL) *hour = ihour; | |||
if (minute != NULL) *minute = imin; | |||
if (second != NULL) *second = isec; | |||
// Final OK | |||
thisModem().waitResponse(); | |||
return true; | |||
} | |||
}; | |||
#endif // SRC_TINYGSMGSMLOCATION_H_ |
@ -0,0 +1,330 @@ | |||
/** | |||
* @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> | |||
inline 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"; } | |||
res2.replace("\r\nOK\r\n", ""); | |||
res2.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 | |||
int8_t getRegistrationStatusXREG(const char* regCommand) { | |||
thisModem().sendAT('+', regCommand, '?'); | |||
// check for any of the three for simplicity | |||
int8_t resp = thisModem().waitResponse(GF("+CREG:"), GF("+CGREG:"), | |||
GF("+CEREG:")); | |||
if (resp != 1 && resp != 2 && resp != 3) { return -1; } | |||
thisModem().streamSkipUntil(','); /* Skip format (0) */ | |||
int status = thisModem().streamGetIntBefore('\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 | |||
int8_t getSignalQualityImpl() { | |||
thisModem().sendAT(GF("+CSQ")); | |||
if (thisModem().waitResponse(GF("+CSQ:")) != 1) { return 99; } | |||
int8_t res = thisModem().streamGetIntBefore(','); | |||
thisModem().waitResponse(); | |||
return res; | |||
} | |||
String getLocalIPImpl() { | |||
thisModem().sendAT(GF("+CGPADDR=1")); | |||
if (thisModem().waitResponse(GF("+CGPADDR:")) != 1) { return ""; } | |||
thisModem().streamSkipUntil(','); // Skip context id | |||
String res = thisModem().stream.readStringUntil('\r'); | |||
if (thisModem().waitResponse() != 1) { return ""; } | |||
return res; | |||
} | |||
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]); | |||
} | |||
/* | |||
Utilities | |||
*/ | |||
public: | |||
// Utility templates for writing/skipping characters on a stream | |||
template <typename T> | |||
inline void streamWrite(T last) { | |||
thisModem().stream.print(last); | |||
} | |||
template <typename T, typename... Args> | |||
inline void streamWrite(T head, Args... tail) { | |||
thisModem().stream.print(head); | |||
thisModem().streamWrite(tail...); | |||
} | |||
inline void streamClear() { | |||
while (thisModem().stream.available()) { | |||
thisModem().waitResponse(50, NULL, NULL); | |||
} | |||
} | |||
protected: | |||
inline int16_t streamGetIntLength(int8_t numChars) { | |||
char buf[6]; | |||
size_t bytesRead = thisModem().stream.readBytes(buf, numChars); | |||
if (bytesRead) { | |||
buf[numChars] = '\0'; | |||
int16_t res = atoi(buf); | |||
return res; | |||
} else { | |||
return -9999; | |||
} | |||
} | |||
inline int16_t streamGetIntBefore(char lastChar) { | |||
char buf[7]; | |||
size_t bytesRead = thisModem().stream.readBytesUntil( | |||
lastChar, buf, static_cast<size_t>(7)); | |||
// if we read 7 or more bytes, it's an overflow | |||
if (bytesRead && bytesRead < 7) { | |||
buf[bytesRead] = '\0'; | |||
int16_t res = atoi(buf); | |||
return res; | |||
} else { | |||
return -9999; | |||
} | |||
} | |||
inline float streamGetFloatLength(int8_t numChars) { | |||
char buf[16]; | |||
size_t bytesRead = thisModem().stream.readBytes(buf, numChars); | |||
DBG("### bytesRead:", bytesRead); | |||
if (bytesRead) { | |||
buf[numChars] = '\0'; | |||
int16_t res = atof(buf); | |||
return res; | |||
} else { | |||
return static_cast<float>(-9999); | |||
} | |||
} | |||
inline float streamGetFloatBefore(char lastChar) { | |||
char buf[16]; | |||
size_t bytesRead = thisModem().stream.readBytesUntil( | |||
lastChar, buf, static_cast<size_t>(16)); | |||
if (bytesRead) { | |||
buf[bytesRead] = '\0'; | |||
float res = atof(buf); | |||
return res; | |||
} else { | |||
return static_cast<float>(-9999); | |||
} | |||
} | |||
inline bool streamSkipUntil(const char c, const uint32_t timeout_ms = 1000L) { | |||
uint32_t startMillis = millis(); | |||
while (millis() - startMillis < timeout_ms) { | |||
while (millis() - startMillis < timeout_ms && | |||
!thisModem().stream.available()) { | |||
TINY_GSM_YIELD(); | |||
} | |||
if (thisModem().stream.read() == c) { return true; } | |||
} | |||
return false; | |||
} | |||
}; | |||
#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; | |||
int8_t bitstate = 7; | |||
for (uint8_t i = 0; i < instr.length(); i += 2) { | |||
char buf[4] = { | |||
0, | |||
}; | |||
buf[0] = instr[i]; | |||
buf[1] = instr[i + 1]; | |||
byte b = strtol(buf, 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(','); | |||
int8_t dcs = thisModem().streamGetIntBefore('\n'); | |||
if (dcs == 15) { | |||
return TinyGsmDecodeHex8bit(hex); | |||
} else if (dcs == 72) { | |||
return TinyGsmDecodeHex16bit(hex); | |||
} else { | |||
return hex; | |||
} | |||
} | |||
bool sendSMSImpl(const String& number, const String& text) { | |||
// Set preferred message format to text mode | |||
thisModem().sendAT(GF("+CMGF=1")); | |||
thisModem().waitResponse(); | |||
// Set GSM 7 bit default alphabet (3GPP TS 23.038) | |||
thisModem().sendAT(GF("+CSCS=\"GSM\"")); | |||
thisModem().waitResponse(); | |||
thisModem().sendAT(GF("+CMGS=\""), number, GF("\"")); | |||
if (thisModem().waitResponse(GF(">")) != 1) { return false; } | |||
thisModem().stream.print(text); // Actually send the message | |||
thisModem().stream.write(static_cast<char>(0x1A)); // Terminate the message | |||
thisModem().stream.flush(); | |||
return thisModem().waitResponse(60000L) == 1; | |||
} | |||
// Common methods for UTF8/UTF16 SMS. | |||
// Supported by: BG96, M95, MC60, SIM5360, SIM7000, SIM7600, SIM800 | |||
class UTF8Print : public Print { | |||
public: | |||
explicit UTF8Print(Print& p) : p(p) {} | |||
size_t write(const uint8_t c) override { | |||
if (prv < 0xC0) { | |||
if (c < 0xC0) printHex(c); | |||
prv = c; | |||
} else { | |||
uint16_t v = uint16_t(prv) << 8 | c; | |||
v -= (v >> 8 == 0xD0) ? 0xCC80 : 0xCD40; | |||
printHex(v); | |||
prv = 0; | |||
} | |||
return 1; | |||
} | |||
private: | |||
Print& p; | |||
uint8_t prv = 0; | |||
void printHex(const uint16_t v) { | |||
uint8_t c = v >> 8; | |||
if (c < 0x10) p.print('0'); | |||
p.print(c, HEX); | |||
c = v & 0xFF; | |||
if (c < 0x10) p.print('0'); | |||
p.print(c, HEX); | |||
} | |||
}; | |||
bool sendSMS_UTF8_begin(const char* const number) { | |||
thisModem().sendAT(GF("+CMGF=1")); | |||
thisModem().waitResponse(); | |||
thisModem().sendAT(GF("+CSCS=\"HEX\"")); | |||
thisModem().waitResponse(); | |||
thisModem().sendAT(GF("+CSMP=17,167,0,8")); | |||
thisModem().waitResponse(); | |||
thisModem().sendAT(GF("+CMGS=\""), number, GF("\"")); | |||
return thisModem().waitResponse(GF(">")) == 1; | |||
} | |||
bool sendSMS_UTF8_end() { | |||
thisModem().stream.write(static_cast<char>(0x1A)); | |||
thisModem().stream.flush(); | |||
return thisModem().waitResponse(60000L) == 1; | |||
} | |||
UTF8Print sendSMS_UTF8_stream() { | |||
return UTF8Print(thisModem().stream); | |||
} | |||
bool sendSMS_UTF16Impl(const char* const number, const void* text, | |||
size_t len) { | |||
if (!sendSMS_UTF8_begin(number)) { return false; } | |||
uint16_t* t = | |||
const_cast<uint16_t*>(reinterpret_cast<const uint16_t*>(text)); | |||
for (size_t i = 0; i < len; i++) { | |||
uint8_t c = t[i] >> 8; | |||
if (c < 0x10) { thisModem().stream.print('0'); } | |||
thisModem().stream.print(c, HEX); | |||
c = t[i] & 0xFF; | |||
if (c < 0x10) { thisModem().stream.print('0'); } | |||
thisModem().stream.print(c, HEX); | |||
} | |||
return sendSMS_UTF8_end(); | |||
} | |||
}; | |||
#endif // SRC_TINYGSMSMS_H_ |
@ -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 = 0) | |||
: GsmClientSim800(modem, mux) {} | |||
public: | |||
int connect(const char* host, uint16_t port, int timeout_s) overide { | |||
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,345 @@ | |||
/** | |||
* @file TinyGsmTCP.tpp | |||
* @author Volodymyr Shymanskyy | |||
* @license LGPL-3.0 | |||
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy | |||
* @date Nov 2016 | |||
*/ | |||
#ifndef SRC_TINYGSMTCP_H_ | |||
#define SRC_TINYGSMTCP_H_ | |||
#include "TinyGsmCommon.h" | |||
#define TINY_GSM_MODEM_HAS_TCP | |||
#include "TinyGsmFifo.h" | |||
#if !defined(TINY_GSM_RX_BUFFER) | |||
#define TINY_GSM_RX_BUFFER 64 | |||
#endif | |||
// Because of the ordering of resolution of overrides in templates, these need | |||
// to be written out every time. This macro is to shorten that. | |||
#define TINY_GSM_CLIENT_CONNECT_OVERRIDES \ | |||
int connect(IPAddress ip, uint16_t port, int timeout_s) { \ | |||
return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); \ | |||
} \ | |||
int connect(const char* host, uint16_t port) override { \ | |||
return connect(host, port, 75); \ | |||
} \ | |||
int connect(IPAddress ip, uint16_t port) override { \ | |||
return connect(ip, port, 75); \ | |||
} | |||
// // For modules that do not store incoming data in any sort of buffer | |||
// #define TINY_GSM_NO_MODEM_BUFFER | |||
// // Data is stored in a buffer, but we can only read from the buffer, | |||
// // not check how much data is stored in it | |||
// #define TINY_GSM_BUFFER_READ_NO_CHECK | |||
// // Data is stored in a buffer and we can both read and check the size | |||
// // of the buffer | |||
// #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE | |||
template <class modemType, uint8_t muxCount> | |||
class TinyGsmTCP { | |||
public: | |||
/* | |||
* Basic functions | |||
*/ | |||
void maintain() { | |||
return thisModem().maintainImpl(); | |||
} | |||
/* | |||
* CRTP Helper | |||
*/ | |||
protected: | |||
inline const modemType& thisModem() const { | |||
return static_cast<const modemType&>(*this); | |||
} | |||
inline modemType& thisModem() { | |||
return static_cast<modemType&>(*this); | |||
} | |||
/* | |||
* Inner Client | |||
*/ | |||
public: | |||
class GsmClient : public Client { | |||
// Make all classes created from the modem template friends | |||
friend class TinyGsmTCP<modemType, muxCount>; | |||
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo; | |||
public: | |||
// bool init(modemType* modem, uint8_t); | |||
// int connect(const char* host, uint16_t port, int timeout_s); | |||
// Connect to a IP address given as an IPAddress object by | |||
// converting said IP address to text | |||
// virtual int connect(IPAddress ip,uint16_t port, int timeout_s) { | |||
// return connect(TinyGsmStringFromIp(ip).c_str(), port, | |||
// timeout_s); | |||
// } | |||
// int connect(const char* host, uint16_t port) override { | |||
// return connect(host, port, 75); | |||
// } | |||
// int connect(IPAddress ip,uint16_t port) override { | |||
// return connect(ip, port, 75); | |||
// } | |||
static inline String TinyGsmStringFromIp(IPAddress ip) { | |||
String host; | |||
host.reserve(16); | |||
host += ip[0]; | |||
host += "."; | |||
host += ip[1]; | |||
host += "."; | |||
host += ip[2]; | |||
host += "."; | |||
host += ip[3]; | |||
return host; | |||
} | |||
// void stop(uint32_t maxWaitMs); | |||
// void stop() override { | |||
// stop(15000L); | |||
// } | |||
// Writes data out on the client using the modem send functionality | |||
size_t write(const uint8_t* buf, size_t size) override { | |||
TINY_GSM_YIELD(); | |||
at->maintain(); | |||
return at->modemSend(buf, size, mux); | |||
} | |||
size_t write(uint8_t c) override { | |||
return write(&c, 1); | |||
} | |||
size_t write(const char* str) { | |||
if (str == NULL) return 0; | |||
return write((const uint8_t*)str, strlen(str)); | |||
} | |||
int available() override { | |||
TINY_GSM_YIELD(); | |||
#if defined TINY_GSM_NO_MODEM_BUFFER | |||
// Returns the number of characters available in the TinyGSM fifo | |||
if (!rx.size() && sock_connected) { at->maintain(); } | |||
return rx.size(); | |||
#elif defined TINY_GSM_BUFFER_READ_NO_CHECK | |||
// Returns the combined number of characters available in the TinyGSM | |||
// fifo and the modem chips internal fifo. | |||
if (!rx.size()) { at->maintain(); } | |||
return rx.size() + sock_available; | |||
#elif defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE | |||
// Returns the combined number of characters available in the TinyGSM | |||
// fifo and the modem chips internal fifo, doing an extra check-in | |||
// with the modem to see if anything has arrived without a UURC. | |||
if (!rx.size()) { | |||
if (millis() - prev_check > 500) { | |||
got_data = true; | |||
prev_check = millis(); | |||
} | |||
at->maintain(); | |||
} | |||
return rx.size() + sock_available; | |||
#else | |||
#error Modem client has been incorrectly created | |||
#endif | |||
} | |||
int read(uint8_t* buf, size_t size) override { | |||
TINY_GSM_YIELD(); | |||
size_t cnt = 0; | |||
#if defined TINY_GSM_NO_MODEM_BUFFER | |||
// Reads characters out of the TinyGSM fifo, waiting for any URC's | |||
// from the modem for new data if there's nothing in the fifo. | |||
uint32_t _startMillis = millis(); | |||
while (cnt < size && millis() - _startMillis < _timeout) { | |||
size_t chunk = TinyGsmMin(size - cnt, rx.size()); | |||
if (chunk > 0) { | |||
rx.get(buf, chunk); | |||
buf += chunk; | |||
cnt += chunk; | |||
continue; | |||
} /* TODO: Read directly into user buffer? */ | |||
if (!rx.size() && sock_connected) { at->maintain(); } | |||
} | |||
return cnt; | |||
#elif defined TINY_GSM_BUFFER_READ_NO_CHECK | |||
// Reads characters out of the TinyGSM fifo, and from the modem chip's | |||
// internal fifo if avaiable. | |||
at->maintain(); | |||
while (cnt < size) { | |||
size_t chunk = TinyGsmMin(size - cnt, rx.size()); | |||
if (chunk > 0) { | |||
rx.get(buf, chunk); | |||
buf += chunk; | |||
cnt += chunk; | |||
continue; | |||
} /* TODO: Read directly into user buffer? */ | |||
at->maintain(); | |||
if (sock_available > 0) { | |||
int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), | |||
mux); | |||
if (n == 0) break; | |||
} else { | |||
break; | |||
} | |||
} | |||
return cnt; | |||
#elif defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE | |||
// Reads characters out of the TinyGSM fifo, and from the modem chips | |||
// internal fifo if avaiable, also double checking with the modem if | |||
// data has arrived without issuing a UURC. | |||
at->maintain(); | |||
while (cnt < size) { | |||
size_t chunk = TinyGsmMin(size - cnt, rx.size()); | |||
if (chunk > 0) { | |||
rx.get(buf, chunk); | |||
buf += chunk; | |||
cnt += chunk; | |||
continue; | |||
} | |||
// Workaround: Some modules "forget" to notify about data arrival | |||
if (millis() - prev_check > 500) { | |||
got_data = true; | |||
prev_check = millis(); | |||
} | |||
// TODO(vshymanskyy): Read directly into user buffer? | |||
at->maintain(); | |||
if (sock_available > 0) { | |||
int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), | |||
mux); | |||
if (n == 0) break; | |||
} else { | |||
break; | |||
} | |||
} | |||
return cnt; | |||
#else | |||
#error Modem client has been incorrectly created | |||
#endif | |||
} | |||
int read() override { | |||
uint8_t c; | |||
if (read(&c, 1) == 1) { return c; } | |||
return -1; | |||
} | |||
// TODO(SRGDamia1): Implement peek | |||
int peek() override { | |||
return -1; | |||
} | |||
void flush() override { | |||
at->stream.flush(); | |||
} | |||
uint8_t connected() override { | |||
if (available()) { return true; } | |||
return sock_connected; | |||
} | |||
operator bool() override { | |||
return connected(); | |||
} | |||
/* | |||
* Extended API | |||
*/ | |||
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED; | |||
protected: | |||
// Read and dump anything remaining in the modem's internal buffer. | |||
// Using this in the client stop() function. | |||
// The socket will appear open in response to connected() even after it | |||
// closes until all data is read from the buffer. | |||
// Doing it this way allows the external mcu to find and get all of the | |||
// data that it wants from the socket even if it was closed externally. | |||
inline void dumpModemBuffer(uint32_t maxWaitMs) { | |||
#if defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE || \ | |||
defined TINY_GSM_BUFFER_READ_NO_CHECK | |||
TINY_GSM_YIELD(); | |||
uint32_t startMillis = millis(); | |||
while (sock_available > 0 && (millis() - startMillis < maxWaitMs)) { | |||
rx.clear(); | |||
at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); | |||
} | |||
rx.clear(); | |||
at->streamClear(); | |||
#elif defined TINY_GSM_NO_MODEM_BUFFER | |||
rx.clear(); | |||
at->streamClear(); | |||
#else | |||
#error Modem client has been incorrectly created | |||
#endif | |||
} | |||
modemType* at; | |||
uint8_t mux; | |||
uint16_t sock_available; | |||
uint32_t prev_check; | |||
bool sock_connected; | |||
bool got_data; | |||
RxFifo rx; | |||
}; | |||
/* | |||
* Basic functions | |||
*/ | |||
protected: | |||
void maintainImpl() { | |||
#if defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE | |||
// Keep listening for modem URC's and proactively iterate through | |||
// sockets asking if any data is avaiable | |||
for (int mux = 0; mux < muxCount; mux++) { | |||
GsmClient* sock = thisModem().sockets[mux]; | |||
if (sock && sock->got_data) { | |||
sock->got_data = false; | |||
sock->sock_available = thisModem().modemGetAvailable(mux); | |||
} | |||
} | |||
while (thisModem().stream.available()) { | |||
thisModem().waitResponse(15, NULL, NULL); | |||
} | |||
#elif defined TINY_GSM_NO_MODEM_BUFFER || defined TINY_GSM_BUFFER_READ_NO_CHECK | |||
// Just listen for any URC's | |||
thisModem().waitResponse(100, NULL, NULL); | |||
#else | |||
#error Modem client has been incorrectly created | |||
#endif | |||
} | |||
// Yields up to a time-out period and then reads a character from the stream | |||
// into the mux FIFO | |||
// TODO(SRGDamia1): Do we need to wait two _timeout periods for no | |||
// character return? Will wait once in the first "while | |||
// !stream.available()" and then will wait again in the stream.read() | |||
// function. | |||
inline void moveCharFromStreamToFifo(uint8_t mux) { | |||
uint32_t startMillis = millis(); | |||
while (!thisModem().stream.available() && | |||
(millis() - startMillis < thisModem().sockets[mux]->_timeout)) { | |||
TINY_GSM_YIELD(); | |||
} | |||
char c = thisModem().stream.read(); | |||
thisModem().sockets[mux]->rx.put(c); | |||
} | |||
}; | |||
#endif // SRC_TINYGSMTCP_H_ |
@ -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,106 @@ | |||
/** | |||
* @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); | |||
} | |||
bool getNetworkTime(int* year, int* month, int* day, int* hour, int* minute, | |||
int* second, float* timezone) { | |||
return thisModem().getNetworkTimeImpl(year, month, day, hour, minute, | |||
second, timezone); | |||
} | |||
/* | |||
* 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; | |||
} | |||
thisModem().waitResponse(); // Ends with OK | |||
return res; | |||
} | |||
bool getNetworkTimeImpl(int* year, int* month, int* day, int* hour, | |||
int* minute, int* second, float* timezone) { | |||
thisModem().sendAT(GF("+CCLK?")); | |||
if (thisModem().waitResponse(2000L, GF("+CCLK: \"")) != 1) { return false; } | |||
int iyear = 0; | |||
int imonth = 0; | |||
int iday = 0; | |||
int ihour = 0; | |||
int imin = 0; | |||
int isec = 0; | |||
int itimezone = 0; | |||
// Date & Time | |||
iyear = thisModem().streamGetIntBefore('/'); | |||
imonth = thisModem().streamGetIntBefore('/'); | |||
iday = thisModem().streamGetIntBefore(','); | |||
ihour = thisModem().streamGetIntBefore(':'); | |||
imin = thisModem().streamGetIntBefore(':'); | |||
isec = thisModem().streamGetIntLength(2); | |||
char tzSign = thisModem().stream.read(); | |||
itimezone = thisModem().streamGetIntBefore('\n'); | |||
if (tzSign == '-') { itimezone = itimezone * -1; } | |||
// Set pointers | |||
if (iyear < 2000) iyear += 2000; | |||
if (year != NULL) *year = iyear; | |||
if (month != NULL) *month = imonth; | |||
if (day != NULL) *day = iday; | |||
if (hour != NULL) *hour = ihour; | |||
if (minute != NULL) *minute = imin; | |||
if (second != NULL) *second = isec; | |||
if (timezone != NULL) *timezone = static_cast<float>(itimezone) / 4.0; | |||
// Final OK | |||
thisModem().waitResponse(); | |||
return true; | |||
} | |||
}; | |||
#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_ |