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.

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