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.

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