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.

862 lines
24 KiB

7 years ago
6 years ago
7 years ago
7 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. /**
  2. * @file TinyGsmClientXBee.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef TinyGsmClientXBee_h
  9. #define TinyGsmClientXBee_h
  10. //#define TINY_GSM_DEBUG Serial
  11. #define TINY_GSM_MUX_COUNT 1 // Multi-plexing isn't supported using command mode
  12. #include <TinyGsmCommon.h>
  13. #define GSM_NL "\r"
  14. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  15. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  16. enum SimStatus {
  17. SIM_ERROR = 0,
  18. SIM_READY = 1,
  19. SIM_LOCKED = 2,
  20. };
  21. enum RegStatus {
  22. REG_OK = 0,
  23. REG_UNREGISTERED = 1,
  24. REG_SEARCHING = 2,
  25. REG_DENIED = 3,
  26. REG_UNKNOWN = 4,
  27. };
  28. // These are responses to the HS command to get "hardware series"
  29. enum XBeeType {
  30. XBEE_S6B_WIFI = 0x601, // Digi XBee® Wi-Fi
  31. XBEE_LTE1_VZN = 0xB01, // Digi XBee® Cellular LTE Cat 1
  32. XBEE_3G = 0xB02, // Digi XBee® Cellular 3G
  33. XBEE3_LTE1_ATT = 0xB06, // Digi XBee3™ Cellular LTE CAT 1
  34. XBEE3_LTEM_ATT = 0xB08, // Digi XBee3™ Cellular LTE-M
  35. XBEE3_LTENB = 3, // Digi XBee3™ Cellular NB-IoT -- HS unknown to SRGD
  36. };
  37. class TinyGsmXBee : public TinyGsmModem
  38. {
  39. public:
  40. class GsmClient : public Client
  41. {
  42. friend class TinyGsmXBee;
  43. public:
  44. GsmClient() {}
  45. GsmClient(TinyGsmXBee& modem, uint8_t mux = 0) {
  46. init(&modem, mux);
  47. }
  48. bool init(TinyGsmXBee* modem, uint8_t mux = 0) {
  49. this->at = modem;
  50. this->mux = mux;
  51. sock_connected = false;
  52. at->sockets[mux] = this;
  53. return true;
  54. }
  55. public:
  56. virtual int connect(const char *host, uint16_t port) {
  57. at->streamClear(); // Empty anything remaining in the buffer;
  58. bool sock_connected = false;
  59. if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
  60. sock_connected = at->modemConnect(host, port, mux, false);
  61. at->writeChanges();
  62. at->exitCommand();
  63. }
  64. at->streamClear(); // Empty anything remaining in the buffer;
  65. return sock_connected;
  66. }
  67. virtual int connect(IPAddress ip, uint16_t port) {
  68. at->streamClear(); // Empty anything remaining in the buffer;
  69. bool sock_connected = false;
  70. if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
  71. sock_connected = at->modemConnect(ip, port, mux, false);
  72. at->writeChanges();
  73. at->exitCommand();
  74. }
  75. at->streamClear(); // Empty anything remaining in the buffer;
  76. return sock_connected;
  77. }
  78. // This is a hack to shut the socket by setting the timeout to zero and
  79. // then sending an empty line to the server.
  80. virtual void stop() {
  81. at->streamClear(); // Empty anything remaining in the buffer;
  82. at->commandMode();
  83. at->sendAT(GF("TM0")); // Set socket timeout to 0;
  84. at->waitResponse();
  85. at->writeChanges();
  86. at->exitCommand();
  87. at->modemSend("", 1, mux);
  88. at->commandMode();
  89. at->sendAT(GF("TM64")); // Set socket timeout back to 10 seconds;
  90. at->waitResponse();
  91. at->writeChanges();
  92. at->exitCommand();
  93. at->streamClear(); // Empty anything remaining in the buffer;
  94. sock_connected = false;
  95. }
  96. virtual size_t write(const uint8_t *buf, size_t size) {
  97. TINY_GSM_YIELD();
  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. return at->stream.available();
  110. }
  111. virtual int read(uint8_t *buf, size_t size) {
  112. TINY_GSM_YIELD();
  113. return at->stream.readBytes((char*)buf, size);
  114. }
  115. virtual int read() {
  116. TINY_GSM_YIELD();
  117. return at->stream.read();
  118. }
  119. virtual int peek() { return at->stream.peek(); }
  120. virtual void flush() { at->stream.flush(); }
  121. virtual uint8_t connected() {
  122. if (available()) {
  123. return true;
  124. }
  125. return sock_connected;
  126. }
  127. virtual operator bool() { return connected(); }
  128. /*
  129. * Extended API
  130. */
  131. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  132. private:
  133. TinyGsmXBee* at;
  134. uint8_t mux;
  135. bool sock_connected;
  136. };
  137. class GsmClientSecure : public GsmClient
  138. {
  139. public:
  140. GsmClientSecure() {}
  141. GsmClientSecure(TinyGsmXBee& modem, uint8_t mux = 1)
  142. : GsmClient(modem, mux)
  143. {}
  144. public:
  145. virtual int connect(const char *host, uint16_t port) {
  146. at->streamClear(); // Empty anything remaining in the buffer;
  147. bool sock_connected = false;
  148. if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
  149. sock_connected = at->modemConnect(host, port, mux, true);
  150. at->writeChanges();
  151. at->exitCommand();
  152. }
  153. at->streamClear(); // Empty anything remaining in the buffer;
  154. return sock_connected;
  155. }
  156. virtual int connect(IPAddress ip, uint16_t port) {
  157. at->streamClear(); // Empty anything remaining in the buffer;
  158. bool sock_connected = false;
  159. if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
  160. sock_connected = at->modemConnect(ip, port, mux, true);
  161. at->writeChanges();
  162. at->exitCommand();
  163. }
  164. at->streamClear(); // Empty anything remaining in the buffer;
  165. return sock_connected;
  166. }
  167. };
  168. public:
  169. TinyGsmXBee(Stream& stream)
  170. : TinyGsmModem(stream), stream(stream)
  171. {}
  172. /*
  173. * Basic functions
  174. */
  175. bool init(const char* pin = NULL) {
  176. guardTime = 1100; // Start with a default guard time of 1 second
  177. if (!commandMode(10)) return false; // Try up to 10 times for the init
  178. sendAT(GF("AP0")); // Put in transparent mode
  179. bool ret_val = waitResponse() == 1;
  180. ret_val &= writeChanges();
  181. sendAT(GF("GT64")); // shorten the guard time to 100ms
  182. ret_val &= waitResponse();
  183. ret_val &= writeChanges();
  184. if (ret_val) guardTime = 125;
  185. sendAT(GF("HS")); // Get the "Hardware Series";
  186. String res = readResponse();
  187. char buf[4] = {0,}; // Set up buffer for response
  188. res.toCharArray(buf, 4);
  189. int intRes = strtol(buf, 0, 16);
  190. beeType = (XBeeType)intRes;
  191. exitCommand();
  192. return ret_val;
  193. }
  194. String getModemName() {
  195. return getBeeName();
  196. }
  197. void setBaud(unsigned long baud) {
  198. if (!commandMode()) return;
  199. switch(baud)
  200. {
  201. case 2400: sendAT(GF("BD1")); break;
  202. case 4800: sendAT(GF("BD2")); break;
  203. case 9600: sendAT(GF("BD3")); break;
  204. case 19200: sendAT(GF("BD4")); break;
  205. case 38400: sendAT(GF("BD5")); break;
  206. case 57600: sendAT(GF("BD6")); break;
  207. case 115200: sendAT(GF("BD7")); break;
  208. case 230400: sendAT(GF("BD8")); break;
  209. case 460800: sendAT(GF("BD9")); break;
  210. case 921600: sendAT(GF("BDA")); break;
  211. default: {
  212. DBG(GF("Specified baud rate is unsupported! Setting to 9600 baud."));
  213. sendAT(GF("BD3")); // Set to default of 9600
  214. break;
  215. }
  216. }
  217. waitResponse();
  218. writeChanges();
  219. exitCommand();
  220. }
  221. bool testAT(unsigned long timeout = 10000L) {
  222. for (unsigned long start = millis(); millis() - start < timeout; ) {
  223. if (commandMode())
  224. {
  225. sendAT();
  226. if (waitResponse(200) == 1) {
  227. return true;
  228. }
  229. exitCommand();
  230. }
  231. delay(100);
  232. }
  233. return false;
  234. }
  235. void maintain() {}
  236. bool factoryDefault() {
  237. if (!commandMode()) return false; // Return immediately
  238. sendAT(GF("RE"));
  239. bool ret_val = waitResponse() == 1;
  240. ret_val &= writeChanges();
  241. exitCommand();
  242. return ret_val;
  243. }
  244. String getModemInfo() {
  245. String modemInf = "";
  246. if (!commandMode()) return modemInf; // Try up to 10 times for the init
  247. sendAT(GF("HS")); // Get the "Hardware Series"
  248. modemInf += readResponse();
  249. exitCommand();
  250. return modemInf;
  251. }
  252. bool hasSSL() {
  253. if (beeType == XBEE_S6B_WIFI) return false;
  254. else return true;
  255. }
  256. bool hasWifi() {
  257. if (beeType == XBEE_S6B_WIFI) return true;
  258. else return false;
  259. }
  260. bool hasGPRS() {
  261. if (beeType == XBEE_S6B_WIFI) return false;
  262. else return true;
  263. }
  264. XBeeType getBeeType() {
  265. return beeType;
  266. }
  267. String getBeeName() {
  268. switch (beeType){
  269. case XBEE_S6B_WIFI: return "Digi XBee® Wi-Fi";
  270. case XBEE_LTE1_VZN: return "Digi XBee® Cellular LTE Cat 1";
  271. case XBEE_3G: return "Digi XBee® Cellular 3G";
  272. case XBEE3_LTE1_ATT: return "Digi XBee3™ Cellular LTE CAT 1";
  273. case XBEE3_LTEM_ATT: return "Digi XBee3™ Cellular LTE-M";
  274. case XBEE3_LTENB: return "Digi XBee3™ Cellular NB-IoT";
  275. default: return "Digi XBee®";
  276. }
  277. }
  278. /*
  279. * Power functions
  280. */
  281. bool restart() {
  282. if (!commandMode()) return false; // Return immediately
  283. sendAT(GF("AM1")); // Digi suggests putting into airplane mode before restarting
  284. // This allows the sockets and connections to close cleanly
  285. writeChanges();
  286. if (waitResponse() != 1) goto fail;
  287. sendAT(GF("FR"));
  288. if (waitResponse() != 1) goto fail;
  289. delay (2000); // Actually resets about 2 seconds later
  290. // Wait until reboot complete and responds to command mode call again
  291. for (unsigned long start = millis(); millis() - start < 60000L; ) {
  292. if (commandMode(1)) {
  293. sendAT(GF("AM0")); // Turn off airplane mode
  294. writeChanges();
  295. exitCommand();
  296. delay(250); // wait a litle before trying again
  297. }
  298. }
  299. return true;
  300. fail:
  301. exitCommand();
  302. return false;
  303. }
  304. void setupPinSleep(bool maintainAssociation = false) {
  305. if (!commandMode()) return; // Return immediately
  306. sendAT(GF("SM"),1); // Pin sleep
  307. waitResponse();
  308. if (beeType == XBEE_S6B_WIFI && !maintainAssociation) {
  309. sendAT(GF("SO"),200); // For lowest power, dissassociated deep sleep
  310. waitResponse();
  311. }
  312. else if (!maintainAssociation){
  313. sendAT(GF("SO"),1); // For lowest power, dissassociated deep sleep
  314. // Not supported by all modules, will return "ERROR"
  315. waitResponse();
  316. }
  317. writeChanges();
  318. exitCommand();
  319. }
  320. bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  321. bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  322. bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  323. /*
  324. * SIM card functions
  325. */
  326. bool simUnlock(const char *pin) { // Not supported
  327. return false;
  328. }
  329. String getSimCCID() {
  330. if (!commandMode()) return ""; // Return immediately
  331. sendAT(GF("S#"));
  332. String res = readResponse();
  333. exitCommand();
  334. return res;
  335. }
  336. String getIMEI() {
  337. if (!commandMode()) return ""; // Return immediately
  338. sendAT(GF("IM"));
  339. String res = readResponse();
  340. exitCommand();
  341. return res;
  342. }
  343. SimStatus getSimStatus(unsigned long timeout = 10000L) {
  344. return SIM_READY; // unsupported
  345. }
  346. RegStatus getRegistrationStatus() {
  347. if (!commandMode()) return REG_UNKNOWN; // Return immediately
  348. sendAT(GF("AI"));
  349. String res = readResponse();
  350. char buf[3] = {0,}; // Set up buffer for response
  351. res.toCharArray(buf, 3);
  352. int intRes = strtol(buf, 0, 16);
  353. RegStatus stat = REG_UNKNOWN;
  354. switch (beeType){
  355. case XBEE_S6B_WIFI: {
  356. if(intRes == 0x00) // 0x00 Successfully joined an access point, established IP addresses and IP listening sockets
  357. stat = REG_OK;
  358. else if(intRes == 0x01) // 0x01 Wi-Fi transceiver initialization in progress.
  359. stat = REG_SEARCHING;
  360. else if(intRes == 0x02) // 0x02 Wi-Fi transceiver initialized, but not yet scanning for access point.
  361. stat = REG_SEARCHING;
  362. else if(intRes == 0x13) { // 0x13 Disconnecting from access point.
  363. restart(); // Restart the device; the S6B tends to get stuck "disconnecting"
  364. stat = REG_UNREGISTERED;
  365. }
  366. else if(intRes == 0x23) // 0x23 SSID not configured.
  367. stat = REG_UNREGISTERED;
  368. else if(intRes == 0x24) // 0x24 Encryption key invalid (either NULL or invalid length for WEP).
  369. stat = REG_DENIED;
  370. else if(intRes == 0x27) // 0x27 SSID was found, but join failed.
  371. stat = REG_DENIED;
  372. else if(intRes == 0x40) // 0x40 Waiting for WPA or WPA2 Authentication.
  373. stat = REG_SEARCHING;
  374. else if(intRes == 0x41) // 0x41 Device joined a network and is waiting for IP configuration to complete
  375. stat = REG_SEARCHING;
  376. else if(intRes == 0x42) // 0x42 Device is joined, IP is configured, and listening sockets are being set up.
  377. stat = REG_SEARCHING;
  378. else if(intRes == 0xFF) // 0xFF Device is currently scanning for the configured SSID.
  379. stat = REG_SEARCHING;
  380. else stat = REG_UNKNOWN;
  381. break;
  382. }
  383. default: {
  384. if(intRes == 0x00) // 0x00 Connected to the Internet.
  385. stat = REG_OK;
  386. else if(intRes == 0x22) // 0x22 Registering to cellular network.
  387. stat = REG_SEARCHING;
  388. else if(intRes == 0x23) // 0x23 Connecting to the Internet.
  389. stat = REG_SEARCHING;
  390. else if(intRes == 0x24) // 0x24 The cellular component is missing, corrupt, or otherwise in error.
  391. stat = REG_UNKNOWN;
  392. else if(intRes == 0x25) // 0x25 Cellular network registration denied.
  393. stat = REG_DENIED;
  394. else if(intRes == 0x2A) { // 0x2A Airplane mode.
  395. sendAT(GF("AM0")); // Turn off airplane mode
  396. waitResponse();
  397. writeChanges();
  398. stat = REG_UNKNOWN;
  399. }
  400. else if(intRes == 0x2F) { // 0x2F Bypass mode active.
  401. sendAT(GF("AP0")); // Set back to transparent mode
  402. waitResponse();
  403. writeChanges();
  404. stat = REG_UNKNOWN;
  405. }
  406. else if(intRes == 0xFF) // 0xFF Device is currently scanning for the configured SSID.
  407. stat = REG_SEARCHING;
  408. else stat = REG_UNKNOWN;
  409. break;
  410. }
  411. }
  412. exitCommand();
  413. return stat;
  414. }
  415. String getOperator() {
  416. if (!commandMode()) return ""; // Return immediately
  417. sendAT(GF("MN"));
  418. String res = readResponse();
  419. exitCommand();
  420. return res;
  421. }
  422. /*
  423. * Generic network functions
  424. */
  425. int getSignalQuality() {
  426. if (!commandMode()) return 0; // Return immediately
  427. if (beeType == XBEE_S6B_WIFI) sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity
  428. else sendAT(GF("DB")); // ask for the cell strength in dBm
  429. String res = readResponse(); // it works better if we read in as a string
  430. exitCommand();
  431. char buf[3] = {0,}; // Set up buffer for response
  432. res.toCharArray(buf, 3);
  433. int intRes = strtol(buf, 0, 16);
  434. if (beeType == XBEE_S6B_WIFI) return -93 + intRes; // the maximum sensitivity is -93dBm
  435. else return -1*intRes; // need to convert to negative number
  436. }
  437. bool isNetworkConnected() {
  438. RegStatus s = getRegistrationStatus();
  439. return (s == REG_OK);
  440. }
  441. bool waitForNetwork(unsigned long timeout = 60000L) {
  442. for (unsigned long start = millis(); millis() - start < timeout; ) {
  443. if (isNetworkConnected()) {
  444. return true;
  445. }
  446. // delay(250); // Enough delay going in and out of command mode
  447. }
  448. return false;
  449. }
  450. /*
  451. * WiFi functions
  452. */
  453. bool networkConnect(const char* ssid, const char* pwd) {
  454. if (!commandMode()) return false; // return immediately
  455. sendAT(GF("EE"), 2); // Set security to WPA2
  456. if (waitResponse() != 1) goto fail;
  457. sendAT(GF("ID"), ssid);
  458. if (waitResponse() != 1) goto fail;
  459. sendAT(GF("PK"), pwd);
  460. if (waitResponse() != 1) goto fail;
  461. writeChanges();
  462. exitCommand();
  463. return true;
  464. fail:
  465. exitCommand();
  466. return false;
  467. }
  468. bool networkDisconnect() {
  469. if (!commandMode()) return false; // return immediately
  470. sendAT(GF("NR0")); // Do a network reset in order to disconnect
  471. int res = (1 == waitResponse(5000));
  472. writeChanges();
  473. exitCommand();
  474. return res;
  475. }
  476. /*
  477. * IP Address functions
  478. */
  479. String getLocalIP() {
  480. if (!commandMode()) return ""; // Return immediately
  481. sendAT(GF("MY"));
  482. String IPaddr; IPaddr.reserve(16);
  483. // wait for the response - this response can be very slow
  484. IPaddr = readResponse(30000);
  485. exitCommand();
  486. IPaddr.trim();
  487. return IPaddr;
  488. }
  489. /*
  490. * GPRS functions
  491. */
  492. bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
  493. if (!commandMode()) return false; // Return immediately
  494. sendAT(GF("AN"), apn); // Set the APN
  495. waitResponse();
  496. writeChanges();
  497. exitCommand();
  498. return true;
  499. }
  500. bool gprsDisconnect() {
  501. if (!commandMode()) return false; // return immediately
  502. sendAT(GF("AM1")); // Cheating and disconnecting by turning on airplane mode
  503. int res = (1 == waitResponse(5000));
  504. writeChanges();
  505. sendAT(GF("AM0")); // Airplane mode off
  506. waitResponse(5000);
  507. writeChanges();
  508. exitCommand();
  509. return res;
  510. }
  511. bool isGprsConnected() {
  512. return isNetworkConnected();
  513. }
  514. /*
  515. * Messaging functions
  516. */
  517. String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  518. bool sendSMS(const String& number, const String& text) {
  519. if (!commandMode()) return false; // Return immediately
  520. sendAT(GF("IP"), 2); // Put in text messaging mode
  521. if (waitResponse() !=1) goto fail;
  522. sendAT(GF("PH"), number); // Set the phone number
  523. if (waitResponse() !=1) goto fail;
  524. sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriage return)
  525. if (waitResponse() !=1) goto fail;
  526. if (!writeChanges()) goto fail;
  527. exitCommand();
  528. streamWrite(text);
  529. stream.write((char)0x0D); // close off with the carriage return
  530. return true;
  531. fail:
  532. exitCommand();
  533. return false;
  534. }
  535. /*
  536. * Location functions
  537. */
  538. String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
  539. /*
  540. * Battery functions
  541. */
  542. uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
  543. int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
  544. /*
  545. * Client related functions
  546. */
  547. protected:
  548. bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false) {
  549. String strIP; strIP.reserve(16);
  550. unsigned long startMillis = millis();
  551. bool gotIP = false;
  552. // XBee's require a numeric IP address for connection, but do provide the
  553. // functionality to look up the IP address from a fully qualified domain name
  554. while (!gotIP && millis() - startMillis < 45000L) // the lookup can take a while
  555. {
  556. sendAT(GF("LA"), host);
  557. while (stream.available() < 4) {}; // wait for any response
  558. strIP = stream.readStringUntil('\r'); // read result
  559. strIP.trim();
  560. //DBG("<<< ", strIP);
  561. if (!strIP.endsWith(GF("ERROR"))) gotIP = true;
  562. delay(100); // short wait before trying again
  563. }
  564. if (gotIP) { // No reason to continue if we don't know the IP address
  565. IPAddress ip = TinyGsmIpFromString(strIP);
  566. return modemConnect(ip, port, mux, ssl);
  567. }
  568. else return false;
  569. }
  570. bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false) {
  571. bool success = true;
  572. String host; host.reserve(16);
  573. host += ip[0];
  574. host += ".";
  575. host += ip[1];
  576. host += ".";
  577. host += ip[2];
  578. host += ".";
  579. host += ip[3];
  580. if (ssl) {
  581. sendAT(GF("IP"), 4); // Put in SSL over TCP communication mode
  582. success &= (1 == waitResponse());
  583. } else {
  584. sendAT(GF("IP"), 1); // Put in TCP mode
  585. success &= (1 == waitResponse());
  586. }
  587. sendAT(GF("DL"), host); // Set the "Destination Address Low"
  588. success &= (1 == waitResponse());
  589. sendAT(GF("DE"), String(port, HEX)); // Set the destination port
  590. success &= (1 == waitResponse());
  591. return success;
  592. }
  593. int modemSend(const void* buff, size_t len, uint8_t mux = 0) {
  594. stream.write((uint8_t*)buff, len);
  595. stream.flush();
  596. return len;
  597. }
  598. bool modemGetConnected(uint8_t mux = 0) {
  599. if (!commandMode()) return false;
  600. sendAT(GF("AI"));
  601. int res = waitResponse(GF("0"));
  602. exitCommand();
  603. return 1 == res;
  604. }
  605. public:
  606. /*
  607. Utilities
  608. */
  609. void streamClear(void) {
  610. TINY_GSM_YIELD();
  611. while (stream.available()) { stream.read(); }
  612. }
  613. template<typename... Args>
  614. void sendAT(Args... cmd) {
  615. streamWrite("AT", cmd..., GSM_NL);
  616. stream.flush();
  617. TINY_GSM_YIELD();
  618. //DBG("### AT:", cmd...);
  619. }
  620. // TODO: Optimize this!
  621. uint8_t waitResponse(uint32_t timeout, String& data,
  622. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  623. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  624. {
  625. /*String r1s(r1); r1s.trim();
  626. String r2s(r2); r2s.trim();
  627. String r3s(r3); r3s.trim();
  628. String r4s(r4); r4s.trim();
  629. String r5s(r5); r5s.trim();
  630. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  631. data.reserve(16); // Should never be getting much here for the XBee
  632. int index = 0;
  633. unsigned long startMillis = millis();
  634. do {
  635. TINY_GSM_YIELD();
  636. while (stream.available() > 0) {
  637. int a = stream.read();
  638. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  639. data += (char)a;
  640. if (r1 && data.endsWith(r1)) {
  641. index = 1;
  642. goto finish;
  643. } else if (r2 && data.endsWith(r2)) {
  644. index = 2;
  645. goto finish;
  646. } else if (r3 && data.endsWith(r3)) {
  647. index = 3;
  648. goto finish;
  649. } else if (r4 && data.endsWith(r4)) {
  650. index = 4;
  651. goto finish;
  652. } else if (r5 && data.endsWith(r5)) {
  653. index = 5;
  654. goto finish;
  655. }
  656. }
  657. } while (millis() - startMillis < timeout);
  658. finish:
  659. if (!index) {
  660. data.trim();
  661. data.replace(GSM_NL GSM_NL, GSM_NL);
  662. data.replace(GSM_NL, "\r\n ");
  663. if (data.length()) {
  664. DBG("### Unhandled:", data, "\r\n");
  665. } else {
  666. DBG("### NO RESPONSE FROM MODEM!\r\n");
  667. }
  668. } else {
  669. data.trim();
  670. data.replace(GSM_NL GSM_NL, GSM_NL);
  671. data.replace(GSM_NL, "\r\n ");
  672. if (data.length()) {
  673. //DBG("<<< ", data);
  674. }
  675. }
  676. //DBG('<', index, '>');
  677. return index;
  678. }
  679. uint8_t waitResponse(uint32_t timeout,
  680. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  681. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  682. {
  683. String data;
  684. return waitResponse(timeout, data, r1, r2, r3, r4, r5);
  685. }
  686. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  687. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  688. {
  689. return waitResponse(1000, r1, r2, r3, r4, r5);
  690. }
  691. bool commandMode(int retries = 2) {
  692. int triesMade = 0;
  693. bool success = false;
  694. streamClear(); // Empty everything in the buffer before starting
  695. while (!success and triesMade < retries) {
  696. // Cannot send anything for 1 "guard time" before entering command mode
  697. // Default guard time is 1s, but the init fxn decreases it to 250 ms
  698. delay(guardTime);
  699. streamWrite(GF("+++")); // enter command mode
  700. //DBG("+++");
  701. success = (1 == waitResponse(guardTime*2));
  702. triesMade ++;
  703. }
  704. return success;
  705. }
  706. bool writeChanges(void) {
  707. sendAT(GF("WR")); // Write changes to flash
  708. if (1 != waitResponse()) return false;
  709. sendAT(GF("AC")); // Apply changes
  710. if (1 != waitResponse()) return false;
  711. return true;
  712. }
  713. void exitCommand(void) {
  714. sendAT(GF("CN")); // Exit command mode
  715. waitResponse();
  716. }
  717. String readResponse(uint32_t timeout = 1000) {
  718. TINY_GSM_YIELD();
  719. unsigned long startMillis = millis();
  720. while (!stream.available() && millis() - startMillis < timeout) {};
  721. String res = stream.readStringUntil('\r'); // lines end with carriage returns
  722. res.trim();
  723. //DBG("<<< ", res);
  724. return res;
  725. }
  726. public:
  727. Stream& stream;
  728. protected:
  729. int guardTime;
  730. XBeeType beeType;
  731. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  732. };
  733. #endif