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.

1008 lines
24 KiB

8 years ago
8 years ago
8 years ago
6 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
6 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
7 years ago
8 years ago
6 years ago
8 years ago
8 years ago
8 years ago
8 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
7 years ago
8 years ago
7 years ago
7 years ago
8 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
7 years ago
8 years ago
8 years ago
7 years ago
8 years ago
8 years ago
7 years ago
8 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
7 years ago
  1. /**
  2. * @file TinyGsmClientSIM800.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef TinyGsmClientSIM800_h
  9. #define TinyGsmClientSIM800_h
  10. //#pragma message("TinyGSM: TinyGsmClientSIM800")
  11. //#define TINY_GSM_DEBUG Serial
  12. //#define TINY_GSM_USE_HEX
  13. #define TINY_GSM_MUX_COUNT 5
  14. #include <TinyGsmCommon.h>
  15. #define GSM_NL "\r\n"
  16. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  17. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  18. enum SimStatus {
  19. SIM_ERROR = 0,
  20. SIM_READY = 1,
  21. SIM_LOCKED = 2,
  22. };
  23. enum RegStatus {
  24. REG_UNREGISTERED = 0,
  25. REG_SEARCHING = 2,
  26. REG_DENIED = 3,
  27. REG_OK_HOME = 1,
  28. REG_OK_ROAMING = 5,
  29. REG_UNKNOWN = 4,
  30. };
  31. enum TinyGSMDateTimeFormat {
  32. DATE_FULL = 0,
  33. DATE_TIME = 1,
  34. DATE_DATE = 2
  35. };
  36. class TinyGsmSim800 : public TinyGsmModem
  37. {
  38. public:
  39. class GsmClient : public Client
  40. {
  41. friend class TinyGsmSim800;
  42. typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
  43. public:
  44. GsmClient() {}
  45. GsmClient(TinyGsmSim800& modem, uint8_t mux = 1) {
  46. init(&modem, mux);
  47. }
  48. bool init(TinyGsmSim800* modem, uint8_t mux = 1) {
  49. this->at = modem;
  50. this->mux = mux;
  51. sock_available = 0;
  52. prev_check = 0;
  53. sock_connected = false;
  54. got_data = false;
  55. at->sockets[mux] = this;
  56. return true;
  57. }
  58. public:
  59. virtual int connect(const char *host, uint16_t port) {
  60. stop();
  61. TINY_GSM_YIELD();
  62. rx.clear();
  63. sock_connected = at->modemConnect(host, port, mux);
  64. return sock_connected;
  65. }
  66. virtual int connect(IPAddress ip, uint16_t port) {
  67. String host; host.reserve(16);
  68. host += ip[0];
  69. host += ".";
  70. host += ip[1];
  71. host += ".";
  72. host += ip[2];
  73. host += ".";
  74. host += ip[3];
  75. return connect(host.c_str(), port);
  76. }
  77. virtual void stop() {
  78. TINY_GSM_YIELD();
  79. at->sendAT(GF("+CIPCLOSE="), mux);
  80. sock_connected = false;
  81. at->waitResponse();
  82. rx.clear();
  83. }
  84. virtual size_t write(const uint8_t *buf, size_t size) {
  85. TINY_GSM_YIELD();
  86. at->maintain();
  87. return at->modemSend(buf, size, mux);
  88. }
  89. virtual size_t write(uint8_t c) {
  90. return write(&c, 1);
  91. }
  92. virtual size_t write(const char *str) {
  93. if (str == NULL) return 0;
  94. return write((const uint8_t *)str, strlen(str));
  95. }
  96. virtual int available() {
  97. TINY_GSM_YIELD();
  98. if (!rx.size()) {
  99. // Workaround: sometimes SIM800 forgets to notify about data arrival.
  100. // TODO: Currently we ping the module periodically,
  101. // but maybe there's a better indicator that we need to poll
  102. if (millis() - prev_check > 250) {
  103. got_data = true;
  104. prev_check = millis();
  105. }
  106. at->maintain();
  107. }
  108. return rx.size() + sock_available;
  109. }
  110. virtual int read(uint8_t *buf, size_t size) {
  111. TINY_GSM_YIELD();
  112. at->maintain();
  113. size_t cnt = 0;
  114. while (cnt < size) {
  115. size_t chunk = TinyGsmMin(size-cnt, rx.size());
  116. if (chunk > 0) {
  117. rx.get(buf, chunk);
  118. buf += chunk;
  119. cnt += chunk;
  120. continue;
  121. }
  122. // TODO: Read directly into user buffer?
  123. // Workaround: sometimes SIM800 forgets to notify about data arrival.
  124. // TODO: Currently we ping the module periodically,
  125. // but maybe there's a better indicator that we need to poll
  126. if (millis() - prev_check > 250) {
  127. got_data = true;
  128. prev_check = millis();
  129. }
  130. at->maintain();
  131. if (sock_available > 0) {
  132. sock_available -= at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
  133. } else {
  134. break;
  135. }
  136. }
  137. return cnt;
  138. }
  139. virtual int read() {
  140. uint8_t c;
  141. if (read(&c, 1) == 1) {
  142. return c;
  143. }
  144. return -1;
  145. }
  146. virtual int peek() { return -1; } //TODO
  147. virtual void flush() { at->stream.flush(); }
  148. virtual uint8_t connected() {
  149. if (available()) {
  150. return true;
  151. }
  152. return sock_connected;
  153. }
  154. virtual operator bool() { return connected(); }
  155. /*
  156. * Extended API
  157. */
  158. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  159. private:
  160. TinyGsmSim800* at;
  161. uint8_t mux;
  162. uint16_t sock_available;
  163. uint32_t prev_check;
  164. bool sock_connected;
  165. bool got_data;
  166. RxFifo rx;
  167. };
  168. class GsmClientSecure : public GsmClient
  169. {
  170. public:
  171. GsmClientSecure() {}
  172. GsmClientSecure(TinyGsmSim800& modem, uint8_t mux = 1)
  173. : GsmClient(modem, mux)
  174. {}
  175. public:
  176. virtual int connect(const char *host, uint16_t port) {
  177. stop();
  178. TINY_GSM_YIELD();
  179. rx.clear();
  180. sock_connected = at->modemConnect(host, port, mux, true);
  181. return sock_connected;
  182. }
  183. };
  184. public:
  185. TinyGsmSim800(Stream& stream)
  186. : TinyGsmModem(stream), stream(stream)
  187. {
  188. memset(sockets, 0, sizeof(sockets));
  189. }
  190. /*
  191. * Basic functions
  192. */
  193. bool init(const char* pin = NULL) {
  194. if (!testAT()) {
  195. return false;
  196. }
  197. sendAT(GF("&FZ")); // Factory + Reset
  198. waitResponse();
  199. sendAT(GF("E0")); // Echo Off
  200. if (waitResponse() != 1) {
  201. return false;
  202. }
  203. DBG(GF("### Modem:"), getModemName());
  204. getSimStatus();
  205. return true;
  206. }
  207. String getModemName() {
  208. #if defined(TINY_GSM_MODEM_SIM800)
  209. return "SIMCom SIM800";
  210. #elif defined(TINY_GSM_MODEM_SIM808)
  211. return "SIMCom SIM808";
  212. #elif defined(TINY_GSM_MODEM_SIM868)
  213. return "SIMCom SIM868";
  214. #elif defined(TINY_GSM_MODEM_SIM900)
  215. return "SIMCom SIM900";
  216. #endif
  217. return "SIMCom SIM800";
  218. }
  219. void setBaud(unsigned long baud) {
  220. sendAT(GF("+IPR="), baud);
  221. }
  222. bool testAT(unsigned long timeout = 10000L) {
  223. //streamWrite(GF("AAAAA" GSM_NL)); // TODO: extra A's to help detect the baud rate
  224. for (unsigned long start = millis(); millis() - start < timeout; ) {
  225. sendAT(GF(""));
  226. if (waitResponse(200) == 1) return true;
  227. delay(100);
  228. }
  229. return false;
  230. }
  231. void maintain() {
  232. for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
  233. GsmClient* sock = sockets[mux];
  234. if (sock && sock->got_data) {
  235. sock->got_data = false;
  236. sock->sock_available = modemGetAvailable(mux);
  237. }
  238. }
  239. while (stream.available()) {
  240. waitResponse(10, NULL, NULL);
  241. }
  242. }
  243. bool factoryDefault() {
  244. sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
  245. waitResponse();
  246. sendAT(GF("+IPR=0")); // Auto-baud
  247. waitResponse();
  248. sendAT(GF("+IFC=0,0")); // No Flow Control
  249. waitResponse();
  250. sendAT(GF("+ICF=3,3")); // 8 data 0 parity 1 stop
  251. waitResponse();
  252. sendAT(GF("+CSCLK=0")); // Disable Slow Clock
  253. waitResponse();
  254. sendAT(GF("&W")); // Write configuration
  255. return waitResponse() == 1;
  256. }
  257. String getModemInfo() {
  258. sendAT(GF("I"));
  259. String res;
  260. if (waitResponse(1000L, res) != 1) {
  261. return "";
  262. }
  263. res.replace(GSM_NL "OK" GSM_NL, "");
  264. res.replace(GSM_NL, " ");
  265. res.trim();
  266. return res;
  267. }
  268. bool hasSSL() {
  269. #if defined(TINY_GSM_MODEM_SIM900)
  270. return false;
  271. #else
  272. sendAT(GF("+CIPSSL=?"));
  273. if (waitResponse(GF(GSM_NL "+CIPSSL:")) != 1) {
  274. return false;
  275. }
  276. return waitResponse() == 1;
  277. #endif
  278. }
  279. bool hasWifi() {
  280. return false;
  281. }
  282. bool hasGPRS() {
  283. return true;
  284. }
  285. /*
  286. * Power functions
  287. */
  288. bool restart() {
  289. if (!testAT()) {
  290. return false;
  291. }
  292. //Enable Local Time Stamp for getting network time
  293. // TODO: Find a better place for this
  294. sendAT(GF("+CLTS=1"));
  295. if (waitResponse(10000L) != 1) {
  296. return false;
  297. }
  298. sendAT(GF("&W"));
  299. waitResponse();
  300. sendAT(GF("+CFUN=0"));
  301. if (waitResponse(10000L) != 1) {
  302. return false;
  303. }
  304. sendAT(GF("+CFUN=1,1"));
  305. if (waitResponse(10000L) != 1) {
  306. return false;
  307. }
  308. delay(3000);
  309. return init();
  310. }
  311. bool poweroff() {
  312. sendAT(GF("+CPOWD=1"));
  313. return waitResponse(GF("NORMAL POWER DOWN")) == 1;
  314. }
  315. bool radioOff() {
  316. sendAT(GF("+CFUN=0"));
  317. if (waitResponse(10000L) != 1) {
  318. return false;
  319. }
  320. delay(3000);
  321. return true;
  322. }
  323. /*
  324. During sleep, the SIM800 module has its serial communication disabled. In order to reestablish communication
  325. pull the DRT-pin of the SIM800 module LOW for at least 50ms. Then use this function to disable sleep mode.
  326. The DTR-pin can then be released again.
  327. */
  328. bool sleepEnable(bool enable = true) {
  329. sendAT(GF("+CSCLK="), enable);
  330. return waitResponse() == 1;
  331. }
  332. /*
  333. * SIM card functions
  334. */
  335. bool simUnlock(const char *pin) {
  336. sendAT(GF("+CPIN=\""), pin, GF("\""));
  337. return waitResponse() == 1;
  338. }
  339. String getSimCCID() {
  340. sendAT(GF("+ICCID"));
  341. if (waitResponse(GF(GSM_NL "+ICCID:")) != 1) {
  342. return "";
  343. }
  344. String res = stream.readStringUntil('\n');
  345. waitResponse();
  346. res.trim();
  347. return res;
  348. }
  349. String getIMEI() {
  350. sendAT(GF("+GSN"));
  351. if (waitResponse(GF(GSM_NL)) != 1) {
  352. return "";
  353. }
  354. String res = stream.readStringUntil('\n');
  355. waitResponse();
  356. res.trim();
  357. return res;
  358. }
  359. SimStatus getSimStatus(unsigned long timeout = 10000L) {
  360. for (unsigned long start = millis(); millis() - start < timeout; ) {
  361. sendAT(GF("+CPIN?"));
  362. if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
  363. delay(1000);
  364. continue;
  365. }
  366. int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED"));
  367. waitResponse();
  368. switch (status) {
  369. case 2:
  370. case 3: return SIM_LOCKED;
  371. case 1: return SIM_READY;
  372. default: return SIM_ERROR;
  373. }
  374. }
  375. return SIM_ERROR;
  376. }
  377. RegStatus getRegistrationStatus() {
  378. sendAT(GF("+CREG?"));
  379. if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
  380. return REG_UNKNOWN;
  381. }
  382. streamSkipUntil(','); // Skip format (0)
  383. int status = stream.readStringUntil('\n').toInt();
  384. waitResponse();
  385. return (RegStatus)status;
  386. }
  387. String getOperator() {
  388. sendAT(GF("+COPS?"));
  389. if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
  390. return "";
  391. }
  392. streamSkipUntil('"'); // Skip mode and format
  393. String res = stream.readStringUntil('"');
  394. waitResponse();
  395. return res;
  396. }
  397. /*
  398. * Generic network functions
  399. */
  400. int16_t getSignalQuality() {
  401. sendAT(GF("+CSQ"));
  402. if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
  403. return 99;
  404. }
  405. int res = stream.readStringUntil(',').toInt();
  406. waitResponse();
  407. return res;
  408. }
  409. bool isNetworkConnected() {
  410. RegStatus s = getRegistrationStatus();
  411. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  412. }
  413. /*
  414. * GPRS functions
  415. */
  416. bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
  417. gprsDisconnect();
  418. // Set the Bearer for the IP
  419. sendAT(GF("+SAPBR=3,1,\"Contype\",\"GPRS\"")); // Set the connection type to GPRS
  420. waitResponse();
  421. sendAT(GF("+SAPBR=3,1,\"APN\",\""), apn, '"'); // Set the APN
  422. waitResponse();
  423. if (user && strlen(user) > 0) {
  424. sendAT(GF("+SAPBR=3,1,\"USER\",\""), user, '"'); // Set the user name
  425. waitResponse();
  426. }
  427. if (pwd && strlen(pwd) > 0) {
  428. sendAT(GF("+SAPBR=3,1,\"PWD\",\""), pwd, '"'); // Set the password
  429. waitResponse();
  430. }
  431. // Define the PDP context
  432. sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
  433. waitResponse();
  434. // Activate the PDP context
  435. sendAT(GF("+CGACT=1,1"));
  436. waitResponse(60000L);
  437. // Open the definied GPRS bearer context
  438. sendAT(GF("+SAPBR=1,1"));
  439. waitResponse(85000L);
  440. // Query the GPRS bearer context status
  441. sendAT(GF("+SAPBR=2,1"));
  442. if (waitResponse(30000L) != 1)
  443. return false;
  444. // Attach to GPRS
  445. sendAT(GF("+CGATT=1"));
  446. if (waitResponse(60000L) != 1)
  447. return false;
  448. // TODO: wait AT+CGATT?
  449. // Set to multi-IP
  450. sendAT(GF("+CIPMUX=1"));
  451. if (waitResponse() != 1) {
  452. return false;
  453. }
  454. // Put in "quick send" mode (thus no extra "Send OK")
  455. sendAT(GF("+CIPQSEND=1"));
  456. if (waitResponse() != 1) {
  457. return false;
  458. }
  459. // Set to get data manually
  460. sendAT(GF("+CIPRXGET=1"));
  461. if (waitResponse() != 1) {
  462. return false;
  463. }
  464. // Start Task and Set APN, USER NAME, PASSWORD
  465. sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
  466. if (waitResponse(60000L) != 1) {
  467. return false;
  468. }
  469. // Bring Up Wireless Connection with GPRS or CSD
  470. sendAT(GF("+CIICR"));
  471. if (waitResponse(60000L) != 1) {
  472. return false;
  473. }
  474. // Get Local IP Address, only assigned after connection
  475. sendAT(GF("+CIFSR;E0"));
  476. if (waitResponse(10000L) != 1) {
  477. return false;
  478. }
  479. // Configure Domain Name Server (DNS)
  480. sendAT(GF("+CDNSCFG=\"8.8.8.8\",\"8.8.4.4\""));
  481. if (waitResponse() != 1) {
  482. return false;
  483. }
  484. return true;
  485. }
  486. bool gprsDisconnect() {
  487. // Shut the TCP/IP connection
  488. sendAT(GF("+CIPSHUT"));
  489. if (waitResponse(60000L) != 1)
  490. return false;
  491. sendAT(GF("+CGATT=0")); // Deactivate the bearer context
  492. if (waitResponse(60000L) != 1)
  493. return false;
  494. return true;
  495. }
  496. bool isGprsConnected() {
  497. sendAT(GF("+CGATT?"));
  498. if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
  499. return false;
  500. }
  501. int res = stream.readStringUntil('\n').toInt();
  502. waitResponse();
  503. if (res != 1)
  504. return false;
  505. sendAT(GF("+CIFSR;E0")); // Another option is to use AT+CGPADDR=1
  506. if (waitResponse() != 1)
  507. return false;
  508. return true;
  509. }
  510. /*
  511. * IP Address functions
  512. */
  513. String getLocalIP() {
  514. sendAT(GF("+CIFSR;E0"));
  515. String res;
  516. if (waitResponse(10000L, res) != 1) {
  517. return "";
  518. }
  519. res.replace(GSM_NL "OK" GSM_NL, "");
  520. res.replace(GSM_NL, "");
  521. res.trim();
  522. return res;
  523. }
  524. /*
  525. * Phone Call functions
  526. */
  527. bool setGsmBusy(bool busy = true) {
  528. sendAT(GF("+GSMBUSY="), busy ? 1 : 0);
  529. return waitResponse() == 1;
  530. }
  531. bool callAnswer() {
  532. sendAT(GF("A"));
  533. return waitResponse() == 1;
  534. }
  535. // Returns true on pick-up, false on error/busy
  536. bool callNumber(const String& number) {
  537. if (number == GF("last")) {
  538. sendAT(GF("DL"));
  539. } else {
  540. sendAT(GF("D"), number, ";");
  541. }
  542. int status = waitResponse(60000L,
  543. GFP(GSM_OK),
  544. GF("BUSY" GSM_NL),
  545. GF("NO ANSWER" GSM_NL),
  546. GF("NO CARRIER" GSM_NL));
  547. switch (status) {
  548. case 1: return true;
  549. case 2:
  550. case 3: return false;
  551. default: return false;
  552. }
  553. }
  554. bool callHangup() {
  555. sendAT(GF("H"));
  556. return waitResponse() == 1;
  557. }
  558. // 0-9,*,#,A,B,C,D
  559. bool dtmfSend(char cmd, int duration_ms = 100) {
  560. duration_ms = constrain(duration_ms, 100, 1000);
  561. sendAT(GF("+VTD="), duration_ms / 100); // VTD accepts in 1/10 of a second
  562. waitResponse();
  563. sendAT(GF("+VTS="), cmd);
  564. return waitResponse(10000L) == 1;
  565. }
  566. /*
  567. * Messaging functions
  568. */
  569. String sendUSSD(const String& code) {
  570. sendAT(GF("+CMGF=1"));
  571. waitResponse();
  572. sendAT(GF("+CSCS=\"HEX\""));
  573. waitResponse();
  574. sendAT(GF("+CUSD=1,\""), code, GF("\""));
  575. if (waitResponse() != 1) {
  576. return "";
  577. }
  578. if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) {
  579. return "";
  580. }
  581. stream.readStringUntil('"');
  582. String hex = stream.readStringUntil('"');
  583. stream.readStringUntil(',');
  584. int dcs = stream.readStringUntil('\n').toInt();
  585. if (dcs == 15) {
  586. return TinyGsmDecodeHex8bit(hex);
  587. } else if (dcs == 72) {
  588. return TinyGsmDecodeHex16bit(hex);
  589. } else {
  590. return hex;
  591. }
  592. }
  593. bool sendSMS(const String& number, const String& text) {
  594. sendAT(GF("+CMGF=1"));
  595. waitResponse();
  596. //Set GSM 7 bit default alphabet (3GPP TS 23.038)
  597. sendAT(GF("+CSCS=\"GSM\""));
  598. waitResponse();
  599. sendAT(GF("+CMGS=\""), number, GF("\""));
  600. if (waitResponse(GF(">")) != 1) {
  601. return false;
  602. }
  603. stream.print(text);
  604. stream.write((char)0x1A);
  605. stream.flush();
  606. return waitResponse(60000L) == 1;
  607. }
  608. bool sendSMS_UTF16(const String& number, const void* text, size_t len) {
  609. sendAT(GF("+CMGF=1"));
  610. waitResponse();
  611. sendAT(GF("+CSCS=\"HEX\""));
  612. waitResponse();
  613. sendAT(GF("+CSMP=17,167,0,8"));
  614. waitResponse();
  615. sendAT(GF("+CMGS=\""), number, GF("\""));
  616. if (waitResponse(GF(">")) != 1) {
  617. return false;
  618. }
  619. uint16_t* t = (uint16_t*)text;
  620. for (size_t i=0; i<len; i++) {
  621. uint8_t c = t[i] >> 8;
  622. if (c < 0x10) { stream.print('0'); }
  623. stream.print(c, HEX);
  624. c = t[i] & 0xFF;
  625. if (c < 0x10) { stream.print('0'); }
  626. stream.print(c, HEX);
  627. }
  628. stream.write((char)0x1A);
  629. stream.flush();
  630. return waitResponse(60000L) == 1;
  631. }
  632. /*
  633. * Location functions
  634. */
  635. String getGsmLocation() {
  636. sendAT(GF("+CIPGSMLOC=1,1"));
  637. if (waitResponse(10000L, GF(GSM_NL "+CIPGSMLOC:")) != 1) {
  638. return "";
  639. }
  640. String res = stream.readStringUntil('\n');
  641. waitResponse();
  642. res.trim();
  643. return res;
  644. }
  645. /*
  646. * Time functions
  647. */
  648. String getGSMDateTime(TinyGSMDateTimeFormat format) {
  649. sendAT(GF("+CCLK?"));
  650. if (waitResponse(2000L, GF(GSM_NL "+CCLK: \"")) != 1) {
  651. return "";
  652. }
  653. String res;
  654. switch(format) {
  655. case DATE_FULL:
  656. res = stream.readStringUntil('"');
  657. break;
  658. case DATE_TIME:
  659. streamSkipUntil(',');
  660. res = stream.readStringUntil('"');
  661. break;
  662. case DATE_DATE:
  663. res = stream.readStringUntil(',');
  664. break;
  665. }
  666. return res;
  667. }
  668. /*
  669. * Battery functions
  670. */
  671. // Use: float vBatt = modem.getBattVoltage() / 1000.0;
  672. uint16_t getBattVoltage() {
  673. sendAT(GF("+CBC"));
  674. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  675. return 0;
  676. }
  677. streamSkipUntil(','); // Skip
  678. streamSkipUntil(','); // Skip
  679. uint16_t res = stream.readStringUntil(',').toInt();
  680. waitResponse();
  681. return res;
  682. }
  683. int8_t getBattPercent() {
  684. sendAT(GF("+CBC"));
  685. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  686. return false;
  687. }
  688. stream.readStringUntil(',');
  689. int res = stream.readStringUntil(',').toInt();
  690. waitResponse();
  691. return res;
  692. }
  693. /*
  694. * Client related functions
  695. */
  696. protected:
  697. bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false) {
  698. int rsp;
  699. #if !defined(TINY_GSM_MODEM_SIM900)
  700. sendAT(GF("+CIPSSL="), ssl);
  701. rsp = waitResponse();
  702. if (ssl && rsp != 1) {
  703. return false;
  704. }
  705. #endif
  706. sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port);
  707. rsp = waitResponse(75000L,
  708. GF("CONNECT OK" GSM_NL),
  709. GF("CONNECT FAIL" GSM_NL),
  710. GF("ALREADY CONNECT" GSM_NL),
  711. GF("ERROR" GSM_NL),
  712. GF("CLOSE OK" GSM_NL) // Happens when HTTPS handshake fails
  713. );
  714. return (1 == rsp);
  715. }
  716. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  717. sendAT(GF("+CIPSEND="), mux, ',', len);
  718. if (waitResponse(GF(">")) != 1) {
  719. return 0;
  720. }
  721. stream.write((uint8_t*)buff, len);
  722. stream.flush();
  723. if (waitResponse(GF(GSM_NL "DATA ACCEPT:")) != 1) {
  724. return 0;
  725. }
  726. streamSkipUntil(','); // Skip mux
  727. return stream.readStringUntil('\n').toInt();
  728. }
  729. size_t modemRead(size_t size, uint8_t mux) {
  730. #ifdef TINY_GSM_USE_HEX
  731. sendAT(GF("+CIPRXGET=3,"), mux, ',', size);
  732. if (waitResponse(GF("+CIPRXGET:")) != 1) {
  733. return 0;
  734. }
  735. #else
  736. sendAT(GF("+CIPRXGET=2,"), mux, ',', size);
  737. if (waitResponse(GF("+CIPRXGET:")) != 1) {
  738. return 0;
  739. }
  740. #endif
  741. streamSkipUntil(','); // Skip mode 2/3
  742. streamSkipUntil(','); // Skip mux
  743. size_t len = stream.readStringUntil(',').toInt();
  744. sockets[mux]->sock_available = stream.readStringUntil('\n').toInt();
  745. for (size_t i=0; i<len; i++) {
  746. #ifdef TINY_GSM_USE_HEX
  747. while (stream.available() < 2) { TINY_GSM_YIELD(); }
  748. char buf[4] = { 0, };
  749. buf[0] = stream.read();
  750. buf[1] = stream.read();
  751. char c = strtol(buf, NULL, 16);
  752. #else
  753. while (!stream.available()) { TINY_GSM_YIELD(); }
  754. char c = stream.read();
  755. #endif
  756. sockets[mux]->rx.put(c);
  757. }
  758. waitResponse();
  759. return len;
  760. }
  761. size_t modemGetAvailable(uint8_t mux) {
  762. sendAT(GF("+CIPRXGET=4,"), mux);
  763. size_t result = 0;
  764. if (waitResponse(GF("+CIPRXGET:")) == 1) {
  765. streamSkipUntil(','); // Skip mode 4
  766. streamSkipUntil(','); // Skip mux
  767. result = stream.readStringUntil('\n').toInt();
  768. waitResponse();
  769. }
  770. if (!result) {
  771. sockets[mux]->sock_connected = modemGetConnected(mux);
  772. }
  773. return result;
  774. }
  775. bool modemGetConnected(uint8_t mux) {
  776. sendAT(GF("+CIPSTATUS="), mux);
  777. int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\""));
  778. waitResponse();
  779. return 1 == res;
  780. }
  781. public:
  782. /*
  783. Utilities
  784. */
  785. template<typename... Args>
  786. void sendAT(Args... cmd) {
  787. streamWrite("AT", cmd..., GSM_NL);
  788. stream.flush();
  789. TINY_GSM_YIELD();
  790. //DBG("### AT:", cmd...);
  791. }
  792. // TODO: Optimize this!
  793. uint8_t waitResponse(uint32_t timeout, String& data,
  794. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  795. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  796. {
  797. /*String r1s(r1); r1s.trim();
  798. String r2s(r2); r2s.trim();
  799. String r3s(r3); r3s.trim();
  800. String r4s(r4); r4s.trim();
  801. String r5s(r5); r5s.trim();
  802. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  803. data.reserve(64);
  804. int index = 0;
  805. unsigned long startMillis = millis();
  806. do {
  807. TINY_GSM_YIELD();
  808. while (stream.available() > 0) {
  809. int a = stream.read();
  810. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  811. data += (char)a;
  812. if (r1 && data.endsWith(r1)) {
  813. index = 1;
  814. goto finish;
  815. } else if (r2 && data.endsWith(r2)) {
  816. index = 2;
  817. goto finish;
  818. } else if (r3 && data.endsWith(r3)) {
  819. index = 3;
  820. goto finish;
  821. } else if (r4 && data.endsWith(r4)) {
  822. index = 4;
  823. goto finish;
  824. } else if (r5 && data.endsWith(r5)) {
  825. index = 5;
  826. goto finish;
  827. } else if (data.endsWith(GF(GSM_NL "+CIPRXGET:"))) {
  828. String mode = stream.readStringUntil(',');
  829. if (mode.toInt() == 1) {
  830. int mux = stream.readStringUntil('\n').toInt();
  831. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  832. sockets[mux]->got_data = true;
  833. }
  834. data = "";
  835. } else {
  836. data += mode;
  837. }
  838. } else if (data.endsWith(GF("CLOSED" GSM_NL))) {
  839. int nl = data.lastIndexOf(GSM_NL, data.length()-8);
  840. int coma = data.indexOf(',', nl+2);
  841. int mux = data.substring(nl+2, coma).toInt();
  842. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  843. sockets[mux]->sock_connected = false;
  844. }
  845. data = "";
  846. DBG("### Closed: ", mux);
  847. }
  848. }
  849. } while (millis() - startMillis < timeout);
  850. finish:
  851. if (!index) {
  852. data.trim();
  853. if (data.length()) {
  854. DBG("### Unhandled:", data);
  855. }
  856. data = "";
  857. }
  858. //DBG('<', index, '>');
  859. return index;
  860. }
  861. uint8_t waitResponse(uint32_t timeout,
  862. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  863. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  864. {
  865. String data;
  866. return waitResponse(timeout, data, r1, r2, r3, r4, r5);
  867. }
  868. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  869. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  870. {
  871. return waitResponse(1000, r1, r2, r3, r4, r5);
  872. }
  873. public:
  874. Stream& stream;
  875. protected:
  876. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  877. };
  878. #endif