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.

328 lines
8.7 KiB

  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. /*
  64. * Generic network functions
  65. */
  66. // RegStatus getRegistrationStatus() {}
  67. bool isNetworkConnected() {
  68. return thisModem().isNetworkConnectedImpl();
  69. }
  70. // Waits for network attachment
  71. bool waitForNetwork(uint32_t timeout_ms = 60000L) {
  72. return thisModem().waitForNetworkImpl(timeout_ms);
  73. }
  74. // Gets signal quality report
  75. int16_t getSignalQuality() {
  76. return thisModem().getSignalQualityImpl();
  77. }
  78. String getLocalIP() {
  79. return thisModem().getLocalIPImpl();
  80. }
  81. IPAddress localIP() {
  82. return thisModem().TinyGsmIpFromString(thisModem().getLocalIP());
  83. }
  84. /*
  85. * CRTP Helper
  86. */
  87. protected:
  88. inline const modemType& thisModem() const {
  89. return static_cast<const modemType&>(*this);
  90. }
  91. inline modemType& thisModem() {
  92. return static_cast<modemType&>(*this);
  93. }
  94. /*
  95. * Basic functions
  96. */
  97. protected:
  98. void setBaudImpl(uint32_t baud) {
  99. thisModem().sendAT(GF("+IPR="), baud);
  100. thisModem().waitResponse();
  101. }
  102. bool testATImpl(uint32_t timeout_ms = 10000L) {
  103. for (uint32_t start = millis(); millis() - start < timeout_ms;) {
  104. thisModem().sendAT(GF(""));
  105. if (thisModem().waitResponse(200) == 1) { return true; }
  106. delay(100);
  107. }
  108. return false;
  109. }
  110. String getModemInfoImpl() {
  111. thisModem().sendAT(GF("I"));
  112. String res;
  113. if (thisModem().waitResponse(1000L, res) != 1) { return ""; }
  114. // Do the replaces twice so we cover both \r and \r\n type endings
  115. res.replace("\r\nOK\r\n", "");
  116. res.replace("\rOK\r", "");
  117. res.replace("\r\n", " ");
  118. res.replace("\r", " ");
  119. res.trim();
  120. return res;
  121. }
  122. String getModemNameImpl() {
  123. thisModem().sendAT(GF("+CGMI"));
  124. String res1;
  125. if (thisModem().waitResponse(1000L, res1) != 1) { return "unknown"; }
  126. res1.replace("\r\nOK\r\n", "");
  127. res1.replace("\rOK\r", "");
  128. res1.trim();
  129. thisModem().sendAT(GF("+GMM"));
  130. String res2;
  131. if (thisModem().waitResponse(1000L, res2) != 1) { return "unknown"; }
  132. res1.replace("\r\nOK\r\n", "");
  133. res1.replace("\rOK\r", "");
  134. res2.trim();
  135. String name = res1 + String(' ') + res2;
  136. DBG("### Modem:", name);
  137. return name;
  138. }
  139. bool factoryDefaultImpl() {
  140. thisModem().sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
  141. thisModem().waitResponse();
  142. thisModem().sendAT(GF("+IPR=0")); // Auto-baud
  143. thisModem().waitResponse();
  144. thisModem().sendAT(GF("&W")); // Write configuration
  145. return thisModem().waitResponse() == 1;
  146. }
  147. /*
  148. * Power functions
  149. */
  150. protected:
  151. bool radioOffImpl() {
  152. thisModem().sendAT(GF("+CFUN=0"));
  153. if (thisModem().waitResponse(10000L) != 1) { return false; }
  154. delay(3000);
  155. return true;
  156. }
  157. bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  158. /*
  159. * Generic network functions
  160. */
  161. protected:
  162. // Gets the modem's registration status via CREG/CGREG/CEREG
  163. // CREG = Generic network registration
  164. // CGREG = GPRS service registration
  165. // CEREG = EPS registration for LTE modules
  166. int8_t getRegistrationStatusXREG(const char* regCommand) {
  167. thisModem().sendAT('+', regCommand, '?');
  168. // check for any of the three for simplicity
  169. int8_t resp = thisModem().waitResponse(GF("+CREG:"), GF("+CGREG:"),
  170. GF("+CEREG:"));
  171. if (resp != 1 && resp != 2 && resp != 3) { return -1; }
  172. thisModem().streamSkipUntil(','); /* Skip format (0) */
  173. int status = thisModem().streamGetIntBefore('\n');
  174. thisModem().waitResponse();
  175. return status;
  176. }
  177. bool waitForNetworkImpl(uint32_t timeout_ms = 60000L) {
  178. for (uint32_t start = millis(); millis() - start < timeout_ms;) {
  179. if (thisModem().isNetworkConnected()) { return true; }
  180. delay(250);
  181. }
  182. return false;
  183. }
  184. // Gets signal quality report according to 3GPP TS command AT+CSQ
  185. int8_t getSignalQualityImpl() {
  186. thisModem().sendAT(GF("+CSQ"));
  187. if (thisModem().waitResponse(GF("+CSQ:")) != 1) { return 99; }
  188. int8_t res = thisModem().streamGetIntBefore(',');
  189. thisModem().waitResponse();
  190. return res;
  191. }
  192. String getLocalIPImpl() {
  193. thisModem().sendAT(GF("+CGPADDR=1"));
  194. if (thisModem().waitResponse(GF("+CGPADDR:")) != 1) { return ""; }
  195. thisModem().streamSkipUntil(','); // Skip context id
  196. String res = thisModem().stream.readStringUntil('\r');
  197. if (thisModem().waitResponse() != 1) { return ""; }
  198. return res;
  199. }
  200. static inline IPAddress TinyGsmIpFromString(const String& strIP) {
  201. int Parts[4] = {
  202. 0,
  203. };
  204. int Part = 0;
  205. for (uint8_t i = 0; i < strIP.length(); i++) {
  206. char c = strIP[i];
  207. if (c == '.') {
  208. Part++;
  209. if (Part > 3) { return IPAddress(0, 0, 0, 0); }
  210. continue;
  211. } else if (c >= '0' && c <= '9') {
  212. Parts[Part] *= 10;
  213. Parts[Part] += c - '0';
  214. } else {
  215. if (Part == 3) break;
  216. }
  217. }
  218. return IPAddress(Parts[0], Parts[1], Parts[2], Parts[3]);
  219. }
  220. /*
  221. Utilities
  222. */
  223. protected:
  224. // Utility templates for writing/skipping characters on a stream
  225. template <typename T>
  226. inline void streamWrite(T last) {
  227. thisModem().stream.print(last);
  228. }
  229. template <typename T, typename... Args>
  230. inline void streamWrite(T head, Args... tail) {
  231. thisModem().stream.print(head);
  232. thisModem().streamWrite(tail...);
  233. }
  234. inline int16_t streamGetIntLength(int8_t numChars) {
  235. char buf[6];
  236. size_t bytesRead = thisModem().stream.readBytes(buf, numChars);
  237. if (bytesRead) {
  238. buf[numChars] = '\0';
  239. int16_t res = atoi(buf);
  240. return res;
  241. } else {
  242. return -9999;
  243. }
  244. }
  245. inline int16_t streamGetIntBefore(char lastChar) {
  246. char buf[6];
  247. size_t bytesRead = thisModem().stream.readBytesUntil(
  248. lastChar, buf, static_cast<size_t>(6));
  249. if (bytesRead) {
  250. buf[bytesRead] = '\0';
  251. int16_t res = atoi(buf);
  252. return res;
  253. } else {
  254. return -9999;
  255. }
  256. }
  257. inline float streamGetFloatLength(int8_t numChars) {
  258. char buf[16];
  259. size_t bytesRead = thisModem().stream.readBytes(buf, numChars);
  260. DBG("### bytesRead:", bytesRead);
  261. if (bytesRead) {
  262. buf[numChars] = '\0';
  263. int16_t res = atof(buf);
  264. return res;
  265. } else {
  266. return static_cast<float>(-9999);
  267. }
  268. }
  269. inline float streamGetFloatBefore(char lastChar) {
  270. char buf[16];
  271. size_t bytesRead = thisModem().stream.readBytesUntil(
  272. lastChar, buf, static_cast<size_t>(16));
  273. if (bytesRead) {
  274. buf[bytesRead] = '\0';
  275. float res = atof(buf);
  276. return res;
  277. } else {
  278. return static_cast<float>(-9999);
  279. }
  280. }
  281. inline bool streamSkipUntil(const char c, const uint32_t timeout_ms = 1000L) {
  282. uint32_t startMillis = millis();
  283. while (millis() - startMillis < timeout_ms) {
  284. while (millis() - startMillis < timeout_ms &&
  285. !thisModem().stream.available()) {
  286. TINY_GSM_YIELD();
  287. }
  288. if (thisModem().stream.read() == c) { return true; }
  289. }
  290. return false;
  291. }
  292. inline void streamClear() {
  293. while (thisModem().stream.available()) {
  294. thisModem().waitResponse(50, NULL, NULL);
  295. }
  296. }
  297. };
  298. #endif // SRC_TINYGSMMODEM_H_