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.

873 lines
22 KiB

  1. /**
  2. * @file TinyGsmClientSIM7600.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef TinyGsmClientSIM7600_h
  9. #define TinyGsmClientSIM7600_h
  10. // #define TINY_GSM_DEBUG Serial
  11. //#define TINY_GSM_USE_HEX
  12. #if !defined(TINY_GSM_RX_BUFFER)
  13. #define TINY_GSM_RX_BUFFER 64
  14. #endif
  15. #define TINY_GSM_MUX_COUNT 10
  16. #include <TinyGsmCommon.h>
  17. #define GSM_NL "\r\n"
  18. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  19. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  20. enum SimStatus {
  21. SIM_ERROR = 0,
  22. SIM_READY = 1,
  23. SIM_LOCKED = 2,
  24. };
  25. enum RegStatus {
  26. REG_UNREGISTERED = 0,
  27. REG_SEARCHING = 2,
  28. REG_DENIED = 3,
  29. REG_OK_HOME = 1,
  30. REG_OK_ROAMING = 5,
  31. REG_UNKNOWN = 4,
  32. };
  33. enum TinyGSMDateTimeFormat {
  34. DATE_FULL = 0,
  35. DATE_TIME = 1,
  36. DATE_DATE = 2
  37. };
  38. class TinyGsmSim7600
  39. {
  40. public:
  41. class GsmClient : public Client
  42. {
  43. friend class TinyGsmSim7600;
  44. typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
  45. public:
  46. GsmClient() {}
  47. GsmClient(TinyGsmSim7600& modem, uint8_t mux = 0) {
  48. init(&modem, mux);
  49. }
  50. virtual ~GsmClient(){}
  51. bool init(TinyGsmSim7600* modem, uint8_t mux = 0) {
  52. this->at = modem;
  53. this->mux = mux;
  54. sock_available = 0;
  55. prev_check = 0;
  56. sock_connected = false;
  57. got_data = false;
  58. at->sockets[mux] = this;
  59. return true;
  60. }
  61. public:
  62. virtual int connect(const char *host, uint16_t port, int timeout_s) {
  63. stop();
  64. TINY_GSM_YIELD();
  65. rx.clear();
  66. sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
  67. return sock_connected;
  68. }
  69. TINY_GSM_CLIENT_CONNECT_OVERLOADS()
  70. virtual void stop(uint32_t maxWaitMs) {
  71. TINY_GSM_CLIENT_DUMP_MODEM_BUFFER()
  72. at->sendAT(GF("+CIPCLOSE="), mux);
  73. sock_connected = false;
  74. at->waitResponse();
  75. }
  76. virtual void stop() { stop(15000L); }
  77. TINY_GSM_CLIENT_WRITE()
  78. TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK()
  79. TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK()
  80. TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
  81. /*
  82. * Extended API
  83. */
  84. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  85. private:
  86. TinyGsmSim7600* at;
  87. uint8_t mux;
  88. uint16_t sock_available;
  89. uint32_t prev_check;
  90. bool sock_connected;
  91. bool got_data;
  92. RxFifo rx;
  93. };
  94. public:
  95. TinyGsmSim7600(Stream& stream)
  96. : stream(stream)
  97. {
  98. memset(sockets, 0, sizeof(sockets));
  99. }
  100. virtual ~TinyGsmSim7600(){}
  101. /*
  102. * Basic functions
  103. */
  104. bool begin(const char* pin = NULL) {
  105. return init(pin);
  106. }
  107. bool init(const char* pin = NULL) {
  108. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  109. if (!testAT()) {
  110. return false;
  111. }
  112. sendAT(GF("E0")); // Echo Off
  113. if (waitResponse() != 1) {
  114. return false;
  115. }
  116. DBG(GF("### Modem:"), getModemName());
  117. getSimStatus();
  118. return true;
  119. }
  120. String getModemName() {
  121. String name = "SIMCom SIM7600";
  122. sendAT(GF("+CGMM"));
  123. String res2;
  124. if (waitResponse(1000L, res2) != 1) {
  125. return name;
  126. }
  127. res2.replace(GSM_NL "OK" GSM_NL, "");
  128. res2.replace("_", " ");
  129. res2.trim();
  130. name = res2;
  131. DBG("### Modem:", name);
  132. return name;
  133. }
  134. TINY_GSM_MODEM_SET_BAUD_IPR()
  135. TINY_GSM_MODEM_TEST_AT()
  136. TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS()
  137. bool factoryDefault() { // these commands aren't supported
  138. return false;
  139. }
  140. TINY_GSM_MODEM_GET_INFO_ATI()
  141. bool hasSSL() {
  142. return false; // TODO: Module supports SSL, but not yet implemented
  143. }
  144. bool hasWifi() {
  145. return false;
  146. }
  147. bool hasGPRS() {
  148. return true;
  149. }
  150. /*
  151. * Power functions
  152. */
  153. bool restart() {
  154. if (!testAT()) {
  155. return false;
  156. }
  157. sendAT(GF("+CRESET"));
  158. if (waitResponse(10000L) != 1) {
  159. return false;
  160. }
  161. delay(5000L); // TODO: Test this delay!
  162. return init();
  163. }
  164. bool poweroff() {
  165. sendAT(GF("+CPOF"));
  166. return waitResponse() == 1;
  167. }
  168. bool radioOff() {
  169. sendAT(GF("+CFUN=4"));
  170. if (waitResponse(10000L) != 1) {
  171. return false;
  172. }
  173. delay(3000);
  174. return true;
  175. }
  176. bool sleepEnable(bool enable = true) {
  177. sendAT(GF("+CSCLK="), enable);
  178. return waitResponse() == 1;
  179. }
  180. /*
  181. * SIM card functions
  182. */
  183. TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
  184. // Gets the CCID of a sim card via AT+CCID
  185. String getSimCCID() {
  186. sendAT(GF("+CICCID"));
  187. if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) {
  188. return "";
  189. }
  190. String res = stream.readStringUntil('\n');
  191. waitResponse();
  192. res.trim();
  193. return res;
  194. }
  195. TINY_GSM_MODEM_GET_IMEI_GSN()
  196. SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
  197. for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
  198. sendAT(GF("+CPIN?"));
  199. if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
  200. delay(1000);
  201. continue;
  202. }
  203. int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"));
  204. waitResponse();
  205. switch (status) {
  206. case 2:
  207. case 3: return SIM_LOCKED;
  208. case 1: return SIM_READY;
  209. default: return SIM_ERROR;
  210. }
  211. }
  212. return SIM_ERROR;
  213. }
  214. TINY_GSM_MODEM_GET_REGISTRATION_XREG(CGREG)
  215. TINY_GSM_MODEM_GET_OPERATOR_COPS()
  216. /*
  217. * Generic network functions
  218. */
  219. TINY_GSM_MODEM_GET_CSQ()
  220. bool isNetworkConnected() {
  221. RegStatus s = getRegistrationStatus();
  222. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  223. }
  224. String getNetworkModes() {
  225. sendAT(GF("+CNMP=?"));
  226. if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) {
  227. return "";
  228. }
  229. String res = stream.readStringUntil('\n');
  230. waitResponse();
  231. return res;
  232. }
  233. TINY_GSM_MODEM_WAIT_FOR_NETWORK()
  234. String setNetworkMode(uint8_t mode) {
  235. sendAT(GF("+CNMP="), mode);
  236. if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) {
  237. return "OK";
  238. }
  239. String res = stream.readStringUntil('\n');
  240. waitResponse();
  241. return res;
  242. }
  243. /*
  244. * GPRS functions
  245. */
  246. bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
  247. gprsDisconnect(); // Make sure we're not connected first
  248. // Define the PDP context
  249. // The CGDCONT commands set up the "external" PDP context
  250. // Set the external authentication
  251. if (user && strlen(user) > 0) {
  252. sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"');
  253. waitResponse();
  254. }
  255. // Define external PDP context 1
  256. sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"',",\"0.0.0.0\",0,0");
  257. waitResponse();
  258. // Configure TCP parameters
  259. // Select TCP/IP application mode (command mode)
  260. sendAT(GF("+CIPMODE=0"));
  261. waitResponse();
  262. // Set Sending Mode - send without waiting for peer TCP ACK
  263. sendAT(GF("+CIPSENDMODE=0"));
  264. waitResponse();
  265. // Configure socket parameters
  266. //AT+CIPCCFG= [<NmRetry>][,[<DelayTm>][,[<Ack>][,[<errMode>][,]<HeaderType>][,[[<AsyncMode>][,[<TimeoutVal>]]]]]]]]
  267. // NmRetry = number of retransmission to be made for an IP packet = 10 (default)
  268. // DelayTm = number of milliseconds to delay to output data of Receiving = 0 (default)
  269. // Ack = sets whether reporting a string “Send ok” = 0 (don't report)
  270. // errMode = mode of reporting error result code = 0 (numberic values)
  271. // HeaderType = which data header of receiving data in multi-client mode = 1 (“+RECEIVE,<link num>,<data length>”)
  272. // AsyncMode = sets mode of executing commands = 0 (synchronous command executing)
  273. // TimeoutVal = minimum retransmission timeout in milliseconds = 75000
  274. sendAT(GF("+CIPCCFG=10,0,0,0,1,0,75000"));
  275. if (waitResponse() != 1) {
  276. return false;
  277. }
  278. // Configure timeouts for opening and closing sockets
  279. // AT+CIPTIMEOUT=[<netopen_timeout>][, [<cipopen_timeout>][, [<cipsend_timeout>]]]
  280. sendAT(GF("+CIPTIMEOUT="), 75000, ',', 15000, ',', 15000);
  281. waitResponse();
  282. // Start the socket service
  283. // This activates and attaches to the external PDP context that is tied
  284. // to the embedded context for TCP/IP (ie AT+CGACT=1,1 and AT+CGATT=1)
  285. // Response may be an immediate "OK" followed later by "+NETOPEN: 0".
  286. // We to ignore any immediate response and wait for the
  287. // URC to show it's really connected.
  288. sendAT(GF("+NETOPEN"));
  289. if (waitResponse(75000L, GF(GSM_NL "+NETOPEN: 0")) != 1) {
  290. return false;
  291. }
  292. return true;
  293. }
  294. bool gprsDisconnect() {
  295. // Close all sockets and stop the socket service
  296. // Note: On the LTE models, this single command closes all sockets and the service
  297. sendAT(GF("+NETCLOSE"));
  298. if (waitResponse(60000L, GF(GSM_NL "+NETCLOSE: 0")) != 1) {
  299. return false;
  300. }
  301. return true;
  302. }
  303. bool isGprsConnected() {
  304. sendAT(GF("+NETOPEN?"));
  305. // May return +NETOPEN: 1, 0. We just confirm that the first number is 1
  306. if (waitResponse(GF(GSM_NL "+NETOPEN: 1")) != 1) {
  307. return false;
  308. }
  309. waitResponse();
  310. sendAT(GF("+IPADDR")); // Inquire Socket PDP address
  311. // sendAT(GF("+CGPADDR=1")); // Show PDP address
  312. if (waitResponse() != 1) {
  313. return false;
  314. }
  315. return true;
  316. }
  317. /*
  318. * IP Address functions
  319. */
  320. String getLocalIP() {
  321. sendAT(GF("+IPADDR")); // Inquire Socket PDP address
  322. // sendAT(GF("+CGPADDR=1")); // Show PDP address
  323. String res;
  324. if (waitResponse(10000L, res) != 1) {
  325. return "";
  326. }
  327. res.replace(GSM_NL "OK" GSM_NL, "");
  328. res.replace(GSM_NL, "");
  329. res.trim();
  330. return res;
  331. }
  332. IPAddress localIP() {
  333. return TinyGsmIpFromString(getLocalIP());
  334. }
  335. /*
  336. * Phone Call functions
  337. */
  338. bool setGsmBusy() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  339. bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  340. bool callNumber() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  341. bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  342. bool dtmfSend() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  343. /*
  344. * Messaging functions
  345. */
  346. String sendUSSD(const String& code) {
  347. // Select message format (1=text)
  348. sendAT(GF("+CMGF=1"));
  349. waitResponse();
  350. // Select TE character set
  351. sendAT(GF("+CSCS=\"HEX\""));
  352. waitResponse();
  353. sendAT(GF("+CUSD=1,\""), code, GF("\""));
  354. if (waitResponse() != 1) {
  355. return "";
  356. }
  357. if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) {
  358. return "";
  359. }
  360. stream.readStringUntil('"');
  361. String hex = stream.readStringUntil('"');
  362. stream.readStringUntil(',');
  363. int dcs = stream.readStringUntil('\n').toInt();
  364. if (dcs == 15) {
  365. return TinyGsmDecodeHex8bit(hex);
  366. } else if (dcs == 72) {
  367. return TinyGsmDecodeHex16bit(hex);
  368. } else {
  369. return hex;
  370. }
  371. }
  372. bool sendSMS(const String& number, const String& text) {
  373. // Get SMS service centre address
  374. sendAT(GF("+AT+CSCA?"));
  375. waitResponse();
  376. // Select message format (1=text)
  377. sendAT(GF("+CMGF=1"));
  378. waitResponse();
  379. //Set GSM 7 bit default alphabet (3GPP TS 23.038)
  380. sendAT(GF("+CSCS=\"GSM\""));
  381. waitResponse();
  382. // Send the message!
  383. sendAT(GF("+CMGS=\""), number, GF("\""));
  384. if (waitResponse(GF(">")) != 1) {
  385. return false;
  386. }
  387. stream.print(text);
  388. stream.write((char)0x1A);
  389. stream.flush();
  390. return waitResponse(60000L) == 1;
  391. }
  392. bool sendSMS_UTF16(const String& number, const void* text, size_t len) {
  393. // Select message format (1=text)
  394. sendAT(GF("+CMGF=1"));
  395. waitResponse();
  396. // Select TE character set
  397. sendAT(GF("+CSCS=\"HEX\""));
  398. waitResponse();
  399. // Set text mode parameters
  400. sendAT(GF("+CSMP=17,167,0,8"));
  401. waitResponse();
  402. // Send the message
  403. sendAT(GF("+CMGS=\""), number, GF("\""));
  404. if (waitResponse(GF(">")) != 1) {
  405. return false;
  406. }
  407. uint16_t* t = (uint16_t*)text;
  408. for (size_t i=0; i<len; i++) {
  409. uint8_t c = t[i] >> 8;
  410. if (c < 0x10) { stream.print('0'); }
  411. stream.print(c, HEX);
  412. c = t[i] & 0xFF;
  413. if (c < 0x10) { stream.print('0'); }
  414. stream.print(c, HEX);
  415. }
  416. stream.write((char)0x1A);
  417. stream.flush();
  418. return waitResponse(60000L) == 1;
  419. }
  420. /*
  421. * Location functions
  422. */
  423. String getGsmLocation() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  424. /*
  425. * GPS location functions
  426. */
  427. // enable GPS
  428. bool enableGPS() {
  429. sendAT(GF("+CGPS=1"));
  430. if (waitResponse() != 1) {
  431. return false;
  432. }
  433. return true;
  434. }
  435. bool disableGPS() {
  436. sendAT(GF("+CGPS=0"));
  437. if (waitResponse() != 1) {
  438. return false;
  439. }
  440. return true;
  441. }
  442. // get the RAW GPS output
  443. String getGPSraw() {
  444. sendAT(GF("+CGNSSINFO=32"));
  445. if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) {
  446. return "";
  447. }
  448. String res = stream.readStringUntil('\n');
  449. waitResponse();
  450. res.trim();
  451. return res;
  452. }
  453. // get GPS informations
  454. bool getGPS(float *lat, float *lon, float *speed=0, int *alt=0, int *vsat=0, int *usat=0) {
  455. //String buffer = "";
  456. bool fix = false;
  457. sendAT(GF("+CGNSSINFO"));
  458. if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) {
  459. return false;
  460. }
  461. //stream.readStringUntil(','); // mode
  462. if ( stream.readStringUntil(',').toInt() == 1 ) fix = true;
  463. stream.readStringUntil(','); //gps
  464. stream.readStringUntil(','); // glonass
  465. stream.readStringUntil(','); // beidu
  466. *lat = stream.readStringUntil(',').toFloat(); //lat
  467. stream.readStringUntil(','); // N/S
  468. *lon = stream.readStringUntil(',').toFloat(); //lon
  469. stream.readStringUntil(','); // E/W
  470. stream.readStringUntil(','); // date
  471. stream.readStringUntil(','); // UTC time
  472. if (alt != NULL) *alt = stream.readStringUntil(',').toFloat(); //alt
  473. if (speed != NULL) *speed = stream.readStringUntil(',').toFloat(); //speed
  474. stream.readStringUntil(','); //course
  475. stream.readStringUntil(','); //time
  476. stream.readStringUntil(',');//PDOP
  477. stream.readStringUntil(',');//HDOP
  478. stream.readStringUntil(',');//VDOP
  479. //if (vsat != NULL) *vsat = stream.readStringUntil(',').toInt(); //viewed satelites
  480. //if (usat != NULL) *usat = stream.readStringUntil(',').toInt(); //used satelites
  481. stream.readStringUntil('\n');
  482. waitResponse();
  483. return fix;
  484. }
  485. /*
  486. * Time functions
  487. */
  488. /*
  489. * Battery & temperature functions
  490. */
  491. // Use: float vBatt = modem.getBattVoltage() / 1000.0;
  492. uint16_t getBattVoltage() {
  493. sendAT(GF("+CBC"));
  494. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  495. return 0;
  496. }
  497. // get voltage in VOLTS
  498. float voltage = stream.readStringUntil('\n').toFloat();
  499. // Wait for final OK
  500. waitResponse();
  501. // Return millivolts
  502. uint16_t res = voltage*1000;
  503. return res;
  504. }
  505. int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
  506. uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE;
  507. bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
  508. sendAT(GF("+CBC?"));
  509. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  510. return false;
  511. }
  512. // get voltage in VOLTS
  513. float voltage = stream.readStringUntil('\n').toFloat();
  514. milliVolts = voltage*1000;
  515. // Wait for final OK
  516. waitResponse();
  517. return true;
  518. }
  519. // get temperature in degree celsius
  520. uint16_t getTemperature() {
  521. sendAT(GF("+CPMUTEMP"));
  522. if (waitResponse(GF(GSM_NL "+CPMUTEMP:")) != 1) {
  523. return 0;
  524. }
  525. // return temperature in C
  526. uint16_t res = stream.readStringUntil('\n').toInt();
  527. // Wait for final OK
  528. waitResponse();
  529. return res;
  530. }
  531. /*
  532. * Client related functions
  533. */
  534. protected:
  535. bool modemConnect(const char* host, uint16_t port, uint8_t mux,
  536. bool ssl = false, int timeout_s = 75) {
  537. // Make sure we'll be getting data manually on this connection
  538. sendAT(GF("+CIPRXGET=1"));
  539. if (waitResponse() != 1) {
  540. return false;
  541. }
  542. // Establish a connection in multi-socket mode
  543. sendAT(GF("+CIPOPEN="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port);
  544. // The reply is +CIPOPEN: ## of socket created
  545. if (waitResponse(15000L, GF(GSM_NL "+CIPOPEN:")) != 1) {
  546. return false;
  547. }
  548. return true;
  549. }
  550. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  551. sendAT(GF("+CIPSEND="), mux, ',', len);
  552. if (waitResponse(GF(">")) != 1) {
  553. return 0;
  554. }
  555. stream.write((uint8_t*)buff, len);
  556. stream.flush();
  557. if (waitResponse(GF(GSM_NL "+CIPSEND:")) != 1) {
  558. return 0;
  559. }
  560. streamSkipUntil(','); // Skip mux
  561. streamSkipUntil(','); // Skip requested bytes to send
  562. // TODO: make sure requested and confirmed bytes match
  563. return stream.readStringUntil('\n').toInt();
  564. }
  565. size_t modemRead(size_t size, uint8_t mux) {
  566. #ifdef TINY_GSM_USE_HEX
  567. sendAT(GF("+CIPRXGET=3,"), mux, ',', size);
  568. if (waitResponse(GF("+CIPRXGET:")) != 1) {
  569. return 0;
  570. }
  571. #else
  572. sendAT(GF("+CIPRXGET=2,"), mux, ',', size);
  573. if (waitResponse(GF("+CIPRXGET:")) != 1) {
  574. return 0;
  575. }
  576. #endif
  577. streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX
  578. streamSkipUntil(','); // Skip mux/cid (connecion id)
  579. size_t len_requested = stream.readStringUntil(',').toInt();
  580. // ^^ Requested number of data bytes (1-1460 bytes)to be read
  581. size_t len_confirmed = stream.readStringUntil('\n').toInt();
  582. // ^^ The data length which not read in the buffer
  583. for (size_t i=0; i<len_requested; i++) {
  584. uint32_t startMillis = millis();
  585. #ifdef TINY_GSM_USE_HEX
  586. while (stream.available() < 2 && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); }
  587. char buf[4] = { 0, };
  588. buf[0] = stream.read();
  589. buf[1] = stream.read();
  590. char c = strtol(buf, NULL, 16);
  591. #else
  592. while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); }
  593. char c = stream.read();
  594. #endif
  595. sockets[mux]->rx.put(c);
  596. }
  597. DBG("### READ:", len_requested, "from", mux);
  598. // sockets[mux]->sock_available = modemGetAvailable(mux);
  599. sockets[mux]->sock_available = len_confirmed;
  600. waitResponse();
  601. return len_requested;
  602. }
  603. size_t modemGetAvailable(uint8_t mux) {
  604. sendAT(GF("+CIPRXGET=4,"), mux);
  605. size_t result = 0;
  606. if (waitResponse(GF("+CIPRXGET:")) == 1) {
  607. streamSkipUntil(','); // Skip mode 4
  608. streamSkipUntil(','); // Skip mux
  609. result = stream.readStringUntil('\n').toInt();
  610. waitResponse();
  611. }
  612. DBG("### Available:", result, "on", mux);
  613. if (!result) {
  614. sockets[mux]->sock_connected = modemGetConnected(mux);
  615. }
  616. return result;
  617. }
  618. bool modemGetConnected(uint8_t mux) {
  619. // Read the status of all sockets at once
  620. sendAT(GF("+CIPCLOSE?"));
  621. if (waitResponse(GF("+CIPCLOSE:")) != 1) {
  622. // return false; // TODO: Why does this not read correctly?
  623. }
  624. for (int muxNo = 0; muxNo <= TINY_GSM_MUX_COUNT; muxNo++) {
  625. // +CIPCLOSE:<link0_state>,<link1_state>,...,<link9_state>
  626. sockets[muxNo]->sock_connected = stream.parseInt();
  627. }
  628. waitResponse(); // Should be an OK at the end
  629. return sockets[mux]->sock_connected;
  630. }
  631. public:
  632. /*
  633. Utilities
  634. */
  635. TINY_GSM_MODEM_STREAM_UTILITIES()
  636. // TODO: Optimize this!
  637. uint8_t waitResponse(uint32_t timeout_ms, String& data,
  638. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  639. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  640. {
  641. /*String r1s(r1); r1s.trim();
  642. String r2s(r2); r2s.trim();
  643. String r3s(r3); r3s.trim();
  644. String r4s(r4); r4s.trim();
  645. String r5s(r5); r5s.trim();
  646. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  647. data.reserve(64);
  648. int index = 0;
  649. unsigned long startMillis = millis();
  650. do {
  651. TINY_GSM_YIELD();
  652. while (stream.available() > 0) {
  653. TINY_GSM_YIELD();
  654. int a = stream.read();
  655. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  656. data += (char)a;
  657. if (r1 && data.endsWith(r1)) {
  658. index = 1;
  659. goto finish;
  660. } else if (r2 && data.endsWith(r2)) {
  661. index = 2;
  662. goto finish;
  663. } else if (r3 && data.endsWith(r3)) {
  664. index = 3;
  665. goto finish;
  666. } else if (r4 && data.endsWith(r4)) {
  667. index = 4;
  668. goto finish;
  669. } else if (r5 && data.endsWith(r5)) {
  670. index = 5;
  671. goto finish;
  672. } else if (data.endsWith(GF(GSM_NL "+CIPRXGET:"))) {
  673. String mode = stream.readStringUntil(',');
  674. if (mode.toInt() == 1) {
  675. int mux = stream.readStringUntil('\n').toInt();
  676. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  677. sockets[mux]->got_data = true;
  678. }
  679. data = "";
  680. DBG("### Got Data:", mux);
  681. } else {
  682. data += mode;
  683. }
  684. } else if (data.endsWith(GF(GSM_NL "+RECEIVE:"))) {
  685. int mux = stream.readStringUntil(',').toInt();
  686. int len = stream.readStringUntil('\n').toInt();
  687. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  688. sockets[mux]->got_data = true;
  689. sockets[mux]->sock_available = len;
  690. }
  691. data = "";
  692. DBG("### Got Data:", len, "on", mux);
  693. } else if (data.endsWith(GF("+IPCLOSE:"))) {
  694. int mux = stream.readStringUntil(',').toInt();
  695. streamSkipUntil('\n'); // Skip the reason code
  696. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  697. sockets[mux]->sock_connected = false;
  698. }
  699. data = "";
  700. DBG("### Closed: ", mux);
  701. } else if (data.endsWith(GF("+CIPEVENT:"))) {
  702. // Need to close all open sockets and release the network library.
  703. // User will then need to reconnect.
  704. DBG("### Network error!");
  705. if (!isGprsConnected()) {
  706. gprsDisconnect();
  707. }
  708. data = "";
  709. }
  710. }
  711. } while (millis() - startMillis < timeout_ms);
  712. finish:
  713. if (!index) {
  714. data.trim();
  715. if (data.length()) {
  716. DBG("### Unhandled:", data);
  717. }
  718. data = "";
  719. }
  720. //data.replace(GSM_NL, "/");
  721. //DBG('<', index, '>', data);
  722. return index;
  723. }
  724. uint8_t waitResponse(uint32_t timeout_ms,
  725. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  726. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  727. {
  728. String data;
  729. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  730. }
  731. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  732. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  733. {
  734. return waitResponse(1000, r1, r2, r3, r4, r5);
  735. }
  736. public:
  737. Stream& stream;
  738. protected:
  739. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  740. };
  741. #endif