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.

878 lines
22 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. /**
  2. * @file TinyGsmClientSIM5360.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef TinyGsmClientSIM5360_h
  9. #define TinyGsmClientSIM5360_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 TinyGsmSim5360
  39. {
  40. public:
  41. class GsmClient : public Client
  42. {
  43. friend class TinyGsmSim5360;
  44. typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
  45. public:
  46. GsmClient() {}
  47. GsmClient(TinyGsmSim5360& modem, uint8_t mux = 0) {
  48. init(&modem, mux);
  49. }
  50. virtual ~GsmClient(){}
  51. bool init(TinyGsmSim5360* 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. TinyGsmSim5360* 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. TinyGsmSim5360(Stream& stream)
  96. : stream(stream)
  97. {
  98. memset(sockets, 0, sizeof(sockets));
  99. }
  100. virtual ~TinyGsmSim5360(){}
  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 SIM5360";
  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("+REBOOT"));
  167. if (waitResponse(10000L) != 1) {
  168. return false;
  169. }
  170. delay(3000L); // 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. // The CGSOCKCONT commands define the "embedded" PDP context for TCP/IP
  268. // Define the socket PDP context
  269. sendAT(GF("+CGSOCKCONT=1,\"IP\",\""), apn, '"');
  270. waitResponse();
  271. // Set the embedded authentication
  272. if (user && strlen(user) > 0) {
  273. sendAT(GF("+CSOCKAUTH=1,1,\""), user, "\",\"", pwd, '"');
  274. waitResponse();
  275. }
  276. // Set active PDP context’s profile number
  277. // This ties the embedded TCP/IP application to the external PDP context
  278. sendAT(GF("+CSOCKSETPN=1"));
  279. waitResponse();
  280. // Configure TCP parameters
  281. // Select TCP/IP application mode (command mode)
  282. sendAT(GF("+CIPMODE=0"));
  283. waitResponse();
  284. // Set Sending Mode - send without waiting for peer TCP ACK
  285. sendAT(GF("+CIPSENDMODE=0"));
  286. waitResponse();
  287. // Configure socket parameters
  288. //AT+CIPCCFG= [<NmRetry>][,[<DelayTm>][,[<Ack>][,[<errMode>][,]<HeaderType>][,[[<AsyncMode>][,[<TimeoutVal>]]]]]]]]
  289. // NmRetry = number of retransmission to be made for an IP packet = 10 (default)
  290. // DelayTm = number of milliseconds to delay to output data of Receiving = 0 (default)
  291. // Ack = sets whether reporting a string “Send ok” = 0 (don't report)
  292. // errMode = mode of reporting error result code = 0 (numberic values)
  293. // HeaderType = which data header of receiving data in multi-client mode = 1 (“+RECEIVE,<link num>,<data length>”)
  294. // AsyncMode = sets mode of executing commands = 0 (synchronous command executing)
  295. // TimeoutVal = minimum retransmission timeout in milliseconds = 75000
  296. sendAT(GF("+CIPCCFG=10,0,0,0,1,0,75000"));
  297. if (waitResponse() != 1) {
  298. return false;
  299. }
  300. // Configure timeouts for opening and closing sockets
  301. // AT+CIPTIMEOUT=[<netopen_timeout>][, [<cipopen_timeout>][, [<cipsend_timeout>]]]
  302. sendAT(GF("+CIPTIMEOUT="), 75000, ',', 15000, ',', 15000);
  303. waitResponse();
  304. // Start the socket service
  305. // This activates and attaches to the external PDP context that is tied
  306. // to the embedded context for TCP/IP (ie AT+CGACT=1,1 and AT+CGATT=1)
  307. // Response may be an immediate "OK" followed later by "+NETOPEN: 0".
  308. // We to ignore any immediate response and wait for the
  309. // URC to show it's really connected.
  310. sendAT(GF("+NETOPEN"));
  311. if (waitResponse(75000L, GF(GSM_NL "+NETOPEN: 0")) != 1) {
  312. return false;
  313. }
  314. return true;
  315. }
  316. bool gprsDisconnect() {
  317. // Close any open sockets
  318. for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
  319. GsmClient *sock = sockets[mux];
  320. if (sock) {
  321. sock->stop();
  322. }
  323. }
  324. // Stop the socket service
  325. // Note: all sockets should be closed first - on 3G/4G models the sockets must be closed manually
  326. sendAT(GF("+NETCLOSE"));
  327. if (waitResponse(60000L, GF(GSM_NL "+NETCLOSE: 0")) != 1) {
  328. return false;
  329. }
  330. return true;
  331. }
  332. bool isGprsConnected() {
  333. sendAT(GF("+NETOPEN?"));
  334. // May return +NETOPEN: 1, 0. We just confirm that the first number is 1
  335. if (waitResponse(GF(GSM_NL "+NETOPEN: 1")) != 1) {
  336. return false;
  337. }
  338. waitResponse();
  339. sendAT(GF("+IPADDR")); // Inquire Socket PDP address
  340. // sendAT(GF("+CGPADDR=1")); // Show PDP address
  341. if (waitResponse() != 1) {
  342. return false;
  343. }
  344. return true;
  345. }
  346. /*
  347. * IP Address functions
  348. */
  349. String getLocalIP() {
  350. sendAT(GF("+IPADDR")); // Inquire Socket PDP address
  351. // sendAT(GF("+CGPADDR=1")); // Show PDP address
  352. String res;
  353. if (waitResponse(10000L, res) != 1) {
  354. return "";
  355. }
  356. res.replace(GSM_NL "OK" GSM_NL, "");
  357. res.replace(GSM_NL, "");
  358. res.trim();
  359. return res;
  360. }
  361. IPAddress localIP() {
  362. return TinyGsmIpFromString(getLocalIP());
  363. }
  364. /*
  365. * Phone Call functions
  366. */
  367. bool setGsmBusy() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  368. bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  369. bool callNumber() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  370. bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  371. bool dtmfSend() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  372. /*
  373. * Messaging functions
  374. */
  375. String sendUSSD(const String& code) {
  376. // Select message format (1=text)
  377. sendAT(GF("+CMGF=1"));
  378. waitResponse();
  379. // Select TE character set
  380. sendAT(GF("+CSCS=\"HEX\""));
  381. waitResponse();
  382. sendAT(GF("+CUSD=1,\""), code, GF("\""));
  383. if (waitResponse() != 1) {
  384. return "";
  385. }
  386. if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) {
  387. return "";
  388. }
  389. stream.readStringUntil('"');
  390. String hex = stream.readStringUntil('"');
  391. stream.readStringUntil(',');
  392. int dcs = stream.readStringUntil('\n').toInt();
  393. if (dcs == 15) {
  394. return TinyGsmDecodeHex8bit(hex);
  395. } else if (dcs == 72) {
  396. return TinyGsmDecodeHex16bit(hex);
  397. } else {
  398. return hex;
  399. }
  400. }
  401. bool sendSMS(const String& number, const String& text) {
  402. // Get SMS service centre address
  403. sendAT(GF("+AT+CSCA?"));
  404. waitResponse();
  405. // Select message format (1=text)
  406. sendAT(GF("+CMGF=1"));
  407. waitResponse();
  408. //Set GSM 7 bit default alphabet (3GPP TS 23.038)
  409. sendAT(GF("+CSCS=\"GSM\""));
  410. waitResponse();
  411. // Send the message!
  412. sendAT(GF("+CMGS=\""), number, GF("\""));
  413. if (waitResponse(GF(">")) != 1) {
  414. return false;
  415. }
  416. stream.print(text);
  417. stream.write((char)0x1A);
  418. stream.flush();
  419. return waitResponse(60000L) == 1;
  420. }
  421. bool sendSMS_UTF16(const String& number, const void* text, size_t len) {
  422. // Select message format (1=text)
  423. sendAT(GF("+CMGF=1"));
  424. waitResponse();
  425. // Select TE character set
  426. sendAT(GF("+CSCS=\"HEX\""));
  427. waitResponse();
  428. // Set text mode parameters
  429. sendAT(GF("+CSMP=17,167,0,8"));
  430. waitResponse();
  431. // Send the message
  432. sendAT(GF("+CMGS=\""), number, GF("\""));
  433. if (waitResponse(GF(">")) != 1) {
  434. return false;
  435. }
  436. uint16_t* t = (uint16_t*)text;
  437. for (size_t i=0; i<len; i++) {
  438. uint8_t c = t[i] >> 8;
  439. if (c < 0x10) { stream.print('0'); }
  440. stream.print(c, HEX);
  441. c = t[i] & 0xFF;
  442. if (c < 0x10) { stream.print('0'); }
  443. stream.print(c, HEX);
  444. }
  445. stream.write((char)0x1A);
  446. stream.flush();
  447. return waitResponse(60000L) == 1;
  448. }
  449. /*
  450. * Location functions
  451. */
  452. String getGsmLocation() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  453. /*
  454. * GPS location functions
  455. */
  456. /*
  457. * Time functions
  458. */
  459. /*
  460. * Battery & temperature functions
  461. */
  462. // Use: float vBatt = modem.getBattVoltage() / 1000.0;
  463. uint16_t getBattVoltage() {
  464. sendAT(GF("+CBC"));
  465. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  466. return 0;
  467. }
  468. streamSkipUntil(','); // Skip battery charge status
  469. streamSkipUntil(','); // Skip battery charge level
  470. // get voltage in VOLTS
  471. float voltage = stream.readStringUntil('\n').toFloat();
  472. // Wait for final OK
  473. waitResponse();
  474. // Return millivolts
  475. uint16_t res = voltage*1000;
  476. return res;
  477. }
  478. int8_t getBattPercent() {
  479. sendAT(GF("+CBC"));
  480. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  481. return false;
  482. }
  483. streamSkipUntil(','); // Skip battery charge status
  484. // Read battery charge level
  485. int res = stream.readStringUntil(',').toInt();
  486. // Wait for final OK
  487. waitResponse();
  488. return res;
  489. }
  490. uint8_t getBattChargeState() {
  491. sendAT(GF("+CBC?"));
  492. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  493. return false;
  494. }
  495. // Read battery charge status
  496. int res = stream.readStringUntil(',').toInt();
  497. // Wait for final OK
  498. waitResponse();
  499. return res;
  500. }
  501. bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
  502. sendAT(GF("+CBC?"));
  503. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  504. return false;
  505. }
  506. chargeState = stream.readStringUntil(',').toInt();
  507. percent = stream.readStringUntil(',').toInt();
  508. // get voltage in VOLTS
  509. float voltage = stream.readStringUntil('\n').toFloat();
  510. milliVolts = voltage*1000;
  511. // Wait for final OK
  512. waitResponse();
  513. return true;
  514. }
  515. // get temperature in degree celsius
  516. float getTemperature() {
  517. // Enable Temparature Reading
  518. sendAT(GF("+CMTE=1"));
  519. if (waitResponse() != 1) {
  520. return 0;
  521. }
  522. // Get Temparature Value
  523. sendAT(GF("+CMTE?"));
  524. if (waitResponse(GF(GSM_NL "+CMTE:")) != 1) {
  525. return false;
  526. }
  527. float res = stream.readStringUntil('\n').toFloat();
  528. // Wait for final OK
  529. waitResponse();
  530. return res;
  531. }
  532. /*
  533. * Client related functions
  534. */
  535. protected:
  536. bool modemConnect(const char* host, uint16_t port, uint8_t mux,
  537. bool ssl = false, int timeout_s = 15) {
  538. // Make sure we'll be getting data manually on this connection
  539. sendAT(GF("+CIPRXGET=1"));
  540. if (waitResponse() != 1) {
  541. return false;
  542. }
  543. if (ssl) DBG("SSL not yet supported on this module!");
  544. // Establish a connection in multi-socket mode
  545. uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
  546. sendAT(GF("+CIPOPEN="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port);
  547. // The reply is +CIPOPEN: ## of socket created
  548. if (waitResponse(timeout_ms, GF(GSM_NL "+CIPOPEN:")) != 1) {
  549. return false;
  550. }
  551. return true;
  552. }
  553. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  554. sendAT(GF("+CIPSEND="), mux, ',', len);
  555. if (waitResponse(GF(">")) != 1) {
  556. return 0;
  557. }
  558. stream.write((uint8_t*)buff, len);
  559. stream.flush();
  560. if (waitResponse(GF(GSM_NL "+CIPSEND:")) != 1) {
  561. return 0;
  562. }
  563. streamSkipUntil(','); // Skip mux
  564. streamSkipUntil(','); // Skip requested bytes to send
  565. // TODO: make sure requested and confirmed bytes match
  566. return stream.readStringUntil('\n').toInt();
  567. }
  568. size_t modemRead(size_t size, uint8_t mux) {
  569. #ifdef TINY_GSM_USE_HEX
  570. sendAT(GF("+CIPRXGET=3,"), mux, ',', size);
  571. if (waitResponse(GF("+CIPRXGET:")) != 1) {
  572. return 0;
  573. }
  574. #else
  575. sendAT(GF("+CIPRXGET=2,"), mux, ',', size);
  576. if (waitResponse(GF("+CIPRXGET:")) != 1) {
  577. return 0;
  578. }
  579. #endif
  580. streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX
  581. streamSkipUntil(','); // Skip mux/cid (connecion id)
  582. size_t len_requested = stream.readStringUntil(',').toInt();
  583. // ^^ Requested number of data bytes (1-1460 bytes)to be read
  584. size_t len_confirmed = stream.readStringUntil('\n').toInt();
  585. // ^^ The data length which not read in the buffer
  586. for (size_t i=0; i<len_requested; i++) {
  587. uint32_t startMillis = millis();
  588. #ifdef TINY_GSM_USE_HEX
  589. while (stream.available() < 2 && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); }
  590. char buf[4] = { 0, };
  591. buf[0] = stream.read();
  592. buf[1] = stream.read();
  593. char c = strtol(buf, NULL, 16);
  594. #else
  595. while (!stream.available() && (millis() - startMillis < sockets[mux]->_timeout)) { TINY_GSM_YIELD(); }
  596. char c = stream.read();
  597. #endif
  598. sockets[mux]->rx.put(c);
  599. }
  600. DBG("### READ:", len_requested, "from", mux);
  601. // sockets[mux]->sock_available = modemGetAvailable(mux);
  602. sockets[mux]->sock_available = len_confirmed;
  603. waitResponse();
  604. return len_requested;
  605. }
  606. size_t modemGetAvailable(uint8_t mux) {
  607. sendAT(GF("+CIPRXGET=4,"), mux);
  608. size_t result = 0;
  609. if (waitResponse(GF("+CIPRXGET:")) == 1) {
  610. streamSkipUntil(','); // Skip mode 4
  611. streamSkipUntil(','); // Skip mux
  612. result = stream.readStringUntil('\n').toInt();
  613. waitResponse();
  614. }
  615. DBG("### Available:", result, "on", mux);
  616. if (!result) {
  617. sockets[mux]->sock_connected = modemGetConnected(mux);
  618. }
  619. return result;
  620. }
  621. bool modemGetConnected(uint8_t mux) {
  622. // Read the status of all sockets at once
  623. sendAT(GF("+CIPCLOSE?"));
  624. if (waitResponse(GF("+CIPCLOSE:")) != 1) {
  625. return false;
  626. }
  627. for (int muxNo = 0; muxNo <= TINY_GSM_MUX_COUNT; muxNo++) {
  628. // +CIPCLOSE:<link0_state>,<link1_state>,...,<link9_state>
  629. sockets[muxNo]->sock_connected = stream.parseInt();
  630. }
  631. waitResponse(); // Should be an OK at the end
  632. return sockets[mux]->sock_connected;
  633. }
  634. public:
  635. /*
  636. Utilities
  637. */
  638. TINY_GSM_MODEM_STREAM_UTILITIES()
  639. // TODO: Optimize this!
  640. uint8_t waitResponse(uint32_t timeout_ms, String& data,
  641. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  642. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  643. {
  644. /*String r1s(r1); r1s.trim();
  645. String r2s(r2); r2s.trim();
  646. String r3s(r3); r3s.trim();
  647. String r4s(r4); r4s.trim();
  648. String r5s(r5); r5s.trim();
  649. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  650. data.reserve(64);
  651. int index = 0;
  652. unsigned long startMillis = millis();
  653. do {
  654. TINY_GSM_YIELD();
  655. while (stream.available() > 0) {
  656. TINY_GSM_YIELD();
  657. int a = stream.read();
  658. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  659. data += (char)a;
  660. if (r1 && data.endsWith(r1)) {
  661. index = 1;
  662. goto finish;
  663. } else if (r2 && data.endsWith(r2)) {
  664. index = 2;
  665. goto finish;
  666. } else if (r3 && data.endsWith(r3)) {
  667. index = 3;
  668. goto finish;
  669. } else if (r4 && data.endsWith(r4)) {
  670. index = 4;
  671. goto finish;
  672. } else if (r5 && data.endsWith(r5)) {
  673. index = 5;
  674. goto finish;
  675. } else if (data.endsWith(GF(GSM_NL "+CIPRXGET:"))) {
  676. String mode = stream.readStringUntil(',');
  677. if (mode.toInt() == 1) {
  678. int mux = stream.readStringUntil('\n').toInt();
  679. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  680. sockets[mux]->got_data = true;
  681. }
  682. data = "";
  683. DBG("### Got Data:", mux);
  684. } else {
  685. data += mode;
  686. }
  687. } else if (data.endsWith(GF(GSM_NL "+RECEIVE:"))) {
  688. int mux = stream.readStringUntil(',').toInt();
  689. int len = stream.readStringUntil('\n').toInt();
  690. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  691. sockets[mux]->got_data = true;
  692. sockets[mux]->sock_available = len;
  693. }
  694. data = "";
  695. DBG("### Got Data:", len, "on", mux);
  696. } else if (data.endsWith(GF("+IPCLOSE:"))) {
  697. int mux = stream.readStringUntil(',').toInt();
  698. streamSkipUntil('\n'); // Skip the reason code
  699. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  700. sockets[mux]->sock_connected = false;
  701. }
  702. data = "";
  703. DBG("### Closed: ", mux);
  704. } else if (data.endsWith(GF("+CIPEVENT:"))) {
  705. // Need to close all open sockets and release the network library.
  706. // User will then need to reconnect.
  707. DBG("### Network error!");
  708. if (!isGprsConnected()) {
  709. gprsDisconnect();
  710. }
  711. data = "";
  712. }
  713. }
  714. } while (millis() - startMillis < timeout_ms);
  715. finish:
  716. if (!index) {
  717. data.trim();
  718. if (data.length()) {
  719. DBG("### Unhandled:", data);
  720. }
  721. data = "";
  722. }
  723. //data.replace(GSM_NL, "/");
  724. //DBG('<', index, '>', data);
  725. return index;
  726. }
  727. uint8_t waitResponse(uint32_t timeout_ms,
  728. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  729. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  730. {
  731. String data;
  732. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  733. }
  734. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  735. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  736. {
  737. return waitResponse(1000, r1, r2, r3, r4, r5);
  738. }
  739. public:
  740. Stream& stream;
  741. protected:
  742. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  743. };
  744. #endif