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.

645 lines
17 KiB

5 years ago
8 years ago
8 years ago
8 years ago
  1. /**
  2. * @file TinyGsmCommon.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef TinyGsmCommon_h
  9. #define TinyGsmCommon_h
  10. // The current library version number
  11. #define TINYGSM_VERSION "0.9.7"
  12. #if defined(SPARK) || defined(PARTICLE)
  13. #include "Particle.h"
  14. #elif defined(ARDUINO)
  15. #if ARDUINO >= 100
  16. #include "Arduino.h"
  17. #else
  18. #include "WProgram.h"
  19. #endif
  20. #endif
  21. #if defined(ARDUINO_DASH)
  22. #include <ArduinoCompat/Client.h>
  23. #else
  24. #include <Client.h>
  25. #endif
  26. #include <TinyGsmFifo.h>
  27. #ifndef TINY_GSM_YIELD_MS
  28. #define TINY_GSM_YIELD_MS 0
  29. #endif
  30. #ifndef TINY_GSM_YIELD
  31. #define TINY_GSM_YIELD() { delay(TINY_GSM_YIELD_MS); }
  32. #endif
  33. #define TINY_GSM_ATTR_NOT_AVAILABLE __attribute__((error("Not available on this modem type")))
  34. #define TINY_GSM_ATTR_NOT_IMPLEMENTED __attribute__((error("Not implemented")))
  35. #if defined(__AVR__)
  36. #define TINY_GSM_PROGMEM PROGMEM
  37. typedef const __FlashStringHelper* GsmConstStr;
  38. #define GFP(x) (reinterpret_cast<GsmConstStr>(x))
  39. #define GF(x) F(x)
  40. #else
  41. #define TINY_GSM_PROGMEM
  42. typedef const char* GsmConstStr;
  43. #define GFP(x) x
  44. #define GF(x) x
  45. #endif
  46. #ifdef TINY_GSM_DEBUG
  47. namespace {
  48. template<typename T>
  49. static void DBG_PLAIN(T last) {
  50. TINY_GSM_DEBUG.println(last);
  51. }
  52. template<typename T, typename... Args>
  53. static void DBG_PLAIN(T head, Args... tail) {
  54. TINY_GSM_DEBUG.print(head);
  55. TINY_GSM_DEBUG.print(' ');
  56. DBG_PLAIN(tail...);
  57. }
  58. template<typename... Args>
  59. static void DBG(Args... args) {
  60. TINY_GSM_DEBUG.print(GF("["));
  61. TINY_GSM_DEBUG.print(millis());
  62. TINY_GSM_DEBUG.print(GF("] "));
  63. DBG_PLAIN(args...);
  64. }
  65. }
  66. #else
  67. #define DBG_PLAIN(...)
  68. #define DBG(...)
  69. #endif
  70. template<class T>
  71. const T& TinyGsmMin(const T& a, const T& b)
  72. {
  73. return (b < a) ? b : a;
  74. }
  75. template<class T>
  76. const T& TinyGsmMax(const T& a, const T& b)
  77. {
  78. return (b < a) ? a : b;
  79. }
  80. template<class T>
  81. uint32_t TinyGsmAutoBaud(T& SerialAT, uint32_t minimum = 9600, uint32_t maximum = 115200)
  82. {
  83. static uint32_t rates[] = { 115200, 57600, 38400, 19200, 9600, 74400, 74880, 230400, 460800, 2400, 4800, 14400, 28800 };
  84. for (unsigned i = 0; i < sizeof(rates)/sizeof(rates[0]); i++) {
  85. uint32_t rate = rates[i];
  86. if (rate < minimum || rate > maximum) continue;
  87. DBG("Trying baud rate", rate, "...");
  88. SerialAT.begin(rate);
  89. delay(10);
  90. for (int i=0; i<10; i++) {
  91. SerialAT.print("AT\r\n");
  92. String input = SerialAT.readString();
  93. if (input.indexOf("OK") >= 0) {
  94. DBG("Modem responded at rate", rate);
  95. return rate;
  96. }
  97. }
  98. }
  99. return 0;
  100. }
  101. static inline
  102. IPAddress TinyGsmIpFromString(const String& strIP) {
  103. int Parts[4] = {0, };
  104. int Part = 0;
  105. for (uint8_t i=0; i<strIP.length(); i++) {
  106. char c = strIP[i];
  107. if (c == '.') {
  108. Part++;
  109. if (Part > 3) {
  110. return IPAddress(0,0,0,0);
  111. }
  112. continue;
  113. } else if (c >= '0' && c <= '9') {
  114. Parts[Part] *= 10;
  115. Parts[Part] += c - '0';
  116. } else {
  117. if (Part == 3) break;
  118. }
  119. }
  120. return IPAddress(Parts[0], Parts[1], Parts[2], Parts[3]);
  121. }
  122. static inline
  123. String TinyGsmDecodeHex7bit(String &instr) {
  124. String result;
  125. byte reminder = 0;
  126. int bitstate = 7;
  127. for (unsigned i=0; i<instr.length(); i+=2) {
  128. char buf[4] = { 0, };
  129. buf[0] = instr[i];
  130. buf[1] = instr[i+1];
  131. byte b = strtol(buf, NULL, 16);
  132. byte bb = b << (7 - bitstate);
  133. char c = (bb + reminder) & 0x7F;
  134. result += c;
  135. reminder = b >> bitstate;
  136. bitstate--;
  137. if (bitstate == 0) {
  138. char c = reminder;
  139. result += c;
  140. reminder = 0;
  141. bitstate = 7;
  142. }
  143. }
  144. return result;
  145. }
  146. static inline
  147. String TinyGsmDecodeHex8bit(String &instr) {
  148. String result;
  149. for (unsigned i=0; i<instr.length(); i+=2) {
  150. char buf[4] = { 0, };
  151. buf[0] = instr[i];
  152. buf[1] = instr[i+1];
  153. char b = strtol(buf, NULL, 16);
  154. result += b;
  155. }
  156. return result;
  157. }
  158. static inline
  159. String TinyGsmDecodeHex16bit(String &instr) {
  160. String result;
  161. for (unsigned i=0; i<instr.length(); i+=4) {
  162. char buf[4] = { 0, };
  163. buf[0] = instr[i];
  164. buf[1] = instr[i+1];
  165. char b = strtol(buf, NULL, 16);
  166. if (b) { // If high byte is non-zero, we can't handle it ;(
  167. #if defined(TINY_GSM_UNICODE_TO_HEX)
  168. result += "\\x";
  169. result += instr.substring(i, i+4);
  170. #else
  171. result += "?";
  172. #endif
  173. } else {
  174. buf[0] = instr[i+2];
  175. buf[1] = instr[i+3];
  176. b = strtol(buf, NULL, 16);
  177. result += b;
  178. }
  179. }
  180. return result;
  181. }
  182. // Connect to a IP address given as an IPAddress object by
  183. // converting said IP address to text
  184. #define TINY_GSM_CLIENT_CONNECT_OVERLOADS() \
  185. virtual int connect(IPAddress ip, uint16_t port, int timeout_s) { \
  186. String host; host.reserve(16); \
  187. host += ip[0]; \
  188. host += "."; \
  189. host += ip[1]; \
  190. host += "."; \
  191. host += ip[2]; \
  192. host += "."; \
  193. host += ip[3]; \
  194. return connect(host.c_str(), port, timeout_s); \
  195. } \
  196. virtual int connect(const char *host, uint16_t port) { \
  197. return connect(host, port, 75); \
  198. } \
  199. virtual int connect(IPAddress ip, uint16_t port) { \
  200. return connect(ip, port, 75); \
  201. }
  202. // Writes data out on the client using the modem send functionality
  203. #define TINY_GSM_CLIENT_WRITE() \
  204. virtual size_t write(const uint8_t *buf, size_t size) { \
  205. TINY_GSM_YIELD(); \
  206. at->maintain(); \
  207. return at->modemSend(buf, size, mux); \
  208. } \
  209. \
  210. virtual size_t write(uint8_t c) {\
  211. return write(&c, 1); \
  212. }\
  213. \
  214. virtual size_t write(const char *str) { \
  215. if (str == NULL) return 0; \
  216. return write((const uint8_t *)str, strlen(str)); \
  217. }
  218. // Returns the combined number of characters available in the TinyGSM fifo
  219. // and the modem chips internal fifo, doing an extra check-in with the
  220. // modem to see if anything has arrived without a UURC.
  221. #define TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK() \
  222. virtual int available() { \
  223. TINY_GSM_YIELD(); \
  224. if (!rx.size()) { \
  225. /* Workaround: sometimes module forgets to notify about data arrival.
  226. TODO: Currently we ping the module periodically,
  227. but maybe there's a better indicator that we need to poll */ \
  228. if (millis() - prev_check > 500) { \
  229. got_data = true; \
  230. prev_check = millis(); \
  231. } \
  232. at->maintain(); \
  233. } \
  234. return rx.size() + sock_available; \
  235. }
  236. // Returns the combined number of characters available in the TinyGSM fifo and
  237. // the modem chips internal fifo. Use this if you don't expect to miss any URC's.
  238. #define TINY_GSM_CLIENT_AVAILABLE_NO_BUFFER_CHECK() \
  239. virtual int available() { \
  240. TINY_GSM_YIELD(); \
  241. if (!rx.size()) { \
  242. at->maintain(); \
  243. } \
  244. return rx.size() + sock_available; \
  245. }
  246. // Returns the number of characters available in the TinyGSM fifo
  247. // Assumes the modem chip has no internal fifo
  248. #define TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO() \
  249. virtual int available() { \
  250. TINY_GSM_YIELD(); \
  251. if (!rx.size() && sock_connected) { \
  252. at->maintain(); \
  253. } \
  254. return rx.size(); \
  255. }
  256. #define TINY_GSM_CLIENT_READ_OVERLOAD() \
  257. virtual int read() { \
  258. uint8_t c; \
  259. if (read(&c, 1) == 1) { \
  260. return c; \
  261. } \
  262. return -1; \
  263. }
  264. // Reads characters out of the TinyGSM fifo, and from the modem chips internal
  265. // fifo if avaiable, also double checking with the modem if data has arrived
  266. // without issuing a UURC.
  267. #define TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK() \
  268. virtual int read(uint8_t *buf, size_t size) { \
  269. TINY_GSM_YIELD(); \
  270. at->maintain(); \
  271. size_t cnt = 0; \
  272. while (cnt < size) { \
  273. size_t chunk = TinyGsmMin(size-cnt, rx.size()); \
  274. if (chunk > 0) { \
  275. rx.get(buf, chunk); \
  276. buf += chunk; \
  277. cnt += chunk; \
  278. continue; \
  279. } \
  280. /* Workaround: sometimes module forgets to notify about data arrival.
  281. TODO: Currently we ping the module periodically,
  282. but maybe there's a better indicator that we need to poll */ \
  283. if (millis() - prev_check > 500) { \
  284. got_data = true; \
  285. prev_check = millis(); \
  286. } \
  287. /* TODO: Read directly into user buffer? */ \
  288. at->maintain(); \
  289. if (sock_available > 0) { \
  290. int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); \
  291. if (n == 0) break; \
  292. } else { \
  293. break; \
  294. } \
  295. } \
  296. return cnt; \
  297. } \
  298. TINY_GSM_CLIENT_READ_OVERLOAD()
  299. // Reads characters out of the TinyGSM fifo, and from the modem chips internal
  300. // fifo if avaiable. Use this if you don't expect to miss any URC's.
  301. #define TINY_GSM_CLIENT_READ_NO_BUFFER_CHECK() \
  302. virtual int read(uint8_t *buf, size_t size) { \
  303. TINY_GSM_YIELD(); \
  304. at->maintain(); \
  305. size_t cnt = 0; \
  306. while (cnt < size) { \
  307. size_t chunk = TinyGsmMin(size-cnt, rx.size()); \
  308. if (chunk > 0) { \
  309. rx.get(buf, chunk); \
  310. buf += chunk; \
  311. cnt += chunk; \
  312. continue; \
  313. } \
  314. /* TODO: Read directly into user buffer? */ \
  315. at->maintain(); \
  316. if (sock_available > 0) { \
  317. int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); \
  318. if (n == 0) break; \
  319. } else { \
  320. break; \
  321. } \
  322. } \
  323. return cnt; \
  324. } \
  325. TINY_GSM_CLIENT_READ_OVERLOAD()
  326. // Reads characters out of the TinyGSM fifo, waiting for any URC's from the
  327. // modem for new data if there's nothing in the fifo. This assumes the
  328. //modem chip itself has no fifo.
  329. #define TINY_GSM_CLIENT_READ_NO_MODEM_FIFO() \
  330. virtual int read(uint8_t *buf, size_t size) { \
  331. TINY_GSM_YIELD(); \
  332. size_t cnt = 0; \
  333. uint32_t _startMillis = millis(); \
  334. while (cnt < size && millis() - _startMillis < _timeout) { \
  335. size_t chunk = TinyGsmMin(size-cnt, rx.size()); \
  336. if (chunk > 0) { \
  337. rx.get(buf, chunk); \
  338. buf += chunk; \
  339. cnt += chunk; \
  340. continue; \
  341. } \
  342. /* TODO: Read directly into user buffer? */ \
  343. if (!rx.size() && sock_connected) { \
  344. at->maintain(); \
  345. } \
  346. } \
  347. return cnt; \
  348. } \
  349. \
  350. virtual int read() { \
  351. uint8_t c; \
  352. if (read(&c, 1) == 1) { \
  353. return c; \
  354. } \
  355. return -1; \
  356. }
  357. // Read and dump anything remaining in the modem's internal buffer.
  358. // Using this in the client stop() function.
  359. // The socket will appear open in response to connected() even after it
  360. // closes until all data is read from the buffer.
  361. // Doing it this way allows the external mcu to find and get all of the data
  362. // that it wants from the socket even if it was closed externally.
  363. #define TINY_GSM_CLIENT_DUMP_MODEM_BUFFER() \
  364. TINY_GSM_YIELD(); \
  365. rx.clear(); \
  366. at->maintain(); \
  367. unsigned long startMillis = millis(); \
  368. while (sock_available > 0 && (millis() - startMillis < maxWaitMs)) { \
  369. at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux); \
  370. rx.clear(); \
  371. at->maintain(); \
  372. }
  373. // The peek, flush, and connected functions
  374. #define TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED() \
  375. virtual int peek() { return -1; } /* TODO */ \
  376. \
  377. virtual void flush() { at->stream.flush(); } \
  378. \
  379. virtual uint8_t connected() { \
  380. if (available()) { \
  381. return true; \
  382. } \
  383. return sock_connected; \
  384. } \
  385. virtual operator bool() { return connected(); }
  386. // Set baud rate via the V.25TER standard IPR command
  387. #define TINY_GSM_MODEM_SET_BAUD_IPR() \
  388. void setBaud(unsigned long baud) { \
  389. sendAT(GF("+IPR="), baud); \
  390. }
  391. // Test response to AT commands
  392. #define TINY_GSM_MODEM_TEST_AT() \
  393. bool testAT(unsigned long timeout_ms = 10000L) { \
  394. for (unsigned long start = millis(); millis() - start < timeout_ms; ) { \
  395. sendAT(GF("")); \
  396. if (waitResponse(200) == 1) return true; \
  397. delay(100); \
  398. } \
  399. return false; \
  400. }
  401. // Keeps listening for modem URC's and iterates through sockets
  402. // to see if any data is avaiable
  403. #define TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS() \
  404. void maintain() { \
  405. for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) { \
  406. GsmClient* sock = sockets[mux]; \
  407. if (sock && sock->got_data) { \
  408. sock->got_data = false; \
  409. sock->sock_available = modemGetAvailable(mux); \
  410. } \
  411. } \
  412. while (stream.available()) { \
  413. waitResponse(15, NULL, NULL); \
  414. } \
  415. }
  416. // Keeps listening for modem URC's - doesn't check socks because
  417. // modem has no internal fifo
  418. #define TINY_GSM_MODEM_MAINTAIN_LISTEN() \
  419. void maintain() { \
  420. waitResponse(100, NULL, NULL); \
  421. }
  422. // Asks for modem information via the V.25TER standard ATI command
  423. // NOTE: The actual value and style of the response is quite varied
  424. #define TINY_GSM_MODEM_GET_INFO_ATI() \
  425. String getModemInfo() { \
  426. sendAT(GF("I")); \
  427. String res; \
  428. if (waitResponse(1000L, res) != 1) { \
  429. return ""; \
  430. } \
  431. res.replace(GSM_NL "OK" GSM_NL, ""); \
  432. res.replace(GSM_NL, " "); \
  433. res.trim(); \
  434. return res; \
  435. }
  436. // Unlocks a sim via the 3GPP TS command AT+CPIN
  437. #define TINY_GSM_MODEM_SIM_UNLOCK_CPIN() \
  438. bool simUnlock(const char *pin) { \
  439. sendAT(GF("+CPIN=\""), pin, GF("\"")); \
  440. return waitResponse() == 1; \
  441. }
  442. // Gets the CCID of a sim card via AT+CCID
  443. #define TINY_GSM_MODEM_GET_SIMCCID_CCID() \
  444. String getSimCCID() { \
  445. sendAT(GF("+CCID")); \
  446. if (waitResponse(GF(GSM_NL "+CCID:")) != 1) { \
  447. return ""; \
  448. } \
  449. String res = stream.readStringUntil('\n'); \
  450. waitResponse(); \
  451. res.trim(); \
  452. return res; \
  453. }
  454. // Asks for TA Serial Number Identification (IMEI) via the V.25TER standard AT+GSN command
  455. #define TINY_GSM_MODEM_GET_IMEI_GSN() \
  456. String getIMEI() { \
  457. sendAT(GF("+GSN")); \
  458. if (waitResponse(GF(GSM_NL)) != 1) { \
  459. return ""; \
  460. } \
  461. String res = stream.readStringUntil('\n'); \
  462. waitResponse(); \
  463. res.trim(); \
  464. return res; \
  465. }
  466. // Gets the modem's registration status via CREG/CGREG/CEREG
  467. // CREG = Generic network registration
  468. // CGREG = GPRS service registration
  469. // CEREG = EPS registration for LTE modules
  470. #define TINY_GSM_MODEM_GET_REGISTRATION_XREG(regCommand) \
  471. RegStatus getRegistrationStatus() { \
  472. sendAT(GF("+" #regCommand "?")); \
  473. if (waitResponse(GF(GSM_NL "+" #regCommand ":")) != 1) { \
  474. return REG_UNKNOWN; \
  475. } \
  476. streamSkipUntil(','); /* Skip format (0) */ \
  477. int status = stream.readStringUntil('\n').toInt(); \
  478. waitResponse(); \
  479. return (RegStatus)status; \
  480. }
  481. // Gets the current network operator via the 3GPP TS command AT+COPS
  482. #define TINY_GSM_MODEM_GET_OPERATOR_COPS() \
  483. String getOperator() { \
  484. sendAT(GF("+COPS?")); \
  485. if (waitResponse(GF(GSM_NL "+COPS:")) != 1) { \
  486. return ""; \
  487. } \
  488. streamSkipUntil('"'); /* Skip mode and format */ \
  489. String res = stream.readStringUntil('"'); \
  490. waitResponse(); \
  491. return res; \
  492. }
  493. // Waits for network attachment
  494. #define TINY_GSM_MODEM_WAIT_FOR_NETWORK() \
  495. bool waitForNetwork(unsigned long timeout_ms = 60000L) { \
  496. for (unsigned long start = millis(); millis() - start < timeout_ms; ) { \
  497. if (isNetworkConnected()) { \
  498. return true; \
  499. } \
  500. delay(250); \
  501. } \
  502. return false; \
  503. }
  504. // Checks if current attached to GPRS/EPS service
  505. #define TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED() \
  506. bool isGprsConnected() { \
  507. sendAT(GF("+CGATT?")); \
  508. if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) { \
  509. return false; \
  510. } \
  511. int res = stream.readStringUntil('\n').toInt(); \
  512. waitResponse(); \
  513. if (res != 1) \
  514. return false; \
  515. \
  516. return localIP() != IPAddress(0,0,0,0); \
  517. }
  518. // Gets signal quality report according to 3GPP TS command AT+CSQ
  519. #define TINY_GSM_MODEM_GET_CSQ() \
  520. int16_t getSignalQuality() { \
  521. sendAT(GF("+CSQ")); \
  522. if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) { \
  523. return 99; \
  524. } \
  525. int res = stream.readStringUntil(',').toInt(); \
  526. waitResponse(); \
  527. return res; \
  528. }
  529. // Yields up to a time-out period and then reads a character from the stream into the mux FIFO
  530. // TODO: Do we need to wait two _timeout periods for no character return? Will wait once in the first
  531. // "while !stream.available()" and then will wait again in the stream.read() function.
  532. #define TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT \
  533. uint32_t startMillis = millis(); \
  534. while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); } \
  535. char c = stream.read(); \
  536. sockets[mux]->rx.put(c);
  537. // Utility templates for writing/skipping characters on a stream
  538. #define TINY_GSM_MODEM_STREAM_UTILITIES() \
  539. template<typename T> \
  540. void streamWrite(T last) { \
  541. stream.print(last); \
  542. } \
  543. \
  544. template<typename T, typename... Args> \
  545. void streamWrite(T head, Args... tail) { \
  546. stream.print(head); \
  547. streamWrite(tail...); \
  548. } \
  549. \
  550. template<typename... Args> \
  551. void sendAT(Args... cmd) { \
  552. streamWrite("AT", cmd..., GSM_NL); \
  553. stream.flush(); \
  554. TINY_GSM_YIELD(); \
  555. /* DBG("### AT:", cmd...); */ \
  556. } \
  557. \
  558. bool streamSkipUntil(const char c, const unsigned long timeout_ms = 1000L) { \
  559. unsigned long startMillis = millis(); \
  560. while (millis() - startMillis < timeout_ms) { \
  561. while (millis() - startMillis < timeout_ms && !stream.available()) { \
  562. TINY_GSM_YIELD(); \
  563. } \
  564. if (stream.read() == c) { \
  565. return true; \
  566. } \
  567. } \
  568. return false; \
  569. }
  570. #endif