You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

352 lines
9.4 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. /**
  2. * @file TinyGsmModem.tpp
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef SRC_TINYGSMMODEM_H_
  9. #define SRC_TINYGSMMODEM_H_
  10. #include "TinyGsmCommon.h"
  11. template <class modemType>
  12. class TinyGsmModem {
  13. public:
  14. /*
  15. * Basic functions
  16. */
  17. bool begin(const char* pin = NULL) {
  18. return thisModem().initImpl(pin);
  19. }
  20. bool init(const char* pin = NULL) {
  21. return thisModem().initImpl(pin);
  22. }
  23. template <typename... Args>
  24. inline void sendAT(Args... cmd) {
  25. thisModem().streamWrite("AT", cmd..., thisModem().gsmNL);
  26. thisModem().stream.flush();
  27. TINY_GSM_YIELD(); /* DBG("### AT:", cmd...); */
  28. }
  29. void setBaud(uint32_t baud) {
  30. thisModem().setBaudImpl(baud);
  31. }
  32. // Test response to AT commands
  33. bool testAT(uint32_t timeout_ms = 10000L) {
  34. return thisModem().testATImpl(timeout_ms);
  35. }
  36. // Asks for modem information via the V.25TER standard ATI command
  37. // NOTE: The actual value and style of the response is quite varied
  38. String getModemInfo() {
  39. return thisModem().getModemInfoImpl();
  40. }
  41. // Gets the modem name (as it calls itself)
  42. String getModemName() {
  43. return thisModem().getModemNameImpl();
  44. }
  45. bool factoryDefault() {
  46. return thisModem().factoryDefaultImpl();
  47. }
  48. /*
  49. * Power functions
  50. */
  51. bool restart() {
  52. return thisModem().restartImpl();
  53. }
  54. bool poweroff() {
  55. return thisModem().powerOffImpl();
  56. }
  57. bool radioOff() {
  58. return thisModem().radioOffImpl();
  59. }
  60. bool sleepEnable(bool enable = true) {
  61. return thisModem().sleepEnableImpl(enable);
  62. }
  63. bool setPhoneFunctionality(uint8_t fun, bool reset = false) {
  64. return thisModem().setPhoneFunctionalityImpl(fun, reset);
  65. }
  66. /*
  67. * Generic network functions
  68. */
  69. // RegStatus getRegistrationStatus() {}
  70. bool isNetworkConnected() {
  71. return thisModem().isNetworkConnectedImpl();
  72. }
  73. // Waits for network attachment
  74. bool waitForNetwork(uint32_t timeout_ms = 60000L) {
  75. return thisModem().waitForNetworkImpl(timeout_ms);
  76. }
  77. // Gets signal quality report
  78. int16_t getSignalQuality() {
  79. return thisModem().getSignalQualityImpl();
  80. }
  81. String getLocalIP() {
  82. return thisModem().getLocalIPImpl();
  83. }
  84. IPAddress localIP() {
  85. return thisModem().TinyGsmIpFromString(thisModem().getLocalIP());
  86. }
  87. /*
  88. * CRTP Helper
  89. */
  90. protected:
  91. inline const modemType& thisModem() const {
  92. return static_cast<const modemType&>(*this);
  93. }
  94. inline modemType& thisModem() {
  95. return static_cast<modemType&>(*this);
  96. }
  97. /*
  98. * Basic functions
  99. */
  100. protected:
  101. void setBaudImpl(uint32_t baud) {
  102. thisModem().sendAT(GF("+IPR="), baud);
  103. thisModem().waitResponse();
  104. }
  105. bool testATImpl(uint32_t timeout_ms = 10000L) {
  106. for (uint32_t start = millis(); millis() - start < timeout_ms;) {
  107. thisModem().sendAT(GF(""));
  108. if (thisModem().waitResponse(200) == 1) { return true; }
  109. delay(100);
  110. }
  111. return false;
  112. }
  113. String getModemInfoImpl() {
  114. thisModem().sendAT(GF("I"));
  115. String res;
  116. if (thisModem().waitResponse(1000L, res) != 1) { return ""; }
  117. // Do the replaces twice so we cover both \r and \r\n type endings
  118. res.replace("\r\nOK\r\n", "");
  119. res.replace("\rOK\r", "");
  120. res.replace("\r\n", " ");
  121. res.replace("\r", " ");
  122. res.trim();
  123. return res;
  124. }
  125. String getModemNameImpl() {
  126. thisModem().sendAT(GF("+CGMI"));
  127. String res1;
  128. if (thisModem().waitResponse(1000L, res1) != 1) { return "unknown"; }
  129. res1.replace("\r\nOK\r\n", "");
  130. res1.replace("\rOK\r", "");
  131. res1.trim();
  132. thisModem().sendAT(GF("+GMM"));
  133. String res2;
  134. if (thisModem().waitResponse(1000L, res2) != 1) { return "unknown"; }
  135. res2.replace("\r\nOK\r\n", "");
  136. res2.replace("\rOK\r", "");
  137. res2.trim();
  138. String name = res1 + String(' ') + res2;
  139. DBG("### Modem:", name);
  140. return name;
  141. }
  142. bool factoryDefaultImpl() {
  143. thisModem().sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
  144. thisModem().waitResponse();
  145. thisModem().sendAT(GF("+IPR=0")); // Auto-baud
  146. thisModem().waitResponse();
  147. thisModem().sendAT(GF("&W")); // Write configuration
  148. return thisModem().waitResponse() == 1;
  149. }
  150. /*
  151. * Power functions
  152. */
  153. protected:
  154. bool radioOffImpl() {
  155. if (!thisModem().setPhoneFunctionality(0)) { return false; }
  156. delay(3000);
  157. return true;
  158. }
  159. bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  160. bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false)
  161. TINY_GSM_ATTR_NOT_IMPLEMENTED;
  162. /*
  163. * Generic network functions
  164. */
  165. protected:
  166. // Gets the modem's registration status via CREG/CGREG/CEREG
  167. // CREG = Generic network registration
  168. // CGREG = GPRS service registration
  169. // CEREG = EPS registration for LTE modules
  170. int8_t getRegistrationStatusXREG(const char* regCommand) {
  171. thisModem().sendAT('+', regCommand, '?');
  172. // check for any of the three for simplicity
  173. int8_t resp = thisModem().waitResponse(GF("+CREG:"), GF("+CGREG:"),
  174. GF("+CEREG:"));
  175. if (resp != 1 && resp != 2 && resp != 3) { return -1; }
  176. thisModem().streamSkipUntil(','); /* Skip format (0) */
  177. int status = thisModem().streamGetIntBefore('\n');
  178. thisModem().waitResponse();
  179. return status;
  180. }
  181. bool waitForNetworkImpl(uint32_t timeout_ms = 60000L) {
  182. for (uint32_t start = millis(); millis() - start < timeout_ms;) {
  183. if (thisModem().isNetworkConnected()) { return true; }
  184. delay(250);
  185. }
  186. return false;
  187. }
  188. // Gets signal quality report according to 3GPP TS command AT+CSQ
  189. int8_t getSignalQualityImpl() {
  190. thisModem().sendAT(GF("+CSQ"));
  191. if (thisModem().waitResponse(GF("+CSQ:")) != 1) { return 99; }
  192. int8_t res = thisModem().streamGetIntBefore(',');
  193. thisModem().waitResponse();
  194. return res;
  195. }
  196. String getLocalIPImpl() {
  197. thisModem().sendAT(GF("+CGPADDR=1"));
  198. if (thisModem().waitResponse(GF("+CGPADDR:")) != 1) { return ""; }
  199. thisModem().streamSkipUntil(','); // Skip context id
  200. String res = thisModem().stream.readStringUntil('\r');
  201. if (thisModem().waitResponse() != 1) { return ""; }
  202. return res;
  203. }
  204. static inline IPAddress TinyGsmIpFromString(const String& strIP) {
  205. int Parts[4] = {
  206. 0,
  207. };
  208. int Part = 0;
  209. for (uint8_t i = 0; i < strIP.length(); i++) {
  210. char c = strIP[i];
  211. if (c == '.') {
  212. Part++;
  213. if (Part > 3) { return IPAddress(0, 0, 0, 0); }
  214. continue;
  215. } else if (c >= '0' && c <= '9') {
  216. Parts[Part] *= 10;
  217. Parts[Part] += c - '0';
  218. } else {
  219. if (Part == 3) break;
  220. }
  221. }
  222. return IPAddress(Parts[0], Parts[1], Parts[2], Parts[3]);
  223. }
  224. /*
  225. Utilities
  226. */
  227. public:
  228. // Utility templates for writing/skipping characters on a stream
  229. template <typename T>
  230. inline void streamWrite(T last) {
  231. thisModem().stream.print(last);
  232. }
  233. template <typename T, typename... Args>
  234. inline void streamWrite(T head, Args... tail) {
  235. thisModem().stream.print(head);
  236. thisModem().streamWrite(tail...);
  237. }
  238. inline void streamClear() {
  239. while (thisModem().stream.available()) {
  240. thisModem().waitResponse(50, NULL, NULL);
  241. }
  242. }
  243. protected:
  244. inline bool streamGetLength(char* buf, int8_t numChars,
  245. const uint32_t timeout_ms = 1000L) {
  246. if (!buf) { return false; }
  247. int8_t numCharsReady = -1;
  248. uint32_t startMillis = millis();
  249. while (millis() - startMillis < timeout_ms &&
  250. (numCharsReady = thisModem().stream.available()) < numChars) {
  251. TINY_GSM_YIELD();
  252. }
  253. if (numCharsReady >= numChars) {
  254. thisModem().stream.readBytes(buf, numChars);
  255. return true;
  256. }
  257. return false;
  258. }
  259. inline int16_t streamGetIntLength(int8_t numChars,
  260. const uint32_t timeout_ms = 1000L) {
  261. char buf[numChars + 1];
  262. if (streamGetLength(buf, numChars, timeout_ms)) {
  263. buf[numChars] = '\0';
  264. return atoi(buf);
  265. }
  266. return -9999;
  267. }
  268. inline int16_t streamGetIntBefore(char lastChar) {
  269. char buf[7];
  270. size_t bytesRead = thisModem().stream.readBytesUntil(
  271. lastChar, buf, static_cast<size_t>(7));
  272. // if we read 7 or more bytes, it's an overflow
  273. if (bytesRead && bytesRead < 7) {
  274. buf[bytesRead] = '\0';
  275. int16_t res = atoi(buf);
  276. return res;
  277. }
  278. return -9999;
  279. }
  280. inline float streamGetFloatLength(int8_t numChars,
  281. const uint32_t timeout_ms = 1000L) {
  282. char buf[numChars + 1];
  283. if (streamGetLength(buf, numChars, timeout_ms)) {
  284. buf[numChars] = '\0';
  285. return atof(buf);
  286. }
  287. return -9999.0F;
  288. }
  289. inline float streamGetFloatBefore(char lastChar) {
  290. char buf[16];
  291. size_t bytesRead = thisModem().stream.readBytesUntil(
  292. lastChar, buf, static_cast<size_t>(16));
  293. // if we read 16 or more bytes, it's an overflow
  294. if (bytesRead && bytesRead < 16) {
  295. buf[bytesRead] = '\0';
  296. float res = atof(buf);
  297. return res;
  298. }
  299. return -9999.0F;
  300. }
  301. inline bool streamSkipUntil(const char c, const uint32_t timeout_ms = 1000L) {
  302. uint32_t startMillis = millis();
  303. while (millis() - startMillis < timeout_ms) {
  304. while (millis() - startMillis < timeout_ms &&
  305. !thisModem().stream.available()) {
  306. TINY_GSM_YIELD();
  307. }
  308. if (thisModem().stream.read() == c) { return true; }
  309. }
  310. return false;
  311. }
  312. };
  313. #endif // SRC_TINYGSMMODEM_H_