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.

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