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.

849 lines
20 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. /**
  2. * @file TinyGsmClientBG96.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Apr 2018
  7. */
  8. #ifndef TinyGsmClientBG96_h
  9. #define TinyGsmClientBG96_h
  10. //#pragma message("TinyGSM: TinyGsmClientBG96")
  11. //#define TINY_GSM_DEBUG Serial
  12. //#define TINY_GSM_USE_HEX
  13. #define TINY_GSM_MUX_COUNT 12
  14. #include <TinyGsmCommon.h>
  15. #define GSM_NL "\r\n"
  16. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  17. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  18. enum SimStatus {
  19. SIM_ERROR = 0,
  20. SIM_READY = 1,
  21. SIM_LOCKED = 2,
  22. };
  23. enum RegStatus {
  24. REG_UNREGISTERED = 0,
  25. REG_SEARCHING = 2,
  26. REG_DENIED = 3,
  27. REG_OK_HOME = 1,
  28. REG_OK_ROAMING = 5,
  29. REG_UNKNOWN = 4,
  30. };
  31. class TinyGsmBG96
  32. {
  33. public:
  34. class GsmClient : public Client
  35. {
  36. friend class TinyGsmBG96;
  37. typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
  38. public:
  39. GsmClient() {}
  40. GsmClient(TinyGsmBG96& modem, uint8_t mux = 1) {
  41. init(&modem, mux);
  42. }
  43. bool init(TinyGsmBG96* modem, uint8_t mux = 1) {
  44. this->at = modem;
  45. this->mux = mux;
  46. sock_available = 0;
  47. sock_connected = false;
  48. got_data = false;
  49. at->sockets[mux] = this;
  50. // ^^ TODO: attach the socket here at init? Or later at connect?
  51. // Currently done inconsistently between modems
  52. return true;
  53. }
  54. public:
  55. virtual int connect(const char *host, uint16_t port) {
  56. stop();
  57. TINY_GSM_YIELD();
  58. rx.clear();
  59. sock_connected = at->modemConnect(host, port, mux);
  60. // sock_connected = at->modemConnect(host, port, &mux);
  61. // at->sockets[mux] = this;
  62. // ^^ TODO: attach the socket after attempting connection or above at init?
  63. // Currently done inconsistently between modems
  64. return sock_connected;
  65. }
  66. virtual int connect(IPAddress ip, uint16_t port) {
  67. String host; host.reserve(16);
  68. host += ip[0];
  69. host += ".";
  70. host += ip[1];
  71. host += ".";
  72. host += ip[2];
  73. host += ".";
  74. host += ip[3];
  75. return connect(host.c_str(), port);
  76. }
  77. virtual void stop() {
  78. TINY_GSM_YIELD();
  79. // Read and dump anything remaining in the modem's internal buffer.
  80. // The socket will appear open in response to connected() even after it
  81. // closes until all data is read from the buffer.
  82. // Doing it this way allows the external mcu to find and get all of the data
  83. // that it wants from the socket even if it was closed externally.
  84. rx.clear();
  85. at->maintain();
  86. while (sock_available > 0) {
  87. at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
  88. rx.clear();
  89. at->maintain();
  90. }
  91. at->sendAT(GF("+QICLOSE="), mux);
  92. sock_connected = false;
  93. at->waitResponse();
  94. }
  95. virtual size_t write(const uint8_t *buf, size_t size) {
  96. TINY_GSM_YIELD();
  97. at->maintain();
  98. return at->modemSend(buf, size, mux);
  99. }
  100. virtual size_t write(uint8_t c) {
  101. return write(&c, 1);
  102. }
  103. virtual size_t write(const char *str) {
  104. if (str == NULL) return 0;
  105. return write((const uint8_t *)str, strlen(str));
  106. }
  107. virtual int available() {
  108. TINY_GSM_YIELD();
  109. if (!rx.size()) {
  110. at->maintain();
  111. }
  112. return rx.size() + sock_available;
  113. }
  114. virtual int read(uint8_t *buf, size_t size) {
  115. TINY_GSM_YIELD();
  116. at->maintain();
  117. size_t cnt = 0;
  118. while (cnt < size) {
  119. size_t chunk = TinyGsmMin(size-cnt, rx.size());
  120. if (chunk > 0) {
  121. rx.get(buf, chunk);
  122. buf += chunk;
  123. cnt += chunk;
  124. continue;
  125. }
  126. // TODO: Read directly into user buffer?
  127. at->maintain();
  128. if (sock_available > 0) {
  129. at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
  130. } else {
  131. break;
  132. }
  133. }
  134. return cnt;
  135. }
  136. virtual int read() {
  137. uint8_t c;
  138. if (read(&c, 1) == 1) {
  139. return c;
  140. }
  141. return -1;
  142. }
  143. virtual int peek() { return -1; } //TODO
  144. virtual void flush() { at->stream.flush(); }
  145. virtual uint8_t connected() {
  146. if (available()) {
  147. return true;
  148. }
  149. return sock_connected;
  150. }
  151. virtual operator bool() { return connected(); }
  152. /*
  153. * Extended API
  154. */
  155. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  156. private:
  157. TinyGsmBG96* at;
  158. uint8_t mux;
  159. uint16_t sock_available;
  160. bool sock_connected;
  161. bool got_data;
  162. RxFifo rx;
  163. };
  164. // class GsmClientSecure : public GsmClient
  165. // {
  166. // public:
  167. // GsmClientSecure() {}
  168. //
  169. // GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 1)
  170. // : GsmClient(modem, mux)
  171. // {}
  172. //
  173. // public:
  174. // virtual int connect(const char *host, uint16_t port) {
  175. // stop();
  176. // TINY_GSM_YIELD();
  177. // rx.clear();
  178. // sock_connected = at->modemConnect(host, port, mux, true);
  179. // return sock_connected;
  180. // }
  181. // };
  182. public:
  183. TinyGsmBG96(Stream& stream)
  184. : stream(stream)
  185. {
  186. memset(sockets, 0, sizeof(sockets));
  187. }
  188. /*
  189. * Basic functions
  190. */
  191. bool init(const char* pin = NULL) {
  192. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  193. if (!testAT()) {
  194. return false;
  195. }
  196. sendAT(GF("&FZE0")); // Factory + Reset + Echo Off
  197. if (waitResponse() != 1) {
  198. return false;
  199. }
  200. DBG(GF("### Modem:"), getModemName());
  201. getSimStatus();
  202. return true;
  203. }
  204. bool begin(const char* pin = NULL) {
  205. return init(pin);
  206. }
  207. String getModemName() {
  208. return "Quectel BG96";
  209. }
  210. void setBaud(unsigned long baud) {
  211. sendAT(GF("+IPR="), baud);
  212. }
  213. bool testAT(unsigned long timeout = 10000L) {
  214. for (unsigned long start = millis(); millis() - start < timeout; ) {
  215. sendAT(GF(""));
  216. if (waitResponse(200) == 1) return true;
  217. delay(100);
  218. }
  219. return false;
  220. }
  221. void maintain() {
  222. for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
  223. GsmClient* sock = sockets[mux];
  224. if (sock && sock->got_data) {
  225. sock->got_data = false;
  226. sock->sock_available = modemGetAvailable(mux);
  227. }
  228. }
  229. while (stream.available()) {
  230. waitResponse(10, NULL, NULL);
  231. }
  232. }
  233. bool factoryDefault() {
  234. sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
  235. waitResponse();
  236. sendAT(GF("+IPR=0")); // Auto-baud
  237. waitResponse();
  238. sendAT(GF("&W")); // Write configuration
  239. return waitResponse() == 1;
  240. }
  241. String getModemInfo() {
  242. sendAT(GF("I"));
  243. String res;
  244. if (waitResponse(1000L, res) != 1) {
  245. return "";
  246. }
  247. res.replace(GSM_NL "OK" GSM_NL, "");
  248. res.replace(GSM_NL, " ");
  249. res.trim();
  250. return res;
  251. }
  252. bool hasSSL() {
  253. return false; // TODO: For now
  254. }
  255. bool hasWifi() {
  256. return false;
  257. }
  258. bool hasGPRS() {
  259. return true;
  260. }
  261. /*
  262. * Power functions
  263. */
  264. bool restart() {
  265. if (!testAT()) {
  266. return false;
  267. }
  268. sendAT(GF("+CFUN=1,1"));
  269. if (waitResponse(60000L, GF("POWERED DOWN")) != 1) {
  270. return false;
  271. }
  272. delay(3000);
  273. return init();
  274. }
  275. bool poweroff() {
  276. sendAT(GF("+QPOWD=1"));
  277. waitResponse(300); // returns OK first
  278. return waitResponse(300, GF("POWERED DOWN")) == 1;
  279. }
  280. bool radioOff() {
  281. sendAT(GF("+CFUN=0"));
  282. if (waitResponse(10000L) != 1) {
  283. return false;
  284. }
  285. delay(3000);
  286. return true;
  287. }
  288. /*
  289. * SIM card functions
  290. */
  291. bool simUnlock(const char *pin) {
  292. sendAT(GF("+CPIN=\""), pin, GF("\""));
  293. return waitResponse() == 1;
  294. }
  295. String getSimCCID() {
  296. sendAT(GF("+ICCID"));
  297. if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) {
  298. return "";
  299. }
  300. String res = stream.readStringUntil('\n');
  301. waitResponse();
  302. res.trim();
  303. return res;
  304. }
  305. String getIMEI() {
  306. sendAT(GF("+GSN"));
  307. if (waitResponse(GF(GSM_NL)) != 1) {
  308. return "";
  309. }
  310. String res = stream.readStringUntil('\n');
  311. waitResponse();
  312. res.trim();
  313. return res;
  314. }
  315. SimStatus getSimStatus(unsigned long timeout = 10000L) {
  316. for (unsigned long start = millis(); millis() - start < timeout; ) {
  317. sendAT(GF("+CPIN?"));
  318. if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
  319. delay(1000);
  320. continue;
  321. }
  322. int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED"));
  323. waitResponse();
  324. switch (status) {
  325. case 2:
  326. case 3: return SIM_LOCKED;
  327. case 1: return SIM_READY;
  328. default: return SIM_ERROR;
  329. }
  330. }
  331. return SIM_ERROR;
  332. }
  333. RegStatus getRegistrationStatus() {
  334. sendAT(GF("+CREG?"));
  335. if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
  336. return REG_UNKNOWN;
  337. }
  338. streamSkipUntil(','); // Skip format (0)
  339. int status = stream.readStringUntil('\n').toInt();
  340. waitResponse();
  341. return (RegStatus)status;
  342. }
  343. String getOperator() {
  344. sendAT(GF("+COPS?"));
  345. if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
  346. return "";
  347. }
  348. streamSkipUntil('"'); // Skip mode and format
  349. String res = stream.readStringUntil('"');
  350. waitResponse();
  351. return res;
  352. }
  353. /*
  354. * Generic network functions
  355. */
  356. int16_t getSignalQuality() {
  357. sendAT(GF("+CSQ"));
  358. if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
  359. return 99;
  360. }
  361. int res = stream.readStringUntil(',').toInt();
  362. waitResponse();
  363. return res;
  364. }
  365. bool isNetworkConnected() {
  366. RegStatus s = getRegistrationStatus();
  367. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  368. }
  369. bool waitForNetwork(unsigned long timeout = 60000L) {
  370. for (unsigned long start = millis(); millis() - start < timeout; ) {
  371. if (isNetworkConnected()) {
  372. return true;
  373. }
  374. delay(250);
  375. }
  376. return false;
  377. }
  378. /*
  379. * GPRS functions
  380. */
  381. bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
  382. gprsDisconnect();
  383. //Configure the TCPIP Context
  384. sendAT(GF("+QICSGP=1,1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
  385. if (waitResponse() != 1) {
  386. return false;
  387. }
  388. //Activate GPRS/CSD Context
  389. sendAT(GF("+QIACT=1"));
  390. if (waitResponse(150000L) != 1) {
  391. return false;
  392. }
  393. //Attach to Packet Domain service - is this necessary?
  394. sendAT(GF("+CGATT=1"));
  395. if (waitResponse(60000L) != 1) {
  396. return false;
  397. }
  398. return true;
  399. }
  400. bool gprsDisconnect() {
  401. sendAT(GF("+QIDEACT=1")); // Deactivate the bearer context
  402. if (waitResponse(40000L) != 1)
  403. return false;
  404. return true;
  405. }
  406. bool isGprsConnected() {
  407. sendAT(GF("+CGATT?"));
  408. if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
  409. return false;
  410. }
  411. int res = stream.readStringUntil('\n').toInt();
  412. waitResponse();
  413. if (res != 1)
  414. return false;
  415. return localIP() != IPAddress(0,0,0,0);
  416. }
  417. /*
  418. * IP Address functions
  419. */
  420. String getLocalIP() {
  421. sendAT(GF("+QILOCIP"));
  422. stream.readStringUntil('\n');
  423. String res = stream.readStringUntil('\n');
  424. if (waitResponse() != 1) {
  425. return "";
  426. }
  427. return res;
  428. }
  429. IPAddress localIP() {
  430. return TinyGsmIpFromString(getLocalIP());
  431. }
  432. /*
  433. * Phone Call functions
  434. */
  435. bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
  436. bool callAnswer() {
  437. sendAT(GF("A"));
  438. return waitResponse() == 1;
  439. }
  440. // Returns true on pick-up, false on error/busy
  441. bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  442. bool callHangup() {
  443. sendAT(GF("H"));
  444. return waitResponse() == 1;
  445. }
  446. // 0-9,*,#,A,B,C,D
  447. bool dtmfSend(char cmd, int duration_ms = 100) { // TODO: check
  448. duration_ms = constrain(duration_ms, 100, 1000);
  449. sendAT(GF("+VTD="), duration_ms / 100); // VTD accepts in 1/10 of a second
  450. waitResponse();
  451. sendAT(GF("+VTS="), cmd);
  452. return waitResponse(10000L) == 1;
  453. }
  454. /*
  455. * Messaging functions
  456. */
  457. String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  458. bool sendSMS(const String& number, const String& text) {
  459. sendAT(GF("+CMGF=1"));
  460. waitResponse();
  461. //Set GSM 7 bit default alphabet (3GPP TS 23.038)
  462. sendAT(GF("+CSCS=\"GSM\""));
  463. waitResponse();
  464. sendAT(GF("+CMGS=\""), number, GF("\""));
  465. if (waitResponse(GF(">")) != 1) {
  466. return false;
  467. }
  468. stream.print(text);
  469. stream.write((char)0x1A);
  470. stream.flush();
  471. return waitResponse(60000L) == 1;
  472. }
  473. bool sendSMS_UTF16(const String& number, const void* text, size_t len) {
  474. sendAT(GF("+CMGF=1"));
  475. waitResponse();
  476. sendAT(GF("+CSCS=\"HEX\""));
  477. waitResponse();
  478. sendAT(GF("+CSMP=17,167,0,8"));
  479. waitResponse();
  480. sendAT(GF("+CMGS=\""), number, GF("\""));
  481. if (waitResponse(GF(">")) != 1) {
  482. return false;
  483. }
  484. uint16_t* t = (uint16_t*)text;
  485. for (size_t i=0; i<len; i++) {
  486. uint8_t c = t[i] >> 8;
  487. if (c < 0x10) { stream.print('0'); }
  488. stream.print(c, HEX);
  489. c = t[i] & 0xFF;
  490. if (c < 0x10) { stream.print('0'); }
  491. stream.print(c, HEX);
  492. }
  493. stream.write((char)0x1A);
  494. stream.flush();
  495. return waitResponse(60000L) == 1;
  496. }
  497. /*
  498. * Location functions
  499. */
  500. String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
  501. /*
  502. * Battery functions
  503. */
  504. // Use: float vBatt = modem.getBattVoltage() / 1000.0;
  505. uint16_t getBattVoltage() {
  506. sendAT(GF("+CBC"));
  507. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  508. return 0;
  509. }
  510. streamSkipUntil(','); // Skip
  511. streamSkipUntil(','); // Skip
  512. uint16_t res = stream.readStringUntil(',').toInt();
  513. waitResponse();
  514. return res;
  515. }
  516. int8_t getBattPercent() {
  517. sendAT(GF("+CBC"));
  518. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  519. return false;
  520. }
  521. stream.readStringUntil(',');
  522. int res = stream.readStringUntil(',').toInt();
  523. waitResponse();
  524. return res;
  525. }
  526. /*
  527. * Client related functions
  528. */
  529. protected:
  530. bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
  531. int rsp;
  532. // <PDPcontextID>(1-16), <connectID>(0-11),"TCP/UDP/TCP LISTENER/UDP SERVICE",
  533. // "<IP_address>/<domain_name>",<remote_port>,<local_port>,<access_mode>(0-2 0=buffer)
  534. sendAT(GF("+QIOPEN=1,"), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(",0,0"));
  535. rsp = waitResponse();
  536. if (waitResponse(20000L, GF(GSM_NL "+QIOPEN:")) != 1) {
  537. return false;
  538. }
  539. if (stream.readStringUntil(',').toInt() != mux) {
  540. return false;
  541. }
  542. // Read status
  543. rsp = stream.readStringUntil('\n').toInt();
  544. return (0 == rsp);
  545. }
  546. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  547. sendAT(GF("+QISEND="), mux, ',', len);
  548. if (waitResponse(GF(">")) != 1) {
  549. return 0;
  550. }
  551. stream.write((uint8_t*)buff, len);
  552. stream.flush();
  553. if (waitResponse(GF(GSM_NL "SEND OK")) != 1) {
  554. return 0;
  555. }
  556. // TODO: Wait for ACK? AT+QISEND=id,0
  557. maintain(); // look for a very quick response from the remote
  558. return len;
  559. }
  560. size_t modemRead(size_t size, uint8_t mux) {
  561. sendAT(GF("+QIRD="), mux, ',', size);
  562. if (waitResponse(GF("+QIRD:")) != 1) {
  563. return 0;
  564. }
  565. size_t len = stream.readStringUntil('\n').toInt();
  566. sockets[mux]->sock_available = len;
  567. for (size_t i=0; i<len; i++) {
  568. while (!stream.available()) { TINY_GSM_YIELD(); }
  569. char c = stream.read();
  570. sockets[mux]->rx.put(c);
  571. }
  572. waitResponse();
  573. DBG("### READ:", len, "from", mux);
  574. maintain(); // Listen for a close or other URC
  575. return len;
  576. }
  577. size_t modemGetAvailable(uint8_t mux) {
  578. sendAT(GF("+QIRD="), mux, GF(",0"));
  579. size_t result = 0;
  580. if (waitResponse(GF("+QIRD:")) == 1) {
  581. streamSkipUntil(','); // Skip total received
  582. streamSkipUntil(','); // Skip have read
  583. result = stream.readStringUntil('\n').toInt();
  584. if (result) DBG("### DATA AVAILABLE:", result, "on", mux);
  585. waitResponse();
  586. }
  587. if (!result) {
  588. sockets[mux]->sock_connected = modemGetConnected(mux);
  589. }
  590. maintain(); // Listen for a close or other URC
  591. return result;
  592. }
  593. bool modemGetConnected(uint8_t mux) {
  594. sendAT(GF("+QISTATE=1,"), mux);
  595. //+QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1"
  596. if (waitResponse(GF("+QISTATE:")))
  597. return false;
  598. streamSkipUntil(','); // Skip mux
  599. streamSkipUntil(','); // Skip socket type
  600. streamSkipUntil(','); // Skip remote ip
  601. streamSkipUntil(','); // Skip remote port
  602. streamSkipUntil(','); // Skip local port
  603. int res = stream.readStringUntil(',').toInt(); // socket state
  604. waitResponse();
  605. maintain(); // Listen for a close or other URC
  606. // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
  607. return 2 == res;
  608. }
  609. public:
  610. /*
  611. Utilities
  612. */
  613. template<typename T>
  614. void streamWrite(T last) {
  615. stream.print(last);
  616. }
  617. template<typename T, typename... Args>
  618. void streamWrite(T head, Args... tail) {
  619. stream.print(head);
  620. streamWrite(tail...);
  621. }
  622. template<typename... Args>
  623. void sendAT(Args... cmd) {
  624. streamWrite("AT", cmd..., GSM_NL);
  625. stream.flush();
  626. TINY_GSM_YIELD();
  627. //DBG("### AT:", cmd...);
  628. }
  629. bool streamSkipUntil(const char c, const unsigned long timeout = 1000L) {
  630. unsigned long startMillis = millis();
  631. while (millis() - startMillis < timeout) {
  632. while (millis() - startMillis < timeout && !stream.available()) {
  633. TINY_GSM_YIELD();
  634. }
  635. if (stream.read() == c) {
  636. return true;
  637. }
  638. }
  639. return false;
  640. }
  641. // TODO: Optimize this!
  642. uint8_t waitResponse(uint32_t timeout, String& data,
  643. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  644. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  645. {
  646. /*String r1s(r1); r1s.trim();
  647. String r2s(r2); r2s.trim();
  648. String r3s(r3); r3s.trim();
  649. String r4s(r4); r4s.trim();
  650. String r5s(r5); r5s.trim();
  651. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  652. data.reserve(64);
  653. int index = 0;
  654. unsigned long startMillis = millis();
  655. do {
  656. TINY_GSM_YIELD();
  657. while (stream.available() > 0) {
  658. int a = stream.read();
  659. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  660. data += (char)a;
  661. if (r1 && data.endsWith(r1)) {
  662. index = 1;
  663. goto finish;
  664. } else if (r2 && data.endsWith(r2)) {
  665. index = 2;
  666. goto finish;
  667. } else if (r3 && data.endsWith(r3)) {
  668. index = 3;
  669. goto finish;
  670. } else if (r4 && data.endsWith(r4)) {
  671. index = 4;
  672. goto finish;
  673. } else if (r5 && data.endsWith(r5)) {
  674. index = 5;
  675. goto finish;
  676. } else if (data.endsWith(GF(GSM_NL "+QIURC:"))) {
  677. stream.readStringUntil('\"');
  678. String urc = stream.readStringUntil('\"');
  679. stream.readStringUntil(',');
  680. if (urc == "recv") {
  681. int mux = stream.readStringUntil('\n').toInt();
  682. DBG("### URC RECV:", mux);
  683. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  684. sockets[mux]->got_data = true;
  685. }
  686. } else if (urc == "closed") {
  687. int mux = stream.readStringUntil('\n').toInt();
  688. DBG("### URC CLOSE:", mux);
  689. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  690. sockets[mux]->sock_connected = false;
  691. }
  692. } else {
  693. stream.readStringUntil('\n');
  694. }
  695. data = "";
  696. }
  697. }
  698. } while (millis() - startMillis < timeout);
  699. finish:
  700. if (!index) {
  701. data.trim();
  702. if (data.length()) {
  703. DBG("### Unhandled:", data);
  704. }
  705. data = "";
  706. }
  707. //DBG('<', index, '>');
  708. return index;
  709. }
  710. uint8_t waitResponse(uint32_t timeout,
  711. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  712. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  713. {
  714. String data;
  715. return waitResponse(timeout, data, r1, r2, r3, r4, r5);
  716. }
  717. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  718. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  719. {
  720. return waitResponse(1000, r1, r2, r3, r4, r5);
  721. }
  722. public:
  723. Stream& stream;
  724. protected:
  725. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  726. };
  727. #endif