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.

796 lines
24 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 SRC_TINYGSMCLIENTSIM7600_H_
  9. #define SRC_TINYGSMCLIENTSIM7600_H_
  10. // #define TINY_GSM_DEBUG Serial
  11. // #define TINY_GSM_USE_HEX
  12. #define TINY_GSM_MUX_COUNT 10
  13. #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
  14. #include "TinyGsmBattery.tpp"
  15. #include "TinyGsmGPRS.tpp"
  16. #include "TinyGsmGPS.tpp"
  17. #include "TinyGsmGSMLocation.tpp"
  18. #include "TinyGsmModem.tpp"
  19. #include "TinyGsmSMS.tpp"
  20. #include "TinyGsmTCP.tpp"
  21. #include "TinyGsmTemperature.tpp"
  22. #include "TinyGsmTime.tpp"
  23. #define GSM_NL "\r\n"
  24. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  25. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  26. #if defined TINY_GSM_DEBUG
  27. static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:";
  28. #endif
  29. enum RegStatus {
  30. REG_NO_RESULT = -1,
  31. REG_UNREGISTERED = 0,
  32. REG_SEARCHING = 2,
  33. REG_DENIED = 3,
  34. REG_OK_HOME = 1,
  35. REG_OK_ROAMING = 5,
  36. REG_UNKNOWN = 4,
  37. };
  38. class TinyGsmSim7600 : public TinyGsmModem<TinyGsmSim7600>,
  39. public TinyGsmGPRS<TinyGsmSim7600>,
  40. public TinyGsmTCP<TinyGsmSim7600, TINY_GSM_MUX_COUNT>,
  41. public TinyGsmSMS<TinyGsmSim7600>,
  42. public TinyGsmGSMLocation<TinyGsmSim7600>,
  43. public TinyGsmGPS<TinyGsmSim7600>,
  44. public TinyGsmTime<TinyGsmSim7600>,
  45. public TinyGsmBattery<TinyGsmSim7600>,
  46. public TinyGsmTemperature<TinyGsmSim7600> {
  47. friend class TinyGsmModem<TinyGsmSim7600>;
  48. friend class TinyGsmGPRS<TinyGsmSim7600>;
  49. friend class TinyGsmTCP<TinyGsmSim7600, TINY_GSM_MUX_COUNT>;
  50. friend class TinyGsmSMS<TinyGsmSim7600>;
  51. friend class TinyGsmGPS<TinyGsmSim7600>;
  52. friend class TinyGsmGSMLocation<TinyGsmSim7600>;
  53. friend class TinyGsmTime<TinyGsmSim7600>;
  54. friend class TinyGsmBattery<TinyGsmSim7600>;
  55. friend class TinyGsmTemperature<TinyGsmSim7600>;
  56. /*
  57. * Inner Client
  58. */
  59. public:
  60. class GsmClientSim7600 : public GsmClient {
  61. friend class TinyGsmSim7600;
  62. public:
  63. GsmClientSim7600() {}
  64. explicit GsmClientSim7600(TinyGsmSim7600& modem, uint8_t mux = 0) {
  65. init(&modem, mux);
  66. }
  67. bool init(TinyGsmSim7600* modem, uint8_t mux = 0) {
  68. this->at = modem;
  69. this->mux = mux;
  70. sock_available = 0;
  71. prev_check = 0;
  72. sock_connected = false;
  73. got_data = false;
  74. at->sockets[mux] = this;
  75. return true;
  76. }
  77. public:
  78. virtual int connect(const char* host, uint16_t port, int timeout_s) {
  79. stop();
  80. TINY_GSM_YIELD();
  81. rx.clear();
  82. sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
  83. return sock_connected;
  84. }
  85. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  86. void stop(uint32_t maxWaitMs) {
  87. dumpModemBuffer(maxWaitMs);
  88. at->sendAT(GF("+CIPCLOSE="), mux);
  89. sock_connected = false;
  90. at->waitResponse();
  91. }
  92. void stop() override {
  93. stop(15000L);
  94. }
  95. /*
  96. * Extended API
  97. */
  98. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  99. };
  100. /*
  101. * Inner Secure Client
  102. */
  103. /*TODO(?))
  104. class GsmClientSecureSIM7000 : public GsmClientSim7000
  105. {
  106. public:
  107. GsmClientSecure() {}
  108. GsmClientSecure(TinyGsmSim7000& modem, uint8_t mux = 1)
  109. : public GsmClient(modem, mux)
  110. {}
  111. public:
  112. int connect(const char* host, uint16_t port, int timeout_s) override {
  113. stop();
  114. TINY_GSM_YIELD();
  115. rx.clear();
  116. sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
  117. return sock_connected;
  118. }
  119. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  120. };
  121. */
  122. /*
  123. * Constructor
  124. */
  125. public:
  126. explicit TinyGsmSim7600(Stream& stream) : stream(stream) {
  127. memset(sockets, 0, sizeof(sockets));
  128. }
  129. /*
  130. * Basic functions
  131. */
  132. protected:
  133. bool initImpl(const char* pin = NULL) {
  134. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  135. if (!testAT()) { return false; }
  136. sendAT(GF("E0")); // Echo Off
  137. if (waitResponse() != 1) { return false; }
  138. #ifdef TINY_GSM_DEBUG
  139. sendAT(GF("+CMEE=2")); // turn on verbose error codes
  140. #else
  141. sendAT(GF("+CMEE=0")); // turn off error codes
  142. #endif
  143. waitResponse();
  144. DBG(GF("### Modem:"), getModemName());
  145. // Disable time and time zone URC's
  146. sendAT(GF("+CTZR=0"));
  147. if (waitResponse(10000L) != 1) { return false; }
  148. // Enable automatic time zome update
  149. sendAT(GF("+CTZU=1"));
  150. if (waitResponse(10000L) != 1) { return false; }
  151. SimStatus ret = getSimStatus();
  152. // if the sim isn't ready and a pin has been provided, try to unlock the sim
  153. if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
  154. simUnlock(pin);
  155. return (getSimStatus() == SIM_READY);
  156. } else {
  157. // if the sim is ready, or it's locked but no pin has been provided,
  158. // return true
  159. return (ret == SIM_READY || ret == SIM_LOCKED);
  160. }
  161. }
  162. String getModemNameImpl() {
  163. String name = "SIMCom SIM7600";
  164. sendAT(GF("+CGMM"));
  165. String res2;
  166. if (waitResponse(1000L, res2) != 1) { return name; }
  167. res2.replace(GSM_NL "OK" GSM_NL, "");
  168. res2.replace("_", " ");
  169. res2.trim();
  170. name = res2;
  171. DBG("### Modem:", name);
  172. return name;
  173. }
  174. bool factoryDefaultImpl() { // these commands aren't supported
  175. return false;
  176. }
  177. /*
  178. * Power functions
  179. */
  180. protected:
  181. bool restartImpl() {
  182. if (!testAT()) { return false; }
  183. sendAT(GF("+CRESET"));
  184. if (waitResponse(10000L) != 1) { return false; }
  185. delay(5000L); // TODO(?): Test this delay!
  186. return init();
  187. }
  188. bool powerOffImpl() {
  189. sendAT(GF("+CPOF"));
  190. return waitResponse() == 1;
  191. }
  192. bool radioOffImpl() {
  193. sendAT(GF("+CFUN=4"));
  194. if (waitResponse(10000L) != 1) { return false; }
  195. delay(3000);
  196. return true;
  197. }
  198. bool sleepEnableImpl(bool enable = true) {
  199. sendAT(GF("+CSCLK="), enable);
  200. return waitResponse() == 1;
  201. }
  202. /*
  203. * Generic network functions
  204. */
  205. public:
  206. RegStatus getRegistrationStatus() {
  207. return (RegStatus)getRegistrationStatusXREG("CGREG");
  208. }
  209. protected:
  210. bool isNetworkConnectedImpl() {
  211. RegStatus s = getRegistrationStatus();
  212. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  213. }
  214. public:
  215. String getNetworkModes() {
  216. sendAT(GF("+CNMP=?"));
  217. if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return ""; }
  218. String res = stream.readStringUntil('\n');
  219. waitResponse();
  220. return res;
  221. }
  222. String setNetworkMode(uint8_t mode) {
  223. sendAT(GF("+CNMP="), mode);
  224. if (waitResponse(GF(GSM_NL "+CNMP:")) != 1) { return "OK"; }
  225. String res = stream.readStringUntil('\n');
  226. waitResponse();
  227. return res;
  228. }
  229. String getLocalIPImpl() {
  230. sendAT(GF("+IPADDR")); // Inquire Socket PDP address
  231. // sendAT(GF("+CGPADDR=1")); // Show PDP address
  232. String res;
  233. if (waitResponse(10000L, res) != 1) { return ""; }
  234. res.replace(GSM_NL "OK" GSM_NL, "");
  235. res.replace(GSM_NL, "");
  236. res.trim();
  237. return res;
  238. }
  239. /*
  240. * GPRS functions
  241. */
  242. protected:
  243. bool gprsConnectImpl(const char* apn, const char* user = NULL,
  244. const char* pwd = NULL) {
  245. gprsDisconnect(); // Make sure we're not connected first
  246. // Define the PDP context
  247. // The CGDCONT commands set up the "external" PDP context
  248. // Set the external authentication
  249. if (user && strlen(user) > 0) {
  250. sendAT(GF("+CGAUTH=1,0,\""), user, GF("\",\""), pwd, '"');
  251. waitResponse();
  252. }
  253. // Define external PDP context 1
  254. sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"', ",\"0.0.0.0\",0,0");
  255. waitResponse();
  256. // Configure TCP parameters
  257. // Select TCP/IP application mode (command mode)
  258. sendAT(GF("+CIPMODE=0"));
  259. waitResponse();
  260. // Set Sending Mode - send without waiting for peer TCP ACK
  261. sendAT(GF("+CIPSENDMODE=0"));
  262. waitResponse();
  263. // Configure socket parameters
  264. // AT+CIPCCFG= <NmRetry>, <DelayTm>, <Ack>, <errMode>, <HeaderType>,
  265. // <AsyncMode>, <TimeoutVal>
  266. // NmRetry = number of retransmission to be made for an IP packet
  267. // = 10 (default)
  268. // DelayTm = number of milliseconds to delay before outputting received data
  269. // = 0 (default)
  270. // Ack = sets whether reporting a string "Send ok" = 0 (don't report)
  271. // errMode = mode of reporting error result code = 0 (numberic values)
  272. // HeaderType = which data header of receiving data in multi-client mode
  273. // = 1 (+RECEIVE,<link num>,<data length>)
  274. // AsyncMode = sets mode of executing commands
  275. // = 0 (synchronous command executing)
  276. // TimeoutVal = minimum retransmission timeout in milliseconds = 75000
  277. sendAT(GF("+CIPCCFG=10,0,0,0,1,0,75000"));
  278. if (waitResponse() != 1) { return false; }
  279. // Configure timeouts for opening and closing sockets
  280. // AT+CIPTIMEOUT=<netopen_timeout> <cipopen_timeout>, <cipsend_timeout>
  281. sendAT(GF("+CIPTIMEOUT="), 75000, ',', 15000, ',', 15000);
  282. waitResponse();
  283. // Start the socket service
  284. // This activates and attaches to the external PDP context that is tied
  285. // to the embedded context for TCP/IP (ie AT+CGACT=1,1 and AT+CGATT=1)
  286. // Response may be an immediate "OK" followed later by "+NETOPEN: 0".
  287. // We to ignore any immediate response and wait for the
  288. // URC to show it's really connected.
  289. sendAT(GF("+NETOPEN"));
  290. if (waitResponse(75000L, GF(GSM_NL "+NETOPEN: 0")) != 1) { return false; }
  291. return true;
  292. }
  293. bool gprsDisconnectImpl() {
  294. // Close all sockets and stop the socket service
  295. // Note: On the LTE models, this single command closes all sockets and the
  296. // service
  297. sendAT(GF("+NETCLOSE"));
  298. if (waitResponse(60000L, GF(GSM_NL "+NETCLOSE: 0")) != 1) { return false; }
  299. return true;
  300. }
  301. bool isGprsConnectedImpl() {
  302. sendAT(GF("+NETOPEN?"));
  303. // May return +NETOPEN: 1, 0. We just confirm that the first number is 1
  304. if (waitResponse(GF(GSM_NL "+NETOPEN: 1")) != 1) { return false; }
  305. waitResponse();
  306. sendAT(GF("+IPADDR")); // Inquire Socket PDP address
  307. // sendAT(GF("+CGPADDR=1")); // Show PDP address
  308. if (waitResponse() != 1) { return false; }
  309. return true;
  310. }
  311. /*
  312. * SIM card functions
  313. */
  314. protected:
  315. // Gets the CCID of a sim card via AT+CCID
  316. String getSimCCIDImpl() {
  317. sendAT(GF("+CICCID"));
  318. if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) { return ""; }
  319. String res = stream.readStringUntil('\n');
  320. waitResponse();
  321. res.trim();
  322. return res;
  323. }
  324. /*
  325. * Phone Call functions
  326. */
  327. protected:
  328. bool callAnswerImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  329. bool callNumberImpl(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  330. bool callHangupImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  331. bool dtmfSendImpl(char cmd,
  332. int duration_ms = 100) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  333. /*
  334. * Messaging functions
  335. */
  336. protected:
  337. // Follows all messaging functions per template
  338. /*
  339. * GSM Location functions
  340. */
  341. protected:
  342. // Can return a GSM-based location from CLBS as per the template
  343. /*
  344. * GPS/GNSS/GLONASS location functions
  345. */
  346. protected:
  347. // enable GPS
  348. bool enableGPSImpl() {
  349. sendAT(GF("+CGPS=1"));
  350. if (waitResponse() != 1) { return false; }
  351. return true;
  352. }
  353. bool disableGPSImpl() {
  354. sendAT(GF("+CGPS=0"));
  355. if (waitResponse() != 1) { return false; }
  356. return true;
  357. }
  358. // get the RAW GPS output
  359. String getGPSrawImpl() {
  360. sendAT(GF("+CGNSSINFO=32"));
  361. if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { return ""; }
  362. String res = stream.readStringUntil('\n');
  363. waitResponse();
  364. res.trim();
  365. return res;
  366. }
  367. // get GPS informations
  368. bool getGPSImpl(float* lat, float* lon, float* speed = 0, int* alt = 0,
  369. int* vsat = 0, int* usat = 0, float* accuracy = 0,
  370. int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
  371. int* minute = 0, int* second = 0) {
  372. sendAT(GF("+CGNSSINFO"));
  373. if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { return false; }
  374. uint8_t fixMode = streamGetIntBefore(','); // mode 2=2D Fix or 3=3DFix
  375. // TODO(?) Can 1 be returned
  376. if (fixMode == 1 || fixMode == 2 || fixMode == 3) {
  377. // init variables
  378. float ilat = 0;
  379. float ilon = 0;
  380. float ispeed = 0;
  381. float ialt = 0;
  382. int ivsat = 0;
  383. int iusat = 0;
  384. float iaccuracy = 0;
  385. int iyear = 0;
  386. int imonth = 0;
  387. int iday = 0;
  388. int ihour = 0;
  389. int imin = 0;
  390. float secondWithSS = 0;
  391. streamSkipUntil(','); // GPS satellite valid numbers
  392. streamSkipUntil(','); // GLONASS satellite valid numbers
  393. streamSkipUntil(','); // BEIDOU satellite valid numbers
  394. ilat = streamGetFloatBefore(','); // Latitude
  395. streamSkipUntil(','); // N/S Indicator, N=north or S=south
  396. ilon = streamGetFloatBefore(','); // Longitude
  397. streamSkipUntil(','); // E/W Indicator, E=east or W=west
  398. // Date. Output format is ddmmyy
  399. iday = streamGetIntLength(2); // Two digit day
  400. imonth = streamGetIntLength(2); // Two digit month
  401. iyear = streamGetIntBefore(','); // Two digit year
  402. // UTC Time. Output format is hhmmss.s
  403. ihour = streamGetIntLength(2); // Two digit hour
  404. imin = streamGetIntLength(2); // Two digit minute
  405. secondWithSS =
  406. streamGetFloatBefore(','); // 4 digit second with subseconds
  407. ialt = streamGetFloatBefore(','); // MSL Altitude. Unit is meters
  408. ispeed = streamGetFloatBefore(','); // Speed Over Ground. Unit is knots.
  409. streamSkipUntil(','); // Course Over Ground. Degrees.
  410. streamSkipUntil(','); // After set, will report GPS every x seconds
  411. iaccuracy = streamGetFloatBefore(','); // Position Dilution Of Precision
  412. streamSkipUntil(','); // Horizontal Dilution Of Precision
  413. streamSkipUntil(','); // Vertical Dilution Of Precision
  414. streamSkipUntil('\n'); // TODO(?) is one more field reported??
  415. // Set pointers
  416. if (lat != NULL) *lat = ilat;
  417. if (lon != NULL) *lon = ilon;
  418. if (speed != NULL) *speed = ispeed;
  419. if (alt != NULL) *alt = static_cast<int>(ialt);
  420. if (vsat != NULL) *vsat = ivsat;
  421. if (usat != NULL) *usat = iusat;
  422. if (accuracy != NULL) *accuracy = iaccuracy;
  423. if (iyear < 2000) iyear += 2000;
  424. if (year != NULL) *year = iyear;
  425. if (month != NULL) *month = imonth;
  426. if (day != NULL) *day = iday;
  427. if (hour != NULL) *hour = ihour;
  428. if (minute != NULL) *minute = imin;
  429. if (second != NULL) *second = static_cast<int>(secondWithSS);
  430. waitResponse();
  431. return true;
  432. }
  433. waitResponse();
  434. return false;
  435. }
  436. /*
  437. * Time functions
  438. */
  439. protected:
  440. // Can follow the standard CCLK function in the template
  441. /*
  442. * Battery functions
  443. */
  444. protected:
  445. // returns volts, multiply by 1000 to get mV
  446. uint16_t getBattVoltageImpl() {
  447. sendAT(GF("+CBC"));
  448. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) { return 0; }
  449. // get voltage in VOLTS
  450. float voltage = streamGetFloatBefore('\n');
  451. // Wait for final OK
  452. waitResponse();
  453. // Return millivolts
  454. uint16_t res = voltage * 1000;
  455. return res;
  456. }
  457. int8_t getBattPercentImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
  458. uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
  459. bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent,
  460. uint16_t& milliVolts) {
  461. chargeState = 0;
  462. percent = 0;
  463. milliVolts = getBattVoltage();
  464. return true;
  465. }
  466. /*
  467. * Temperature functions
  468. */
  469. // get temperature in degree celsius
  470. uint16_t getTemperatureImpl() {
  471. sendAT(GF("+CPMUTEMP"));
  472. if (waitResponse(GF(GSM_NL "+CPMUTEMP:")) != 1) { return 0; }
  473. // return temperature in C
  474. uint16_t res = streamGetIntBefore('\n');
  475. // Wait for final OK
  476. waitResponse();
  477. return res;
  478. }
  479. /*
  480. * Client related functions
  481. */
  482. protected:
  483. bool modemConnect(const char* host, uint16_t port, uint8_t mux,
  484. bool ssl = false, int timeout_s = 15) {
  485. if (ssl) { DBG("SSL not yet supported on this module!"); }
  486. // Make sure we'll be getting data manually on this connection
  487. sendAT(GF("+CIPRXGET=1"));
  488. if (waitResponse() != 1) { return false; }
  489. // Establish a connection in multi-socket mode
  490. uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
  491. sendAT(GF("+CIPOPEN="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","),
  492. port);
  493. // The reply is +CIPOPEN: ## of socket created
  494. if (waitResponse(timeout_ms, GF(GSM_NL "+CIPOPEN:")) != 1) { return false; }
  495. return true;
  496. }
  497. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  498. sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
  499. if (waitResponse(GF(">")) != 1) { return 0; }
  500. stream.write(reinterpret_cast<const uint8_t*>(buff), len);
  501. stream.flush();
  502. if (waitResponse(GF(GSM_NL "+CIPSEND:")) != 1) { return 0; }
  503. streamSkipUntil(','); // Skip mux
  504. streamSkipUntil(','); // Skip requested bytes to send
  505. // TODO(?): make sure requested and confirmed bytes match
  506. return streamGetIntBefore('\n');
  507. }
  508. size_t modemRead(size_t size, uint8_t mux) {
  509. #ifdef TINY_GSM_USE_HEX
  510. sendAT(GF("+CIPRXGET=3,"), mux, ',', (uint16_t)size);
  511. if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
  512. #else
  513. sendAT(GF("+CIPRXGET=2,"), mux, ',', (uint16_t)size);
  514. if (waitResponse(GF("+CIPRXGET:")) != 1) { return 0; }
  515. #endif
  516. streamSkipUntil(','); // Skip Rx mode 2/normal or 3/HEX
  517. streamSkipUntil(','); // Skip mux/cid (connecion id)
  518. int16_t len_requested = streamGetIntBefore(',');
  519. // ^^ Requested number of data bytes (1-1460 bytes)to be read
  520. int16_t len_confirmed = streamGetIntBefore('\n');
  521. // ^^ The data length which not read in the buffer
  522. for (int i = 0; i < len_requested; i++) {
  523. uint32_t startMillis = millis();
  524. #ifdef TINY_GSM_USE_HEX
  525. while (stream.available() < 2 &&
  526. (millis() - startMillis < sockets[mux]->_timeout)) {
  527. TINY_GSM_YIELD();
  528. }
  529. char buf[4] = {
  530. 0,
  531. };
  532. buf[0] = stream.read();
  533. buf[1] = stream.read();
  534. char c = strtol(buf, NULL, 16);
  535. #else
  536. while (!stream.available() &&
  537. (millis() - startMillis < sockets[mux]->_timeout)) {
  538. TINY_GSM_YIELD();
  539. }
  540. char c = stream.read();
  541. #endif
  542. sockets[mux]->rx.put(c);
  543. }
  544. // DBG("### READ:", len_requested, "from", mux);
  545. // sockets[mux]->sock_available = modemGetAvailable(mux);
  546. sockets[mux]->sock_available = len_confirmed;
  547. waitResponse();
  548. return len_requested;
  549. }
  550. size_t modemGetAvailable(uint8_t mux) {
  551. sendAT(GF("+CIPRXGET=4,"), mux);
  552. size_t result = 0;
  553. if (waitResponse(GF("+CIPRXGET:")) == 1) {
  554. streamSkipUntil(','); // Skip mode 4
  555. streamSkipUntil(','); // Skip mux
  556. result = streamGetIntBefore('\n');
  557. waitResponse();
  558. }
  559. // DBG("### Available:", result, "on", mux);
  560. if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
  561. return result;
  562. }
  563. bool modemGetConnected(uint8_t mux) {
  564. // Read the status of all sockets at once
  565. sendAT(GF("+CIPCLOSE?"));
  566. if (waitResponse(GF("+CIPCLOSE:")) != 1) {
  567. // return false; // TODO: Why does this not read correctly?
  568. }
  569. for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
  570. // +CIPCLOSE:<link0_state>,<link1_state>,...,<link9_state>
  571. sockets[muxNo]->sock_connected = stream.parseInt();
  572. }
  573. waitResponse(); // Should be an OK at the end
  574. return sockets[mux]->sock_connected;
  575. }
  576. /*
  577. * Utilities
  578. */
  579. public:
  580. // TODO(vshymanskyy): Optimize this!
  581. int8_t waitResponse(uint32_t timeout_ms, String& data,
  582. GsmConstStr r1 = GFP(GSM_OK),
  583. GsmConstStr r2 = GFP(GSM_ERROR),
  584. #if defined TINY_GSM_DEBUG
  585. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  586. #else
  587. GsmConstStr r3 = NULL,
  588. #endif
  589. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  590. /*String r1s(r1); r1s.trim();
  591. String r2s(r2); r2s.trim();
  592. String r3s(r3); r3s.trim();
  593. String r4s(r4); r4s.trim();
  594. String r5s(r5); r5s.trim();
  595. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  596. data.reserve(64);
  597. uint8_t index = 0;
  598. uint32_t startMillis = millis();
  599. do {
  600. TINY_GSM_YIELD();
  601. while (stream.available() > 0) {
  602. TINY_GSM_YIELD();
  603. int8_t a = stream.read();
  604. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  605. data += static_cast<char>(a);
  606. if (r1 && data.endsWith(r1)) {
  607. index = 1;
  608. goto finish;
  609. } else if (r2 && data.endsWith(r2)) {
  610. index = 2;
  611. goto finish;
  612. } else if (r3 && data.endsWith(r3)) {
  613. #if defined TINY_GSM_DEBUG
  614. if (r3 == GFP(GSM_CME_ERROR)) {
  615. streamSkipUntil('\n'); // Read out the error
  616. }
  617. #endif
  618. index = 3;
  619. goto finish;
  620. } else if (r4 && data.endsWith(r4)) {
  621. index = 4;
  622. goto finish;
  623. } else if (r5 && data.endsWith(r5)) {
  624. index = 5;
  625. goto finish;
  626. } else if (data.endsWith(GF(GSM_NL "+CIPRXGET:"))) {
  627. int8_t mode = streamGetIntBefore(',');
  628. if (mode == 1) {
  629. int8_t mux = streamGetIntBefore('\n');
  630. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  631. sockets[mux]->got_data = true;
  632. }
  633. data = "";
  634. // DBG("### Got Data:", mux);
  635. } else {
  636. data += mode;
  637. }
  638. } else if (data.endsWith(GF(GSM_NL "+RECEIVE:"))) {
  639. int8_t mux = streamGetIntBefore(',');
  640. int16_t len = streamGetIntBefore('\n');
  641. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  642. sockets[mux]->got_data = true;
  643. sockets[mux]->sock_available = len;
  644. }
  645. data = "";
  646. // DBG("### Got Data:", len, "on", mux);
  647. } else if (data.endsWith(GF("+IPCLOSE:"))) {
  648. int8_t mux = streamGetIntBefore(',');
  649. streamSkipUntil('\n'); // Skip the reason code
  650. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  651. sockets[mux]->sock_connected = false;
  652. }
  653. data = "";
  654. DBG("### Closed: ", mux);
  655. } else if (data.endsWith(GF("+CIPEVENT:"))) {
  656. // Need to close all open sockets and release the network library.
  657. // User will then need to reconnect.
  658. DBG("### Network error!");
  659. if (!isGprsConnected()) { gprsDisconnect(); }
  660. data = "";
  661. }
  662. }
  663. } while (millis() - startMillis < timeout_ms);
  664. finish:
  665. if (!index) {
  666. data.trim();
  667. if (data.length()) { DBG("### Unhandled:", data); }
  668. data = "";
  669. }
  670. // data.replace(GSM_NL, "/");
  671. // DBG('<', index, '>', data);
  672. return index;
  673. }
  674. int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
  675. GsmConstStr r2 = GFP(GSM_ERROR),
  676. #if defined TINY_GSM_DEBUG
  677. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  678. #else
  679. GsmConstStr r3 = NULL,
  680. #endif
  681. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  682. String data;
  683. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  684. }
  685. int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
  686. GsmConstStr r2 = GFP(GSM_ERROR),
  687. #if defined TINY_GSM_DEBUG
  688. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  689. #else
  690. GsmConstStr r3 = NULL,
  691. #endif
  692. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  693. return waitResponse(1000, r1, r2, r3, r4, r5);
  694. }
  695. protected:
  696. Stream& stream;
  697. GsmClientSim7600* sockets[TINY_GSM_MUX_COUNT];
  698. const char* gsmNL = GSM_NL;
  699. };
  700. #endif // SRC_TINYGSMCLIENTSIM7600_H_