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.

716 lines
22 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. /**
  2. * @file TinyGsmClientSequansMonarch.h
  3. * @author Michael Krumpus
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2019 Michael Krumpus
  6. * @date Jan 2019
  7. */
  8. #ifndef SRC_TINYGSMCLIENTSEQUANSMONARCH_H_
  9. #define SRC_TINYGSMCLIENTSEQUANSMONARCH_H_
  10. // #define TINY_GSM_DEBUG Serial
  11. #define TINY_GSM_MUX_COUNT 6
  12. #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
  13. #include "TinyGsmCalling.tpp"
  14. #include "TinyGsmGPRS.tpp"
  15. #include "TinyGsmModem.tpp"
  16. #include "TinyGsmSMS.tpp"
  17. #include "TinyGsmSSL.tpp"
  18. #include "TinyGsmTCP.tpp"
  19. #include "TinyGsmTemperature.tpp"
  20. #include "TinyGsmTime.tpp"
  21. #define GSM_NL "\r\n"
  22. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  23. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  24. #if defined TINY_GSM_DEBUG
  25. static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:";
  26. #endif
  27. enum RegStatus {
  28. REG_NO_RESULT = -1,
  29. REG_UNREGISTERED = 0,
  30. REG_SEARCHING = 2,
  31. REG_DENIED = 3,
  32. REG_OK_HOME = 1,
  33. REG_OK_ROAMING = 5,
  34. REG_UNKNOWN = 4,
  35. };
  36. enum SocketStatus {
  37. SOCK_CLOSED = 0,
  38. SOCK_ACTIVE_DATA = 1,
  39. SOCK_SUSPENDED = 2,
  40. SOCK_SUSPENDED_PENDING_DATA = 3,
  41. SOCK_LISTENING = 4,
  42. SOCK_INCOMING = 5,
  43. SOCK_OPENING = 6,
  44. };
  45. class TinyGsmSequansMonarch
  46. : public TinyGsmModem<TinyGsmSequansMonarch>,
  47. public TinyGsmGPRS<TinyGsmSequansMonarch>,
  48. public TinyGsmTCP<TinyGsmSequansMonarch, TINY_GSM_MUX_COUNT>,
  49. public TinyGsmSSL<TinyGsmSequansMonarch>,
  50. public TinyGsmCalling<TinyGsmSequansMonarch>,
  51. public TinyGsmSMS<TinyGsmSequansMonarch>,
  52. public TinyGsmTime<TinyGsmSequansMonarch>,
  53. public TinyGsmTemperature<TinyGsmSequansMonarch> {
  54. friend class TinyGsmModem<TinyGsmSequansMonarch>;
  55. friend class TinyGsmGPRS<TinyGsmSequansMonarch>;
  56. friend class TinyGsmTCP<TinyGsmSequansMonarch, TINY_GSM_MUX_COUNT>;
  57. friend class TinyGsmSSL<TinyGsmSequansMonarch>;
  58. friend class TinyGsmCalling<TinyGsmSequansMonarch>;
  59. friend class TinyGsmSMS<TinyGsmSequansMonarch>;
  60. friend class TinyGsmTime<TinyGsmSequansMonarch>;
  61. friend class TinyGsmTemperature<TinyGsmSequansMonarch>;
  62. /*
  63. * Inner Client
  64. */
  65. public:
  66. class GsmClientSequansMonarch : public GsmClient {
  67. friend class TinyGsmSequansMonarch;
  68. public:
  69. GsmClientSequansMonarch() {}
  70. explicit GsmClientSequansMonarch(TinyGsmSequansMonarch& modem,
  71. uint8_t mux = 1) {
  72. init(&modem, mux);
  73. }
  74. bool init(TinyGsmSequansMonarch* modem, uint8_t mux = 1) {
  75. this->at = modem;
  76. sock_available = 0;
  77. prev_check = 0;
  78. sock_connected = false;
  79. got_data = false;
  80. // adjust for zero indexed socket array vs Sequans' 1 indexed mux numbers
  81. // using modulus will force 6 back to 0
  82. if (mux >= 1 && mux <= TINY_GSM_MUX_COUNT) {
  83. this->mux = mux;
  84. } else {
  85. this->mux = (mux % TINY_GSM_MUX_COUNT) + 1;
  86. }
  87. at->sockets[mux % TINY_GSM_MUX_COUNT] = this;
  88. return true;
  89. }
  90. public:
  91. virtual int connect(const char* host, uint16_t port, int timeout_s) {
  92. if (sock_connected) stop();
  93. TINY_GSM_YIELD();
  94. rx.clear();
  95. sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
  96. return sock_connected;
  97. }
  98. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  99. void stop(uint32_t maxWaitMs) {
  100. dumpModemBuffer(maxWaitMs);
  101. at->sendAT(GF("+SQNSH="), mux);
  102. sock_connected = false;
  103. at->waitResponse();
  104. }
  105. void stop() override {
  106. stop(15000L);
  107. }
  108. /*
  109. * Extended API
  110. */
  111. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  112. };
  113. /*
  114. * Inner Secure Client
  115. */
  116. public:
  117. class GsmClientSecureSequansMonarch : public GsmClientSequansMonarch {
  118. public:
  119. GsmClientSecureSequansMonarch() {}
  120. explicit GsmClientSecureSequansMonarch(TinyGsmSequansMonarch& modem,
  121. uint8_t mux = 1)
  122. : GsmClientSequansMonarch(modem, mux) {}
  123. protected:
  124. bool strictSSL = false;
  125. public:
  126. int connect(const char* host, uint16_t port, int timeout_s) override {
  127. stop();
  128. TINY_GSM_YIELD();
  129. rx.clear();
  130. // configure security profile 1 with parameters:
  131. if (strictSSL) {
  132. // require minimum of TLS 1.2 (3)
  133. // only support cipher suite 0x3D: TLS_RSA_WITH_AES_256_CBC_SHA256
  134. // verify server certificate against imported CA certs 0 and enforce
  135. // validity period (3)
  136. at->sendAT(GF("+SQNSPCFG=1,3,\"0x3D\",3,0,,,\"\",\"\""));
  137. } else {
  138. // use TLS 1.0 or higher (1)
  139. // support wider variety of cipher suites
  140. // do not verify server certificate (0)
  141. at->sendAT(GF("+SQNSPCFG=1,1,\"0x2F;0x35;0x3C;0x3D\",0,,,,\"\",\"\""));
  142. }
  143. if (at->waitResponse() != 1) {
  144. DBG("failed to configure security profile");
  145. return false;
  146. }
  147. sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
  148. return sock_connected;
  149. }
  150. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  151. void setStrictSSL(bool strict) {
  152. strictSSL = strict;
  153. }
  154. };
  155. /*
  156. * Constructor
  157. */
  158. public:
  159. explicit TinyGsmSequansMonarch(Stream& stream) : stream(stream) {
  160. memset(sockets, 0, sizeof(sockets));
  161. }
  162. /*
  163. * Basic functions
  164. */
  165. protected:
  166. bool initImpl(const char* pin = NULL) {
  167. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  168. DBG(GF("### TinyGSM Compiled Module: TinyGsmClientSequansMonarch"));
  169. if (!testAT()) { return false; }
  170. sendAT(GF("E0")); // Echo Off
  171. if (waitResponse() != 1) { return false; }
  172. #ifdef TINY_GSM_DEBUG
  173. sendAT(GF("+CMEE=2")); // turn on verbose error codes
  174. #else
  175. sendAT(GF("+CMEE=0")); // turn off error codes
  176. #endif
  177. waitResponse();
  178. DBG(GF("### Modem:"), getModemName());
  179. // Make sure the module is enabled. Unlike others, the VZN20Q powers on
  180. // with CFUN=0 not CFUN=1 (that is, at minimum functionality instead of full
  181. // functionality The module cannot even detect the sim card if the cellular
  182. // functionality is disabled so unless we explicitly enable the
  183. // functionality the init will fail.
  184. sendAT(GF("+CFUN=1"));
  185. waitResponse();
  186. // Disable time and time zone URC's
  187. sendAT(GF("+CTZR=0"));
  188. if (waitResponse(10000L) != 1) { return false; }
  189. // Enable automatic time zome update
  190. sendAT(GF("+CTZU=1"));
  191. if (waitResponse(10000L) != 1) { return false; }
  192. SimStatus ret = getSimStatus();
  193. // if the sim isn't ready and a pin has been provided, try to unlock the sim
  194. if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
  195. simUnlock(pin);
  196. return (getSimStatus() == SIM_READY);
  197. } else {
  198. // if the sim is ready, or it's locked but no pin has been provided,
  199. // return true
  200. return (ret == SIM_READY || ret == SIM_LOCKED);
  201. }
  202. }
  203. String getModemNameImpl() {
  204. sendAT(GF("+CGMI"));
  205. String res1;
  206. if (waitResponse(1000L, res1) != 1) { return "unknown"; }
  207. res1.replace("\r\nOK\r\n", "");
  208. res1.replace("\rOK\r", "");
  209. res1.trim();
  210. sendAT(GF("+CGMM"));
  211. String res2;
  212. if (waitResponse(1000L, res2) != 1) { return "unknown"; }
  213. res2.replace("\r\nOK\r\n", "");
  214. res2.replace("\rOK\r", "");
  215. res2.trim();
  216. String name = res1 + String(' ') + res2;
  217. DBG("### Modem:", name);
  218. return name;
  219. }
  220. bool factoryDefaultImpl() {
  221. sendAT(GF("&F0")); // Factory
  222. waitResponse();
  223. sendAT(GF("Z")); // default configuration
  224. waitResponse();
  225. sendAT(GF("+IPR=0")); // Auto-baud
  226. return waitResponse() == 1;
  227. }
  228. void maintainImpl() {
  229. for (int mux = 1; mux <= TINY_GSM_MUX_COUNT; mux++) {
  230. GsmClientSequansMonarch* sock = sockets[mux % TINY_GSM_MUX_COUNT];
  231. if (sock && sock->got_data) {
  232. sock->got_data = false;
  233. sock->sock_available = modemGetAvailable(mux);
  234. // modemGetConnected() always checks the state of ALL socks
  235. modemGetConnected();
  236. }
  237. }
  238. while (stream.available()) { waitResponse(15, NULL, NULL); }
  239. }
  240. /*
  241. * Power functions
  242. */
  243. protected:
  244. bool restartImpl() {
  245. if (!testAT()) { return false; }
  246. sendAT(GF("+CFUN=0"));
  247. int8_t res = waitResponse(20000L, GFP(GSM_OK), GFP(GSM_ERROR),
  248. GF("+SYSSTART"));
  249. if (res != 1 && res != 3) { return false; }
  250. sendAT(GF("+CFUN=1,1"));
  251. res = waitResponse(20000L, GF("+SYSSTART"), GFP(GSM_ERROR));
  252. if (res != 1 && res != 3) { return false; }
  253. delay(1000);
  254. return init();
  255. }
  256. bool powerOffImpl() {
  257. // NOTE: The only way to turn the modem back on after this shutdown is with
  258. // a hard reset
  259. sendAT(GF("+SQNSSHDN"));
  260. return waitResponse();
  261. }
  262. // When power saving is enabled, UART0 interface is activated with sleep mode
  263. // support. Module power state is controlled by RTS0 line. When no activity
  264. // on UART, CTS line will be set to OFF state (driven high level) <timeout>
  265. // milliseconds (100ms to 10s, default 5s) after the last sent character,
  266. // then module will go to sleep mode as soon as DTE set RTS line to OFF state
  267. // (driver high level).
  268. bool sleepEnableImpl(bool enable = true) {
  269. sendAT(GF("+SQNIPSCFG="), enable);
  270. return waitResponse() == 1;
  271. }
  272. /*
  273. * Generic network functions
  274. */
  275. public:
  276. RegStatus getRegistrationStatus() {
  277. return (RegStatus)getRegistrationStatusXREG("CEREG");
  278. }
  279. protected:
  280. bool isNetworkConnectedImpl() {
  281. RegStatus s = getRegistrationStatus();
  282. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  283. }
  284. String getLocalIPImpl() {
  285. sendAT(GF("+CGPADDR=3"));
  286. if (waitResponse(10000L, GF("+CGPADDR: 3,\"")) != 1) { return ""; }
  287. String res = stream.readStringUntil('\"');
  288. waitResponse();
  289. return res;
  290. }
  291. /*
  292. * GPRS functions
  293. */
  294. protected:
  295. bool gprsConnectImpl(const char* apn, const char* user = NULL,
  296. const char* pwd = NULL) {
  297. gprsDisconnect();
  298. // Define the PDP context (This uses context #3!)
  299. sendAT(GF("+CGDCONT=3,\"IPV4V6\",\""), apn, '"');
  300. waitResponse();
  301. // Set authentication
  302. if (user && strlen(user) > 0) {
  303. sendAT(GF("+CGAUTH=3,1,\""), user, GF("\",\""), pwd, GF("\""));
  304. waitResponse();
  305. }
  306. // Activate the PDP context
  307. sendAT(GF("+CGACT=1,3"));
  308. waitResponse(60000L);
  309. // Attach to GPRS
  310. sendAT(GF("+CGATT=1"));
  311. if (waitResponse(60000L) != 1) { return false; }
  312. return true;
  313. }
  314. bool gprsDisconnectImpl() {
  315. sendAT(GF("+CGATT=0"));
  316. if (waitResponse(60000L) != 1) { return false; }
  317. return true;
  318. }
  319. /*
  320. * SIM card functions
  321. */
  322. protected:
  323. String getSimCCIDImpl() {
  324. sendAT(GF("+SQNCCID"));
  325. if (waitResponse(GF(GSM_NL "+SQNCCID:")) != 1) { return ""; }
  326. String res = stream.readStringUntil('\n');
  327. waitResponse();
  328. res.trim();
  329. return res;
  330. }
  331. /*
  332. * Phone Call functions
  333. */
  334. protected:
  335. bool callAnswerImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
  336. bool dtmfSendImpl(char cmd,
  337. int duration_ms = 100) TINY_GSM_ATTR_NOT_AVAILABLE;
  338. /*
  339. * Messaging functions
  340. */
  341. protected:
  342. // Follows all messaging functions per template
  343. /*
  344. * Time functions
  345. */
  346. protected:
  347. // Can follow the standard CCLK function in the template
  348. /*
  349. * Temperature functions
  350. */
  351. protected:
  352. float getTemperatureImpl() {
  353. sendAT(GF("+SMDTH"));
  354. if (waitResponse(10000L, GF("+SMDTH: ")) != 1) {
  355. return static_cast<float>(-9999);
  356. }
  357. String res;
  358. if (waitResponse(1000L, res) != 1) { return static_cast<float>(-9999); }
  359. if (res.indexOf("ERROR") >= 0) { return static_cast<float>(-9999); }
  360. return res.toFloat();
  361. }
  362. protected:
  363. bool modemConnect(const char* host, uint16_t port, uint8_t mux,
  364. bool ssl = false, int timeout_s = 75) {
  365. int8_t rsp;
  366. uint32_t startMillis = millis();
  367. uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
  368. if (ssl) {
  369. // enable SSl and use security profile 1
  370. // AT+SQNSSCFG=<connId>,<enable>,<spId>
  371. sendAT(GF("+SQNSSCFG="), mux, GF(",1,1"));
  372. if (waitResponse() != 1) {
  373. DBG("### WARNING: failed to configure secure socket");
  374. return false;
  375. }
  376. }
  377. // Socket configuration
  378. // AT+SQNSCFG:<connId1>, <cid1>, <pktSz1>, <maxTo1>, <connTo1>, <txTo1>
  379. // <connId1> = Connection ID = mux
  380. // <cid1> = PDP context ID = 3 - this is number set up above in the
  381. // GprsConnect function
  382. // <pktSz1> = Packet Size, used for online data mode only = 300 (default)
  383. // <maxTo1> = Max timeout in seconds = 90 (default)
  384. // <connTo1> = Connection timeout in hundreds of milliseconds
  385. // = 600 (default)
  386. // <txTo1> = Data sending timeout in hundreds of milliseconds,
  387. // used for online data mode only = 50 (default)
  388. sendAT(GF("+SQNSCFG="), mux, GF(",3,300,90,600,50"));
  389. waitResponse(5000L);
  390. // Socket configuration extended
  391. // AT+SQNSCFGEXT:<connId1>, <srMode1>, <recvDataMode1>, <keepalive1>,
  392. // <listenAutoRsp1>, <sendDataMode1>
  393. // <connId1> = Connection ID = mux
  394. // <srMode1> = Send/Receive URC model = 1 - data amount mode
  395. // <recvDataMode1> = Receive data mode = 0 - data as text (1 for hex)
  396. // <keepalive1> = unused = 0
  397. // <listenAutoRsp1> = Listen auto-response mode = 0 - deactivated
  398. // <sendDataMode1> = Send data mode = 0 - data as text (1 for hex)
  399. sendAT(GF("+SQNSCFGEXT="), mux, GF(",1,0,0,0,0"));
  400. waitResponse(5000L);
  401. // Socket dial
  402. // AT+SQNSD=<connId>,<txProt>,<rPort>,<IPaddr>[,<closureType>[,<lPort>[,<connMode>[,acceptAnyRemote]]]]
  403. // <connId> = Connection ID = mux
  404. // <txProt> = Transmission protocol = 0 - TCP (1 for UDP)
  405. // <rPort> = Remote host port to contact
  406. // <IPaddr> = Any valid IP address in the format xxx.xxx.xxx.xxx or any
  407. // host name solved with a DNS query
  408. // <closureType> = Socket closure behaviour for TCP, has no effect for UDP
  409. // = 0 - local port closes when remote does (default)
  410. // <lPort> = UDP connection local port, has no effect for TCP connections.
  411. // <connMode> = Connection mode = 1 - command mode connection
  412. // <acceptAnyRemote> = Applies to UDP only
  413. sendAT(GF("+SQNSD="), mux, ",0,", port, ',', GF("\""), host, GF("\""),
  414. ",0,0,1");
  415. rsp = waitResponse((timeout_ms - (millis() - startMillis)), GFP(GSM_OK),
  416. GFP(GSM_ERROR), GF("NO CARRIER" GSM_NL));
  417. // creation of socket failed immediately.
  418. if (rsp != 1) { return false; }
  419. // wait until we get a good status
  420. bool connected = false;
  421. while (!connected && ((millis() - startMillis) < timeout_ms)) {
  422. connected = modemGetConnected(mux);
  423. delay(100); // socket may be in opening state
  424. }
  425. return connected;
  426. }
  427. int modemSend(const void* buff, size_t len, uint8_t mux) {
  428. if (sockets[mux % TINY_GSM_MUX_COUNT]->sock_connected == false) {
  429. DBG("### Sock closed, cannot send data!");
  430. return 0;
  431. }
  432. sendAT(GF("+SQNSSENDEXT="), mux, ',', (uint16_t)len);
  433. waitResponse(10000L, GF(GSM_NL "> "));
  434. stream.write(reinterpret_cast<const uint8_t*>(buff), len);
  435. stream.flush();
  436. if (waitResponse() != 1) {
  437. DBG("### no OK after send");
  438. return 0;
  439. }
  440. return len;
  441. // uint8_t nAttempts = 5;
  442. // bool gotPrompt = false;
  443. // while (nAttempts > 0 && !gotPrompt) {
  444. // sendAT(GF("+SQNSSEND="), mux);
  445. // if (waitResponse(5000, GF(GSM_NL "> ")) == 1) {
  446. // gotPrompt = true;
  447. // }
  448. // nAttempts--;
  449. // delay(50);
  450. // }
  451. // if (gotPrompt) {
  452. // stream.write(reinterpret_cast<const uint8_t*>(buff), len);
  453. // stream.write(reinterpret_cast<char>0x1A);
  454. // stream.flush();
  455. // if (waitResponse() != 1) {
  456. // DBG("### no OK after send");
  457. // return 0;
  458. // }
  459. // return len;
  460. // }
  461. // return 0;
  462. }
  463. size_t modemRead(size_t size, uint8_t mux) {
  464. sendAT(GF("+SQNSRECV="), mux, ',', (uint16_t)size);
  465. if (waitResponse(GF("+SQNSRECV: ")) != 1) { return 0; }
  466. streamSkipUntil(','); // Skip mux
  467. int16_t len = streamGetIntBefore('\n');
  468. for (int i = 0; i < len; i++) {
  469. uint32_t startMillis = millis();
  470. while (!stream.available() &&
  471. ((millis() - startMillis) <
  472. sockets[mux % TINY_GSM_MUX_COUNT]->_timeout)) {
  473. TINY_GSM_YIELD();
  474. }
  475. char c = stream.read();
  476. sockets[mux % TINY_GSM_MUX_COUNT]->rx.put(c);
  477. }
  478. // DBG("### READ:", len, "from", mux);
  479. waitResponse();
  480. sockets[mux % TINY_GSM_MUX_COUNT]->sock_available = modemGetAvailable(mux);
  481. return len;
  482. }
  483. size_t modemGetAvailable(uint8_t mux) {
  484. sendAT(GF("+SQNSI="), mux);
  485. size_t result = 0;
  486. if (waitResponse(GF("+SQNSI:")) == 1) {
  487. streamSkipUntil(','); // Skip mux
  488. streamSkipUntil(','); // Skip total sent
  489. streamSkipUntil(','); // Skip total received
  490. result = streamGetIntBefore(','); // keep data not yet read
  491. waitResponse();
  492. }
  493. // DBG("### Available:", result, "on", mux);
  494. return result;
  495. }
  496. bool modemGetConnected(uint8_t mux = 1) {
  497. // This single command always returns the connection status of all
  498. // six possible sockets.
  499. sendAT(GF("+SQNSS"));
  500. for (int muxNo = 1; muxNo <= TINY_GSM_MUX_COUNT; muxNo++) {
  501. if (waitResponse(GFP(GSM_OK), GF(GSM_NL "+SQNSS: ")) != 2) {
  502. break;
  503. }
  504. uint8_t status = 0;
  505. // if (streamGetIntBefore(',') != muxNo) { // check the mux no
  506. // DBG("### Warning: misaligned mux numbers!");
  507. // }
  508. streamSkipUntil(','); // skip mux [use muxNo]
  509. status = stream.parseInt(); // Read the status
  510. // if mux is in use, will have comma then other info after the status
  511. // if not, there will be new line immediately after status
  512. // streamSkipUntil('\n'); // Skip port and IP info
  513. // SOCK_CLOSED = 0,
  514. // SOCK_ACTIVE_DATA = 1,
  515. // SOCK_SUSPENDED = 2,
  516. // SOCK_SUSPENDED_PENDING_DATA = 3,
  517. // SOCK_LISTENING = 4,
  518. // SOCK_INCOMING = 5,
  519. // SOCK_OPENING = 6,
  520. GsmClientSequansMonarch* sock = sockets[mux % TINY_GSM_MUX_COUNT];
  521. if (sock) {
  522. sock->sock_connected =
  523. ((status != SOCK_CLOSED) && (status != SOCK_INCOMING) &&
  524. (status != SOCK_OPENING));
  525. }
  526. }
  527. waitResponse(); // Should be an OK at the end
  528. return sockets[mux % TINY_GSM_MUX_COUNT]->sock_connected;
  529. }
  530. /*
  531. * Utilities
  532. */
  533. public:
  534. // TODO(vshymanskyy): Optimize this!
  535. int8_t waitResponse(uint32_t timeout_ms, String & data,
  536. GsmConstStr r1 = GFP(GSM_OK),
  537. GsmConstStr r2 = GFP(GSM_ERROR),
  538. #if defined TINY_GSM_DEBUG
  539. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  540. #else
  541. GsmConstStr r3 = NULL,
  542. #endif
  543. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  544. /*String r1s(r1); r1s.trim();
  545. String r2s(r2); r2s.trim();
  546. String r3s(r3); r3s.trim();
  547. String r4s(r4); r4s.trim();
  548. String r5s(r5); r5s.trim();
  549. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  550. data.reserve(64);
  551. uint8_t index = 0;
  552. uint32_t startMillis = millis();
  553. do {
  554. TINY_GSM_YIELD();
  555. while (stream.available() > 0) {
  556. TINY_GSM_YIELD();
  557. int8_t a = stream.read();
  558. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  559. data += static_cast<char>(a);
  560. if (r1 && data.endsWith(r1)) {
  561. index = 1;
  562. goto finish;
  563. } else if (r2 && data.endsWith(r2)) {
  564. index = 2;
  565. goto finish;
  566. } else if (r3 && data.endsWith(r3)) {
  567. #if defined TINY_GSM_DEBUG
  568. if (r3 == GFP(GSM_CME_ERROR)) {
  569. streamSkipUntil('\n'); // Read out the error
  570. }
  571. #endif
  572. index = 3;
  573. goto finish;
  574. } else if (r4 && data.endsWith(r4)) {
  575. index = 4;
  576. goto finish;
  577. } else if (r5 && data.endsWith(r5)) {
  578. index = 5;
  579. goto finish;
  580. } else if (data.endsWith(GF(GSM_NL "+SQNSRING:"))) {
  581. int8_t mux = streamGetIntBefore(',');
  582. int16_t len = streamGetIntBefore('\n');
  583. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT &&
  584. sockets[mux % TINY_GSM_MUX_COUNT]) {
  585. sockets[mux % TINY_GSM_MUX_COUNT]->got_data = true;
  586. sockets[mux % TINY_GSM_MUX_COUNT]->sock_available = len;
  587. }
  588. data = "";
  589. DBG("### URC Data Received:", len, "on", mux);
  590. } else if (data.endsWith(GF("SQNSH: "))) {
  591. int8_t mux = streamGetIntBefore('\n');
  592. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT &&
  593. sockets[mux % TINY_GSM_MUX_COUNT]) {
  594. sockets[mux % TINY_GSM_MUX_COUNT]->sock_connected = false;
  595. }
  596. data = "";
  597. DBG("### URC Sock Closed: ", mux);
  598. }
  599. }
  600. } while (millis() - startMillis < timeout_ms);
  601. finish:
  602. if (!index) {
  603. data.trim();
  604. if (data.length()) { DBG("### Unhandled:", data); }
  605. data = "";
  606. }
  607. // data.replace(GSM_NL, "/");
  608. // DBG('<', index, '>', data);
  609. return index;
  610. }
  611. int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
  612. GsmConstStr r2 = GFP(GSM_ERROR),
  613. #if defined TINY_GSM_DEBUG
  614. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  615. #else
  616. GsmConstStr r3 = NULL,
  617. #endif
  618. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  619. String data;
  620. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  621. }
  622. int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
  623. GsmConstStr r2 = GFP(GSM_ERROR),
  624. #if defined TINY_GSM_DEBUG
  625. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  626. #else
  627. GsmConstStr r3 = NULL,
  628. #endif
  629. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  630. return waitResponse(1000, r1, r2, r3, r4, r5);
  631. }
  632. public:
  633. Stream& stream;
  634. protected:
  635. GsmClientSequansMonarch* sockets[TINY_GSM_MUX_COUNT];
  636. const char* gsmNL = GSM_NL;
  637. };
  638. #endif // SRC_TINYGSMCLIENTSEQUANSMONARCH_H_