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.

703 lines
20 KiB

6 years ago
5 years ago
4 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 SRC_TINYGSMCLIENTBG96_H_
  9. #define SRC_TINYGSMCLIENTBG96_H_
  10. // #pragma message("TinyGSM: TinyGsmClientBG96")
  11. // #define TINY_GSM_DEBUG Serial
  12. #define TINY_GSM_MUX_COUNT 12
  13. #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
  14. #include "TinyGsmBattery.tpp"
  15. #include "TinyGsmCalling.tpp"
  16. #include "TinyGsmGPRS.tpp"
  17. #include "TinyGsmGPS.tpp"
  18. #include "TinyGsmModem.tpp"
  19. #include "TinyGsmSMS.tpp"
  20. #include "TinyGsmTCP.tpp"
  21. #include "TinyGsmTemperature.tpp"
  22. #include "TinyGsmTime.tpp"
  23. #define GSM_NL "\r\n"
  24. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  25. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  26. #if defined TINY_GSM_DEBUG
  27. static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:";
  28. static const char GSM_CMS_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CMS ERROR:";
  29. #endif
  30. enum RegStatus {
  31. REG_NO_RESULT = -1,
  32. REG_UNREGISTERED = 0,
  33. REG_SEARCHING = 2,
  34. REG_DENIED = 3,
  35. REG_OK_HOME = 1,
  36. REG_OK_ROAMING = 5,
  37. REG_UNKNOWN = 4,
  38. };
  39. class TinyGsmBG96 : public TinyGsmModem<TinyGsmBG96>,
  40. public TinyGsmGPRS<TinyGsmBG96>,
  41. public TinyGsmTCP<TinyGsmBG96, TINY_GSM_MUX_COUNT>,
  42. public TinyGsmCalling<TinyGsmBG96>,
  43. public TinyGsmSMS<TinyGsmBG96>,
  44. public TinyGsmTime<TinyGsmBG96>,
  45. public TinyGsmGPS<TinyGsmBG96>,
  46. public TinyGsmBattery<TinyGsmBG96>,
  47. public TinyGsmTemperature<TinyGsmBG96> {
  48. friend class TinyGsmModem<TinyGsmBG96>;
  49. friend class TinyGsmGPRS<TinyGsmBG96>;
  50. friend class TinyGsmTCP<TinyGsmBG96, TINY_GSM_MUX_COUNT>;
  51. friend class TinyGsmCalling<TinyGsmBG96>;
  52. friend class TinyGsmSMS<TinyGsmBG96>;
  53. friend class TinyGsmTime<TinyGsmBG96>;
  54. friend class TinyGsmGPS<TinyGsmBG96>;
  55. friend class TinyGsmBattery<TinyGsmBG96>;
  56. friend class TinyGsmTemperature<TinyGsmBG96>;
  57. /*
  58. * Inner Client
  59. */
  60. public:
  61. class GsmClientBG96 : public GsmClient {
  62. friend class TinyGsmBG96;
  63. public:
  64. GsmClientBG96() {}
  65. explicit GsmClientBG96(TinyGsmBG96& modem, uint8_t mux = 0) {
  66. init(&modem, mux);
  67. }
  68. bool init(TinyGsmBG96* modem, uint8_t mux = 0) {
  69. this->at = modem;
  70. sock_available = 0;
  71. prev_check = 0;
  72. sock_connected = false;
  73. got_data = false;
  74. if (mux < TINY_GSM_MUX_COUNT) {
  75. this->mux = mux;
  76. } else {
  77. this->mux = (mux % TINY_GSM_MUX_COUNT);
  78. }
  79. at->sockets[this->mux] = this;
  80. return true;
  81. }
  82. public:
  83. virtual int connect(const char* host, uint16_t port, int timeout_s) {
  84. stop();
  85. TINY_GSM_YIELD();
  86. rx.clear();
  87. sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
  88. return sock_connected;
  89. }
  90. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  91. void stop(uint32_t maxWaitMs) {
  92. uint32_t startMillis = millis();
  93. dumpModemBuffer(maxWaitMs);
  94. at->sendAT(GF("+QICLOSE="), mux);
  95. sock_connected = false;
  96. at->waitResponse((maxWaitMs - (millis() - startMillis)));
  97. }
  98. void stop() override {
  99. stop(15000L);
  100. }
  101. /*
  102. * Extended API
  103. */
  104. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  105. };
  106. /*
  107. * Inner Secure Client
  108. */
  109. /*
  110. class GsmClientSecureBG96 : public GsmClientBG96
  111. {
  112. public:
  113. GsmClientSecure() {}
  114. GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 0)
  115. : public GsmClient(modem, mux)
  116. {}
  117. public:
  118. int connect(const char* host, uint16_t port, int timeout_s) override {
  119. stop();
  120. TINY_GSM_YIELD();
  121. rx.clear();
  122. sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
  123. return sock_connected;
  124. }
  125. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  126. };
  127. */
  128. /*
  129. * Constructor
  130. */
  131. public:
  132. explicit TinyGsmBG96(Stream& stream) : stream(stream) {
  133. memset(sockets, 0, sizeof(sockets));
  134. }
  135. /*
  136. * Basic functions
  137. */
  138. protected:
  139. bool initImpl(const char* pin = NULL) {
  140. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  141. DBG(GF("### TinyGSM Compiled Module: TinyGsmClientBG96"));
  142. if (!testAT()) { return false; }
  143. sendAT(GF("E0")); // Echo Off
  144. if (waitResponse() != 1) { return false; }
  145. #ifdef TINY_GSM_DEBUG
  146. sendAT(GF("+CMEE=2")); // turn on verbose error codes
  147. #else
  148. sendAT(GF("+CMEE=0")); // turn off error codes
  149. #endif
  150. waitResponse();
  151. DBG(GF("### Modem:"), getModemName());
  152. // Disable time and time zone URC's
  153. sendAT(GF("+CTZR=0"));
  154. if (waitResponse(10000L) != 1) { return false; }
  155. // Enable automatic time zone update
  156. sendAT(GF("+CTZU=1"));
  157. if (waitResponse(10000L) != 1) { return false; }
  158. SimStatus ret = getSimStatus();
  159. // if the sim isn't ready and a pin has been provided, try to unlock the sim
  160. if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
  161. simUnlock(pin);
  162. return (getSimStatus() == SIM_READY);
  163. } else {
  164. // if the sim is ready, or it's locked but no pin has been provided,
  165. // return true
  166. return (ret == SIM_READY || ret == SIM_LOCKED);
  167. }
  168. }
  169. /*
  170. * Power functions
  171. */
  172. protected:
  173. bool restartImpl(const char* pin = NULL) {
  174. if (!testAT()) { return false; }
  175. if (!setPhoneFunctionality(1, true)) { return false; }
  176. waitResponse(10000L, GF("APP RDY"));
  177. return init(pin);
  178. }
  179. bool powerOffImpl() {
  180. sendAT(GF("+QPOWD=1"));
  181. waitResponse(300); // returns OK first
  182. return waitResponse(300, GF("POWERED DOWN")) == 1;
  183. }
  184. // When entering into sleep mode is enabled, DTR is pulled up, and WAKEUP_IN
  185. // is pulled up, the module can directly enter into sleep mode.If entering
  186. // into sleep mode is enabled, DTR is pulled down, and WAKEUP_IN is pulled
  187. // down, there is a need to pull the DTR pin and the WAKEUP_IN pin up first,
  188. // and then the module can enter into sleep mode.
  189. bool sleepEnableImpl(bool enable = true) {
  190. sendAT(GF("+QSCLK="), enable);
  191. return waitResponse() == 1;
  192. }
  193. bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
  194. sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
  195. return waitResponse(10000L, GF("OK")) == 1;
  196. }
  197. /*
  198. * Generic network functions
  199. */
  200. public:
  201. RegStatus getRegistrationStatus() {
  202. // Check first for EPS registration
  203. RegStatus epsStatus = (RegStatus)getRegistrationStatusXREG("CEREG");
  204. // If we're connected on EPS, great!
  205. if (epsStatus == REG_OK_HOME || epsStatus == REG_OK_ROAMING) {
  206. return epsStatus;
  207. } else {
  208. // Otherwise, check generic network status
  209. return (RegStatus)getRegistrationStatusXREG("CREG");
  210. }
  211. }
  212. protected:
  213. bool isNetworkConnectedImpl() {
  214. RegStatus s = getRegistrationStatus();
  215. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  216. }
  217. /*
  218. * GPRS functions
  219. */
  220. protected:
  221. bool gprsConnectImpl(const char* apn, const char* user = NULL,
  222. const char* pwd = NULL) {
  223. gprsDisconnect();
  224. // Configure the TCPIP Context
  225. sendAT(GF("+QICSGP=1,1,\""), apn, GF("\",\""), user, GF("\",\""), pwd,
  226. GF("\""));
  227. if (waitResponse() != 1) { return false; }
  228. // Activate GPRS/CSD Context
  229. sendAT(GF("+QIACT=1"));
  230. if (waitResponse(150000L) != 1) { return false; }
  231. // Attach to Packet Domain service - is this necessary?
  232. sendAT(GF("+CGATT=1"));
  233. if (waitResponse(60000L) != 1) { return false; }
  234. return true;
  235. }
  236. bool gprsDisconnectImpl() {
  237. sendAT(GF("+QIDEACT=1")); // Deactivate the bearer context
  238. if (waitResponse(40000L) != 1) { return false; }
  239. return true;
  240. }
  241. /*
  242. * SIM card functions
  243. */
  244. protected:
  245. String getSimCCIDImpl() {
  246. sendAT(GF("+QCCID"));
  247. if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { return ""; }
  248. String res = stream.readStringUntil('\n');
  249. waitResponse();
  250. res.trim();
  251. return res;
  252. }
  253. /*
  254. * Phone Call functions
  255. */
  256. protected:
  257. // Can follow all of the phone call functions from the template
  258. /*
  259. * Messaging functions
  260. */
  261. protected:
  262. // Follows all messaging functions per template
  263. /*
  264. * GSM Location functions
  265. */
  266. protected:
  267. // NOTE: As of application firmware version 01.016.01.016 triangulated
  268. // locations can be obtained via the QuecLocator service and accompanying AT
  269. // commands. As this is a separate paid service which I do not have access
  270. // to, I am not implementing it here.
  271. /*
  272. * GPS/GNSS/GLONASS location functions
  273. */
  274. protected:
  275. // enable GPS
  276. bool enableGPSImpl() {
  277. sendAT(GF("+QGPS=1"));
  278. if (waitResponse() != 1) { return false; }
  279. return true;
  280. }
  281. bool disableGPSImpl() {
  282. sendAT(GF("+QGPSEND"));
  283. if (waitResponse() != 1) { return false; }
  284. return true;
  285. }
  286. // get the RAW GPS output
  287. String getGPSrawImpl() {
  288. sendAT(GF("+QGPSLOC=2"));
  289. if (waitResponse(10000L, GF(GSM_NL "+QGPSLOC:")) != 1) { return ""; }
  290. String res = stream.readStringUntil('\n');
  291. waitResponse();
  292. res.trim();
  293. return res;
  294. }
  295. // get GPS informations
  296. bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0,
  297. int* vsat = 0, int* usat = 0, float* accuracy = 0,
  298. int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
  299. int* minute = 0, int* second = 0) {
  300. sendAT(GF("+QGPSLOC=2"));
  301. if (waitResponse(10000L, GF(GSM_NL "+QGPSLOC:")) != 1) {
  302. // NOTE: Will return an error if the position isn't fixed
  303. return false;
  304. }
  305. // init variables
  306. float ilat = 0;
  307. float ilon = 0;
  308. float ispeed = 0;
  309. float ialt = 0;
  310. int iusat = 0;
  311. float iaccuracy = 0;
  312. int iyear = 0;
  313. int imonth = 0;
  314. int iday = 0;
  315. int ihour = 0;
  316. int imin = 0;
  317. float secondWithSS = 0;
  318. // UTC date & Time
  319. ihour = streamGetIntLength(2); // Two digit hour
  320. imin = streamGetIntLength(2); // Two digit minute
  321. secondWithSS = streamGetFloatBefore(','); // 6 digit second with subseconds
  322. ilat = streamGetFloatBefore(','); // Latitude
  323. ilon = streamGetFloatBefore(','); // Longitude
  324. iaccuracy = streamGetFloatBefore(','); // Horizontal precision
  325. ialt = streamGetFloatBefore(','); // Altitude from sea level
  326. streamSkipUntil(','); // GNSS positioning mode
  327. streamSkipUntil(','); // Course Over Ground based on true north
  328. streamSkipUntil(','); // Speed Over Ground in Km/h
  329. ispeed = streamGetFloatBefore(','); // Speed Over Ground in knots
  330. iday = streamGetIntLength(2); // Two digit day
  331. imonth = streamGetIntLength(2); // Two digit month
  332. iyear = streamGetIntBefore(','); // Two digit year
  333. iusat = streamGetIntBefore(','); // Number of satellites,
  334. streamSkipUntil('\n'); // The error code of the operation. If it is not
  335. // 0, it is the type of error.
  336. // Set pointers
  337. if (lat != NULL) *lat = ilat;
  338. if (lon != NULL) *lon = ilon;
  339. if (speed != NULL) *speed = ispeed;
  340. if (alt != NULL) *alt = ialt;
  341. if (vsat != NULL) *vsat = 0;
  342. if (usat != NULL) *usat = iusat;
  343. if (accuracy != NULL) *accuracy = iaccuracy;
  344. if (iyear < 2000) iyear += 2000;
  345. if (year != NULL) *year = iyear;
  346. if (month != NULL) *month = imonth;
  347. if (day != NULL) *day = iday;
  348. if (hour != NULL) *hour = ihour;
  349. if (minute != NULL) *minute = imin;
  350. if (second != NULL) *second = static_cast<int>(secondWithSS);
  351. waitResponse(); // Final OK
  352. return true;
  353. }
  354. /*
  355. * Time functions
  356. */
  357. protected:
  358. String getGSMDateTimeImpl(TinyGSMDateTimeFormat format) {
  359. sendAT(GF("+QLTS=2"));
  360. if (waitResponse(2000L, GF("+QLTS: \"")) != 1) { return ""; }
  361. String res;
  362. switch (format) {
  363. case DATE_FULL: res = stream.readStringUntil('"'); break;
  364. case DATE_TIME:
  365. streamSkipUntil(',');
  366. res = stream.readStringUntil('"');
  367. break;
  368. case DATE_DATE: res = stream.readStringUntil(','); break;
  369. }
  370. waitResponse(); // Ends with OK
  371. return res;
  372. }
  373. // The BG96 returns UTC time instead of local time as other modules do in
  374. // response to CCLK, so we're using QLTS where we can specifically request
  375. // local time.
  376. bool getNetworkTimeImpl(int* year, int* month, int* day, int* hour,
  377. int* minute, int* second, float* timezone) {
  378. sendAT(GF("+QLTS=2"));
  379. if (waitResponse(2000L, GF("+QLTS: \"")) != 1) { return false; }
  380. int iyear = 0;
  381. int imonth = 0;
  382. int iday = 0;
  383. int ihour = 0;
  384. int imin = 0;
  385. int isec = 0;
  386. int itimezone = 0;
  387. // Date & Time
  388. iyear = streamGetIntBefore('/');
  389. imonth = streamGetIntBefore('/');
  390. iday = streamGetIntBefore(',');
  391. ihour = streamGetIntBefore(':');
  392. imin = streamGetIntBefore(':');
  393. isec = streamGetIntLength(2);
  394. char tzSign = stream.read();
  395. itimezone = streamGetIntBefore(',');
  396. if (tzSign == '-') { itimezone = itimezone * -1; }
  397. streamSkipUntil('\n'); // DST flag
  398. // Set pointers
  399. if (iyear < 2000) iyear += 2000;
  400. if (year != NULL) *year = iyear;
  401. if (month != NULL) *month = imonth;
  402. if (day != NULL) *day = iday;
  403. if (hour != NULL) *hour = ihour;
  404. if (minute != NULL) *minute = imin;
  405. if (second != NULL) *second = isec;
  406. if (timezone != NULL) *timezone = static_cast<float>(itimezone) / 4.0;
  407. // Final OK
  408. waitResponse(); // Ends with OK
  409. return true;
  410. }
  411. /*
  412. * Battery functions
  413. */
  414. protected:
  415. // Can follow CBC as in the template
  416. /*
  417. * Temperature functions
  418. */
  419. protected:
  420. // get temperature in degree celsius
  421. uint16_t getTemperatureImpl() {
  422. sendAT(GF("+QTEMP"));
  423. if (waitResponse(GF(GSM_NL "+QTEMP:")) != 1) { return 0; }
  424. // return temperature in C
  425. uint16_t res =
  426. streamGetIntBefore(','); // read PMIC (primary ic) temperature
  427. streamSkipUntil(','); // skip XO temperature ??
  428. streamSkipUntil('\n'); // skip PA temperature ??
  429. // Wait for final OK
  430. waitResponse();
  431. return res;
  432. }
  433. /*
  434. * Client related functions
  435. */
  436. protected:
  437. bool modemConnect(const char* host, uint16_t port, uint8_t mux,
  438. bool ssl = false, int timeout_s = 150) {
  439. if (ssl) { DBG("SSL not yet supported on this module!"); }
  440. uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
  441. // <PDPcontextID>(1-16), <connectID>(0-11),
  442. // "TCP/UDP/TCP LISTENER/UDPSERVICE", "<IP_address>/<domain_name>",
  443. // <remote_port>,<local_port>,<access_mode>(0-2; 0=buffer)
  444. sendAT(GF("+QIOPEN=1,"), mux, GF(",\""), GF("TCP"), GF("\",\""), host,
  445. GF("\","), port, GF(",0,0"));
  446. waitResponse();
  447. if (waitResponse(timeout_ms, GF(GSM_NL "+QIOPEN:")) != 1) { return false; }
  448. if (streamGetIntBefore(',') != mux) { return false; }
  449. // Read status
  450. return (0 == streamGetIntBefore('\n'));
  451. }
  452. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  453. sendAT(GF("+QISEND="), mux, ',', (uint16_t)len);
  454. if (waitResponse(GF(">")) != 1) { return 0; }
  455. stream.write(reinterpret_cast<const uint8_t*>(buff), len);
  456. stream.flush();
  457. if (waitResponse(GF(GSM_NL "SEND OK")) != 1) { return 0; }
  458. // TODO(?): Wait for ACK? AT+QISEND=id,0
  459. return len;
  460. }
  461. size_t modemRead(size_t size, uint8_t mux) {
  462. if (!sockets[mux]) return 0;
  463. sendAT(GF("+QIRD="), mux, ',', (uint16_t)size);
  464. if (waitResponse(GF("+QIRD:")) != 1) { return 0; }
  465. int16_t len = streamGetIntBefore('\n');
  466. for (int i = 0; i < len; i++) { moveCharFromStreamToFifo(mux); }
  467. waitResponse();
  468. // DBG("### READ:", len, "from", mux);
  469. sockets[mux]->sock_available = modemGetAvailable(mux);
  470. return len;
  471. }
  472. size_t modemGetAvailable(uint8_t mux) {
  473. if (!sockets[mux]) return 0;
  474. sendAT(GF("+QIRD="), mux, GF(",0"));
  475. size_t result = 0;
  476. if (waitResponse(GF("+QIRD:")) == 1) {
  477. streamSkipUntil(','); // Skip total received
  478. streamSkipUntil(','); // Skip have read
  479. result = streamGetIntBefore('\n');
  480. if (result) { DBG("### DATA AVAILABLE:", result, "on", mux); }
  481. waitResponse();
  482. }
  483. if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
  484. return result;
  485. }
  486. bool modemGetConnected(uint8_t mux) {
  487. sendAT(GF("+QISTATE=1,"), mux);
  488. // +QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1"
  489. if (waitResponse(GF("+QISTATE:")) != 1) { return false; }
  490. streamSkipUntil(','); // Skip mux
  491. streamSkipUntil(','); // Skip socket type
  492. streamSkipUntil(','); // Skip remote ip
  493. streamSkipUntil(','); // Skip remote port
  494. streamSkipUntil(','); // Skip local port
  495. int8_t res = streamGetIntBefore(','); // socket state
  496. waitResponse();
  497. // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
  498. return 2 == res;
  499. }
  500. /*
  501. * Utilities
  502. */
  503. public:
  504. // TODO(vshymanskyy): Optimize this!
  505. int8_t waitResponse(uint32_t timeout_ms, String& data,
  506. GsmConstStr r1 = GFP(GSM_OK),
  507. GsmConstStr r2 = GFP(GSM_ERROR),
  508. #if defined TINY_GSM_DEBUG
  509. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  510. GsmConstStr r4 = GFP(GSM_CMS_ERROR),
  511. #else
  512. GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
  513. #endif
  514. GsmConstStr r5 = NULL) {
  515. /*String r1s(r1); r1s.trim();
  516. String r2s(r2); r2s.trim();
  517. String r3s(r3); r3s.trim();
  518. String r4s(r4); r4s.trim();
  519. String r5s(r5); r5s.trim();
  520. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  521. data.reserve(64);
  522. uint8_t index = 0;
  523. uint32_t startMillis = millis();
  524. do {
  525. TINY_GSM_YIELD();
  526. while (stream.available() > 0) {
  527. TINY_GSM_YIELD();
  528. int8_t a = stream.read();
  529. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  530. data += static_cast<char>(a);
  531. if (r1 && data.endsWith(r1)) {
  532. index = 1;
  533. goto finish;
  534. } else if (r2 && data.endsWith(r2)) {
  535. index = 2;
  536. goto finish;
  537. } else if (r3 && data.endsWith(r3)) {
  538. #if defined TINY_GSM_DEBUG
  539. if (r3 == GFP(GSM_CME_ERROR)) {
  540. streamSkipUntil('\n'); // Read out the error
  541. }
  542. #endif
  543. index = 3;
  544. goto finish;
  545. } else if (r4 && data.endsWith(r4)) {
  546. index = 4;
  547. goto finish;
  548. } else if (r5 && data.endsWith(r5)) {
  549. index = 5;
  550. goto finish;
  551. } else if (data.endsWith(GF(GSM_NL "+QIURC:"))) {
  552. streamSkipUntil('\"');
  553. String urc = stream.readStringUntil('\"');
  554. streamSkipUntil(',');
  555. if (urc == "recv") {
  556. int8_t mux = streamGetIntBefore('\n');
  557. DBG("### URC RECV:", mux);
  558. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  559. sockets[mux]->got_data = true;
  560. }
  561. } else if (urc == "closed") {
  562. int8_t mux = streamGetIntBefore('\n');
  563. DBG("### URC CLOSE:", mux);
  564. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  565. sockets[mux]->sock_connected = false;
  566. }
  567. } else {
  568. streamSkipUntil('\n');
  569. }
  570. data = "";
  571. }
  572. }
  573. } while (millis() - startMillis < timeout_ms);
  574. finish:
  575. if (!index) {
  576. data.trim();
  577. if (data.length()) { DBG("### Unhandled:", data); }
  578. data = "";
  579. }
  580. // data.replace(GSM_NL, "/");
  581. // DBG('<', index, '>', data);
  582. return index;
  583. }
  584. int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
  585. GsmConstStr r2 = GFP(GSM_ERROR),
  586. #if defined TINY_GSM_DEBUG
  587. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  588. GsmConstStr r4 = GFP(GSM_CMS_ERROR),
  589. #else
  590. GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
  591. #endif
  592. GsmConstStr r5 = NULL) {
  593. String data;
  594. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  595. }
  596. int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
  597. GsmConstStr r2 = GFP(GSM_ERROR),
  598. #if defined TINY_GSM_DEBUG
  599. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  600. GsmConstStr r4 = GFP(GSM_CMS_ERROR),
  601. #else
  602. GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
  603. #endif
  604. GsmConstStr r5 = NULL) {
  605. return waitResponse(1000, r1, r2, r3, r4, r5);
  606. }
  607. public:
  608. Stream& stream;
  609. protected:
  610. GsmClientBG96* sockets[TINY_GSM_MUX_COUNT];
  611. const char* gsmNL = GSM_NL;
  612. };
  613. #endif // SRC_TINYGSMCLIENTBG96_H_