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.

729 lines
22 KiB

5 years ago
5 years ago
5 years ago
  1. /**
  2. * @file TinyGsmClientSaraR4.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef SRC_TINYGSMCLIENTSARAR4_H_
  9. #define SRC_TINYGSMCLIENTSARAR4_H_
  10. // #pragma message("TinyGSM: TinyGsmClientSaraR4")
  11. // #define TINY_GSM_DEBUG Serial
  12. #define TINY_GSM_MUX_COUNT 7
  13. #include "TinyGsmCommon.h"
  14. #define GSM_NL "\r\n"
  15. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  16. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  17. static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:";
  18. enum RegStatus {
  19. REG_NO_RESULT = -1,
  20. REG_UNREGISTERED = 0,
  21. REG_SEARCHING = 2,
  22. REG_DENIED = 3,
  23. REG_OK_HOME = 1,
  24. REG_OK_ROAMING = 5,
  25. REG_UNKNOWN = 4,
  26. };
  27. class TinyGsmSaraR4 : public TinyGsmModem<TinyGsmSaraR4, READ_AND_CHECK_SIZE,
  28. TINY_GSM_MUX_COUNT> {
  29. friend class TinyGsmModem<TinyGsmSaraR4, READ_AND_CHECK_SIZE,
  30. TINY_GSM_MUX_COUNT>;
  31. /*
  32. * Inner Client
  33. */
  34. public:
  35. class GsmClientSaraR4 : public GsmClient {
  36. friend class TinyGsmSaraR4;
  37. public:
  38. GsmClientSaraR4() {}
  39. explicit GsmClientSaraR4(TinyGsmSaraR4& modem, uint8_t mux = 0) {
  40. init(&modem, mux);
  41. }
  42. bool init(TinyGsmSaraR4* modem, uint8_t mux = 0) {
  43. this->at = modem;
  44. this->mux = mux;
  45. sock_available = 0;
  46. prev_check = 0;
  47. sock_connected = false;
  48. got_data = false;
  49. at->sockets[mux] = this;
  50. return true;
  51. }
  52. public:
  53. int connect(const char* host, uint16_t port, int timeout_s) {
  54. stop();
  55. TINY_GSM_YIELD();
  56. rx.clear();
  57. uint8_t oldMux = mux;
  58. sock_connected = at->modemConnect(host, port, &mux, false, timeout_s);
  59. if (mux != oldMux) {
  60. DBG("WARNING: Mux number changed from", oldMux, "to", mux);
  61. at->sockets[oldMux] = NULL;
  62. }
  63. at->sockets[mux] = this;
  64. at->maintain();
  65. return sock_connected;
  66. }
  67. int connect(IPAddress ip, uint16_t port, int timeout_s) {
  68. return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s);
  69. }
  70. int connect(const char* host, uint16_t port) override {
  71. return connect(host, port, 120);
  72. }
  73. int connect(IPAddress ip, uint16_t port) override {
  74. return connect(ip, port, 120);
  75. }
  76. void stop(uint32_t maxWaitMs) {
  77. uint32_t startMillis = millis();
  78. dumpModemBuffer(maxWaitMs);
  79. // We want to use an async socket close because the syncrhonous close of
  80. // an open socket is INCREDIBLY SLOW and the modem can freeze up. But we
  81. // only attempt the async close if we already KNOW the socket is open
  82. // because calling the async close on a closed socket and then attempting
  83. // opening a new socket causes the board to lock up for 2-3 minutes and
  84. // then finally return with a "new" socket that is immediately closed.
  85. // Attempting to close a socket that is already closed with a synchronous
  86. // close quickly returns an error.
  87. if (at->supportsAsyncSockets && sock_connected) {
  88. DBG("### Closing socket asynchronously! Socket might remain open "
  89. "until arrival of +UUSOCL:",
  90. mux);
  91. // faster asynchronous close
  92. // NOT supported on SARA-R404M / SARA-R410M-01B
  93. at->sendAT(GF("+USOCL="), mux, GF(",1"));
  94. // NOTE: can take up to 120s to get a response
  95. at->waitResponse((maxWaitMs - (millis() - startMillis)));
  96. // We set the sock as disconnected right away because it can no longer
  97. // be used
  98. sock_connected = false;
  99. } else {
  100. // synchronous close
  101. at->sendAT(GF("+USOCL="), mux);
  102. // NOTE: can take up to 120s to get a response
  103. at->waitResponse((maxWaitMs - (millis() - startMillis)));
  104. sock_connected = false;
  105. }
  106. }
  107. void stop() override {
  108. stop(135000L);
  109. }
  110. /*
  111. * Extended API
  112. */
  113. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  114. };
  115. /*
  116. * Inner Secure Client
  117. */
  118. public:
  119. class GsmClientSecureR4 : public GsmClientSaraR4 {
  120. public:
  121. GsmClientSecureR4() {}
  122. explicit GsmClientSecureR4(TinyGsmSaraR4& modem, uint8_t mux = 1)
  123. : GsmClientSaraR4(modem, mux) {}
  124. public:
  125. int connect(const char* host, uint16_t port, int timeout_s) {
  126. stop();
  127. TINY_GSM_YIELD();
  128. rx.clear();
  129. uint8_t oldMux = mux;
  130. sock_connected = at->modemConnect(host, port, &mux, true, timeout_s);
  131. if (mux != oldMux) {
  132. DBG("WARNING: Mux number changed from", oldMux, "to", mux);
  133. at->sockets[oldMux] = NULL;
  134. }
  135. at->sockets[mux] = this;
  136. at->maintain();
  137. return sock_connected;
  138. }
  139. };
  140. /*
  141. * Constructor
  142. */
  143. public:
  144. explicit TinyGsmSaraR4(Stream& stream)
  145. : stream(stream),
  146. has2GFallback(false),
  147. supportsAsyncSockets(false) {
  148. memset(sockets, 0, sizeof(sockets));
  149. }
  150. /*
  151. * Basic functions
  152. */
  153. protected:
  154. bool initImpl(const char* pin = NULL) {
  155. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  156. if (!testAT()) { return false; }
  157. sendAT(GF("E0")); // Echo Off
  158. if (waitResponse() != 1) { return false; }
  159. #ifdef TINY_GSM_DEBUG
  160. sendAT(GF("+CMEE=2")); // turn on verbose error codes
  161. #else
  162. sendAT(GF("+CMEE=0")); // turn off error codes
  163. #endif
  164. waitResponse();
  165. String modemName = getModemName();
  166. DBG(GF("### Modem:"), modemName);
  167. if (modemName.startsWith("u-blox SARA-R412")) {
  168. has2GFallback = true;
  169. } else {
  170. has2GFallback = false;
  171. }
  172. if (modemName.startsWith("u-blox SARA-R404M") ||
  173. modemName.startsWith("u-blox SARA-R410M-01B")) {
  174. supportsAsyncSockets = false;
  175. } else {
  176. supportsAsyncSockets = true;
  177. }
  178. // Enable automatic time zome update
  179. sendAT(GF("+CTZU=1"));
  180. if (waitResponse(10000L) != 1) { return false; }
  181. int ret = getSimStatus();
  182. // if the sim isn't ready and a pin has been provided, try to unlock the sim
  183. if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
  184. simUnlock(pin);
  185. return (getSimStatus() == SIM_READY);
  186. } else {
  187. // if the sim is ready, or it's locked but no pin has been provided,
  188. // return true
  189. return (ret == SIM_READY || ret == SIM_LOCKED);
  190. }
  191. }
  192. // only difference in implementation is the warning on the wrong type
  193. String getModemNameImpl() {
  194. sendAT(GF("+CGMI"));
  195. String res1;
  196. if (waitResponse(1000L, res1) != 1) { return "u-blox Cellular Modem"; }
  197. res1.replace(GSM_NL "OK" GSM_NL, "");
  198. res1.trim();
  199. sendAT(GF("+GMM"));
  200. String res2;
  201. if (waitResponse(1000L, res2) != 1) { return "u-blox Cellular Modem"; }
  202. res2.replace(GSM_NL "OK" GSM_NL, "");
  203. res2.trim();
  204. String name = res1 + String(' ') + res2;
  205. DBG("### Modem:", name);
  206. if (!name.startsWith("u-blox SARA-R4") &&
  207. !name.startsWith("u-blox SARA-N4")) {
  208. DBG("### WARNING: You are using the wrong TinyGSM modem!");
  209. }
  210. return name;
  211. }
  212. bool factoryDefaultImpl() {
  213. sendAT(GF("&F")); // Resets the current profile, other NVM not affected
  214. return waitResponse() == 1;
  215. }
  216. bool thisHasSSL() {
  217. return true;
  218. }
  219. bool thisHasWifi() {
  220. return false;
  221. }
  222. bool thisHasGPRS() {
  223. return true;
  224. }
  225. /*
  226. * Power functions
  227. */
  228. protected:
  229. // using +CFUN=15 instead of the more common CFUN=1,1
  230. bool restartImpl() {
  231. if (!testAT()) { return false; }
  232. sendAT(GF("+CFUN=15"));
  233. if (waitResponse(10000L) != 1) { return false; }
  234. delay(3000); // TODO(?): Verify delay timing here
  235. return init();
  236. }
  237. bool powerOffImpl() {
  238. sendAT(GF("+CPWROFF"));
  239. return waitResponse(40000L) == 1;
  240. }
  241. bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE;
  242. /*
  243. * SIM card functions
  244. */
  245. protected:
  246. // This uses "CGSN" instead of "GSN"
  247. String getIMEIImpl() {
  248. sendAT(GF("+CGSN"));
  249. if (waitResponse(GF(GSM_NL)) != 1) { return ""; }
  250. String res = stream.readStringUntil('\n');
  251. waitResponse();
  252. res.trim();
  253. return res;
  254. }
  255. /*
  256. * Generic network functions
  257. */
  258. public:
  259. RegStatus getRegistrationStatus() {
  260. // Check first for EPS registration
  261. sendAT(GF("+CEREG?"));
  262. if (waitResponse(GF(GSM_NL "+CEREG:")) != 1) { return REG_UNKNOWN; }
  263. streamSkipUntil(','); /* Skip format (0) */
  264. int status = stream.readStringUntil('\n').toInt();
  265. waitResponse();
  266. // If we're connected on EPS, great!
  267. if ((RegStatus)status == REG_OK_HOME ||
  268. (RegStatus)status == REG_OK_ROAMING) {
  269. return (RegStatus)status;
  270. } else {
  271. // Otherwise, check generic network status
  272. sendAT(GF("+CREG?"));
  273. if (waitResponse(GF(GSM_NL "+CREG:")) != 1) { return REG_UNKNOWN; }
  274. streamSkipUntil(','); /* Skip format (0) */
  275. status = stream.readStringUntil('\n').toInt();
  276. waitResponse();
  277. return (RegStatus)status;
  278. }
  279. }
  280. protected:
  281. bool isNetworkConnectedImpl() {
  282. RegStatus s = getRegistrationStatus();
  283. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  284. }
  285. public:
  286. bool setURAT(uint8_t urat) {
  287. // AT+URAT=<SelectedAcT>[,<PreferredAct>[,<2ndPreferredAct>]]
  288. sendAT(GF("+COPS=2")); // Deregister from network
  289. if (waitResponse() != 1) { return false; }
  290. sendAT(GF("+URAT="), urat); // Radio Access Technology (RAT) selection
  291. if (waitResponse() != 1) { return false; }
  292. sendAT(GF("+COPS=0")); // Auto-register to the network
  293. if (waitResponse() != 1) { return false; }
  294. return restart();
  295. }
  296. /*
  297. * GPRS functions
  298. */
  299. protected:
  300. bool gprsConnectImpl(const char* apn, const char* user = NULL,
  301. const char* pwd = NULL) {
  302. // gprsDisconnect();
  303. sendAT(GF("+CGATT=1")); // attach to GPRS
  304. if (waitResponse(360000L) != 1) { return false; }
  305. // Using CGDCONT sets up an "external" PCP context, i.e. a data connection
  306. // using the external IP stack (e.g. Windows dial up) and PPP link over the
  307. // serial interface. This is the only command set supported by the LTE-M
  308. // and LTE NB-IoT modules (SARA-R4xx, SARA-N4xx)
  309. // Set the authentication
  310. if (user && strlen(user) > 0) {
  311. sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"');
  312. waitResponse();
  313. }
  314. sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"'); // Define PDP context 1
  315. waitResponse();
  316. sendAT(GF("+CGACT=1,1")); // activate PDP profile/context 1
  317. if (waitResponse(150000L) != 1) { return false; }
  318. return true;
  319. }
  320. bool gprsDisconnectImpl() {
  321. // Mark all the sockets as closed
  322. // This ensures that asynchronously closed sockets are marked closed
  323. for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
  324. GsmClientSaraR4* sock = sockets[mux];
  325. if (sock && sock->sock_connected) { sock->sock_connected = false; }
  326. }
  327. // sendAT(GF("+CGACT=0,1")); // Deactivate PDP context 1
  328. sendAT(GF("+CGACT=0")); // Deactivate all contexts
  329. if (waitResponse(40000L) != 1) {
  330. // return false;
  331. }
  332. sendAT(GF("+CGATT=0")); // detach from GPRS
  333. if (waitResponse(360000L) != 1) { return false; }
  334. return true;
  335. }
  336. /*
  337. * IP Address functions
  338. */
  339. protected:
  340. // Can follow the template in all function
  341. /*
  342. * Phone Call functions
  343. */
  344. protected:
  345. // While the AT commands for call answer and hang-up are nominally supported,
  346. // no voice calls are supported rendering them meaningless
  347. bool callAnswerImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
  348. bool callNumberImpl(const String& number) TINY_GSM_ATTR_NOT_AVAILABLE;
  349. bool callHangupImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
  350. bool dtmfSendImpl(char cmd,
  351. int duration_ms = 100) TINY_GSM_ATTR_NOT_AVAILABLE;
  352. /*
  353. * Messaging functions
  354. */
  355. protected:
  356. String sendUSSDImpl(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  357. bool sendSMS_UTF16Impl(const String& number, const void* text,
  358. size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  359. /*
  360. * Location functions
  361. */
  362. protected:
  363. String getGsmLocationImpl() {
  364. sendAT(GF("+ULOC=2,3,0,120,1"));
  365. if (waitResponse(30000L, GF(GSM_NL "+UULOC:")) != 1) { return ""; }
  366. String res = stream.readStringUntil('\n');
  367. waitResponse();
  368. res.trim();
  369. return res;
  370. }
  371. /*
  372. * GPS location functions
  373. */
  374. public:
  375. // No functions of this type supported
  376. /*
  377. * Time functions
  378. */
  379. protected:
  380. // Can follow the standard CCLK function in the template
  381. /*
  382. * Battery & temperature functions
  383. */
  384. protected:
  385. uint16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
  386. int8_t getBattPercentImpl() {
  387. sendAT(GF("+CIND?"));
  388. if (waitResponse(GF(GSM_NL "+CIND:")) != 1) { return 0; }
  389. int res = stream.readStringUntil(',').toInt();
  390. int8_t percent = res * 20; // return is 0-5
  391. // Wait for final OK
  392. waitResponse();
  393. return percent;
  394. }
  395. uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
  396. bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent,
  397. uint16_t& milliVolts) {
  398. chargeState = 0;
  399. percent = getBattPercent();
  400. milliVolts = 0;
  401. return true;
  402. }
  403. float getTemperatureImpl() {
  404. // First make sure the temperature is set to be in celsius
  405. sendAT(GF("+UTEMP=0")); // Would use 1 for Fahrenheit
  406. if (waitResponse() != 1) { return static_cast<float>(-9999); }
  407. sendAT(GF("+UTEMP?"));
  408. if (waitResponse(GF(GSM_NL "+UTEMP:")) != 1) {
  409. return static_cast<float>(-9999);
  410. }
  411. int16_t res = stream.readStringUntil('\n').toInt();
  412. float temp = -9999;
  413. if (res != -1) { temp = (static_cast<float>(res)) / 10; }
  414. return temp;
  415. }
  416. /*
  417. * Client related functions
  418. */
  419. protected:
  420. bool modemConnect(const char* host, uint16_t port, uint8_t* mux,
  421. bool ssl = false, int timeout_s = 120) {
  422. uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
  423. uint32_t startMillis = millis();
  424. // create a socket
  425. sendAT(GF("+USOCR=6"));
  426. // reply is +USOCR: ## of socket created
  427. if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) { return false; }
  428. *mux = stream.readStringUntil('\n').toInt();
  429. waitResponse();
  430. if (ssl) {
  431. sendAT(GF("+USOSEC="), *mux, ",1");
  432. waitResponse();
  433. }
  434. // Enable NODELAY
  435. // AT+USOSO=<socket>,<level>,<opt_name>,<opt_val>[,<opt_val2>]
  436. // <level> - 0 for IP, 6 for TCP, 65535 for socket level options
  437. // <opt_name> TCP/1 = no delay (do not delay send to coalesce packets)
  438. // NOTE: Enabling this may increase data plan usage
  439. // sendAT(GF("+USOSO="), *mux, GF(",6,1,1"));
  440. // waitResponse();
  441. // Enable KEEPALIVE, 30 sec
  442. // sendAT(GF("+USOSO="), *mux, GF(",6,2,30000"));
  443. // waitResponse();
  444. // connect on the allocated socket
  445. // Use an asynchronous open to reduce the number of terminal freeze-ups
  446. // This is still blocking until the URC arrives
  447. // The SARA-R410M-02B with firmware revisions prior to L0.0.00.00.05.08
  448. // has a nasty habit of locking up when opening a socket, especially if
  449. // the cellular service is poor.
  450. // NOT supported on SARA-R404M / SARA-R410M-01B
  451. if (supportsAsyncSockets) {
  452. DBG("### Opening socket asynchronously! Socket cannot be used until "
  453. "the URC '+UUSOCO' appears.");
  454. sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port, ",1");
  455. if (waitResponse(timeout_ms - (millis() - startMillis),
  456. GF(GSM_NL "+UUSOCO:")) == 1) {
  457. stream.readStringUntil(',').toInt(); // skip repeated mux
  458. int connection_status = stream.readStringUntil('\n').toInt();
  459. DBG("### Waited", millis() - startMillis, "ms for socket to open");
  460. return (0 == connection_status);
  461. } else {
  462. DBG("### Waited", millis() - startMillis,
  463. "but never got socket open notice");
  464. return false;
  465. }
  466. } else {
  467. // use synchronous open
  468. sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port);
  469. int rsp = waitResponse(timeout_ms - (millis() - startMillis));
  470. return (1 == rsp);
  471. }
  472. }
  473. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  474. sendAT(GF("+USOWR="), mux, ',', (uint16_t)len);
  475. if (waitResponse(GF("@")) != 1) { return 0; }
  476. // 50ms delay, see AT manual section 25.10.4
  477. delay(50);
  478. stream.write(reinterpret_cast<const uint8_t*>(buff), len);
  479. stream.flush();
  480. if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) { return 0; }
  481. streamSkipUntil(','); // Skip mux
  482. int sent = stream.readStringUntil('\n').toInt();
  483. waitResponse(); // sends back OK after the confirmation of number sent
  484. return sent;
  485. }
  486. size_t modemRead(size_t size, uint8_t mux) {
  487. sendAT(GF("+USORD="), mux, ',', (uint16_t)size);
  488. if (waitResponse(GF(GSM_NL "+USORD:")) != 1) { return 0; }
  489. streamSkipUntil(','); // Skip mux
  490. int len = stream.readStringUntil(',').toInt();
  491. streamSkipUntil('\"');
  492. for (int i = 0; i < len; i++) { moveCharFromStreamToFifo(mux); }
  493. streamSkipUntil('\"');
  494. waitResponse();
  495. DBG("### READ:", len, "from", mux);
  496. sockets[mux]->sock_available = modemGetAvailable(mux);
  497. return len;
  498. }
  499. size_t modemGetAvailable(uint8_t mux) {
  500. // NOTE: Querying a closed socket gives an error "operation not allowed"
  501. sendAT(GF("+USORD="), mux, ",0");
  502. size_t result = 0;
  503. uint8_t res = waitResponse(GF(GSM_NL "+USORD:"));
  504. // Will give error "operation not allowed" when attempting to read a socket
  505. // that you have already told to close
  506. if (res == 1) {
  507. streamSkipUntil(','); // Skip mux
  508. result = stream.readStringUntil('\n').toInt();
  509. // if (result) DBG("### DATA AVAILABLE:", result, "on", mux);
  510. waitResponse();
  511. }
  512. if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
  513. DBG("### AVAILABLE:", result, "on", mux);
  514. return result;
  515. }
  516. bool modemGetConnected(uint8_t mux) {
  517. // NOTE: Querying a closed socket gives an error "operation not allowed"
  518. sendAT(GF("+USOCTL="), mux, ",10");
  519. uint8_t res = waitResponse(GF(GSM_NL "+USOCTL:"));
  520. if (res != 1) { return false; }
  521. streamSkipUntil(','); // Skip mux
  522. streamSkipUntil(','); // Skip type
  523. int result = stream.readStringUntil('\n').toInt();
  524. // 0: the socket is in INACTIVE status (it corresponds to CLOSED status
  525. // defined in RFC793 "TCP Protocol Specification" [112])
  526. // 1: the socket is in LISTEN status
  527. // 2: the socket is in SYN_SENT status
  528. // 3: the socket is in SYN_RCVD status
  529. // 4: the socket is in ESTABILISHED status
  530. // 5: the socket is in FIN_WAIT_1 status
  531. // 6: the socket is in FIN_WAIT_2 status
  532. // 7: the sokcet is in CLOSE_WAIT status
  533. // 8: the socket is in CLOSING status
  534. // 9: the socket is in LAST_ACK status
  535. // 10: the socket is in TIME_WAIT status
  536. waitResponse();
  537. return (result != 0);
  538. }
  539. /*
  540. * Utilities
  541. */
  542. public:
  543. // TODO(vshymanskyy): Optimize this!
  544. uint8_t waitResponse(uint32_t timeout_ms, String& data,
  545. GsmConstStr r1 = GFP(GSM_OK),
  546. GsmConstStr r2 = GFP(GSM_ERROR),
  547. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  548. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  549. /*String r1s(r1); r1s.trim();
  550. String r2s(r2); r2s.trim();
  551. String r3s(r3); r3s.trim();
  552. String r4s(r4); r4s.trim();
  553. String r5s(r5); r5s.trim();
  554. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  555. data.reserve(64);
  556. uint8_t index = 0;
  557. uint32_t startMillis = millis();
  558. do {
  559. TINY_GSM_YIELD();
  560. while (stream.available() > 0) {
  561. TINY_GSM_YIELD();
  562. int a = stream.read();
  563. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  564. data += static_cast<char>(a);
  565. if (r1 && data.endsWith(r1)) {
  566. index = 1;
  567. goto finish;
  568. } else if (r2 && data.endsWith(r2)) {
  569. index = 2;
  570. goto finish;
  571. } else if (r3 && data.endsWith(r3)) {
  572. index = 3;
  573. if (r3 == GFP(GSM_CME_ERROR)) {
  574. streamSkipUntil('\n'); // Read out the error
  575. }
  576. goto finish;
  577. } else if (r4 && data.endsWith(r4)) {
  578. index = 4;
  579. goto finish;
  580. } else if (r5 && data.endsWith(r5)) {
  581. index = 5;
  582. goto finish;
  583. } else if (data.endsWith(GF("+UUSORD:"))) {
  584. int mux = stream.readStringUntil(',').toInt();
  585. int len = stream.readStringUntil('\n').toInt();
  586. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  587. sockets[mux]->got_data = true;
  588. sockets[mux]->sock_available = len;
  589. }
  590. data = "";
  591. DBG("### URC Data Received:", len, "on", mux);
  592. } else if (data.endsWith(GF("+UUSOCL:"))) {
  593. int mux = stream.readStringUntil('\n').toInt();
  594. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  595. sockets[mux]->sock_connected = false;
  596. }
  597. data = "";
  598. DBG("### URC Sock Closed: ", mux);
  599. } else if (data.endsWith(GF("+UUSOCO:"))) {
  600. int mux = stream.readStringUntil('\n').toInt();
  601. int socket_error = stream.readStringUntil('\n').toInt();
  602. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux] &&
  603. socket_error == 0) {
  604. sockets[mux]->sock_connected = true;
  605. }
  606. data = "";
  607. DBG("### URC Sock Opened: ", mux);
  608. }
  609. }
  610. } while (millis() - startMillis < timeout_ms);
  611. finish:
  612. if (!index) {
  613. data.trim();
  614. if (data.length()) { DBG("### Unhandled:", data); }
  615. data = "";
  616. }
  617. // data.replace(GSM_NL, "/");
  618. // DBG('<', index, '>', data);
  619. return index;
  620. }
  621. uint8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
  622. GsmConstStr r2 = GFP(GSM_ERROR),
  623. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  624. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  625. String data;
  626. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  627. }
  628. uint8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
  629. GsmConstStr r2 = GFP(GSM_ERROR),
  630. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  631. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  632. return waitResponse(1000, r1, r2, r3, r4, r5);
  633. }
  634. protected:
  635. Stream& stream;
  636. GsmClientSaraR4* sockets[TINY_GSM_MUX_COUNT];
  637. const char* gsmNL = GSM_NL;
  638. bool has2GFallback;
  639. bool supportsAsyncSockets;
  640. };
  641. #endif // SRC_TINYGSMCLIENTSARAR4_H_