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.

884 lines
22 KiB

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