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.

1408 lines
41 KiB

7 years ago
7 years ago
5 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
5 years ago
  1. /**
  2. * @file TinyGsmClientXBee.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy, XBee module by Sara Damiano
  6. * @date Nov 2016
  7. */
  8. #ifndef TinyGsmClientXBee_h
  9. #define TinyGsmClientXBee_h
  10. //#pragma message("TinyGSM: TinyGsmClientXBee")
  11. //#define TINY_GSM_DEBUG Serial
  12. // XBee's do not support multi-plexing in transparent/command mode
  13. // The much more complicated API mode is needed for multi-plexing
  14. #define TINY_GSM_MUX_COUNT 1
  15. // XBee's have a default guard time of 1 second (1000ms, 10 extra for safety here)
  16. #define TINY_GSM_XBEE_GUARD_TIME 1010
  17. #include <TinyGsmCommon.h>
  18. #define GSM_NL "\r"
  19. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  20. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  21. // Use this to avoid too many entrances and exits from command mode.
  22. // The cellular Bee's often freeze up and won't respond when attempting
  23. // to enter command mode too many times.
  24. #define XBEE_COMMAND_START_DECORATOR(nAttempts, failureReturn) \
  25. bool wasInCommandMode = inCommandMode; \
  26. if (!wasInCommandMode) { /* don't re-enter command mode if already in it */ \
  27. if (!commandMode(nAttempts)) return failureReturn; /* Return immediately if fails */ \
  28. }
  29. #define XBEE_COMMAND_END_DECORATOR \
  30. if (!wasInCommandMode) { /* only exit if we weren't in command mode */ \
  31. exitCommand(); \
  32. }
  33. enum SimStatus {
  34. SIM_ERROR = 0,
  35. SIM_READY = 1,
  36. SIM_LOCKED = 2,
  37. };
  38. enum RegStatus {
  39. REG_OK = 0,
  40. REG_UNREGISTERED = 1,
  41. REG_SEARCHING = 2,
  42. REG_DENIED = 3,
  43. REG_UNKNOWN = 4,
  44. };
  45. // These are responses to the HS command to get "hardware series"
  46. enum XBeeType {
  47. XBEE_UNKNOWN = 0,
  48. XBEE_S6B_WIFI = 0x601, // Digi XBee® Wi-Fi
  49. XBEE_LTE1_VZN = 0xB01, // Digi XBee® Cellular LTE Cat 1
  50. XBEE_3G = 0xB02, // Digi XBee® Cellular 3G
  51. XBEE3_LTE1_ATT = 0xB06, // Digi XBee3™ Cellular LTE CAT 1
  52. XBEE3_LTEM_ATT = 0xB08, // Digi XBee3™ Cellular LTE-M
  53. };
  54. class TinyGsmXBee
  55. {
  56. public:
  57. class GsmClient : public Client
  58. {
  59. friend class TinyGsmXBee;
  60. // typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
  61. public:
  62. GsmClient() {}
  63. GsmClient(TinyGsmXBee& modem, uint8_t mux = 0) {
  64. init(&modem, mux);
  65. }
  66. virtual ~GsmClient(){}
  67. bool init(TinyGsmXBee* modem, uint8_t mux = 0) {
  68. this->at = modem;
  69. this->mux = mux;
  70. sock_connected = false;
  71. at->sockets[mux] = this;
  72. return true;
  73. }
  74. public:
  75. // NOTE: The XBee saves all connection information (ssid/pwd or apn AND last used IP address)
  76. // in flash (NVM). When you turn it on it immediately prepares to re-connect to whatever was
  77. // last set. The TCP connection itself is not opened until you attempt to send data.
  78. // Because all settings are saved to flash, it is possible (or likely) that
  79. // you could send data even if you haven't "made" any connection.
  80. virtual int connect(const char *host, uint16_t port, int timeout_s) {
  81. // NOTE: Not caling stop() or yeild() here
  82. at->streamClear(); // Empty anything in the buffer before starting
  83. sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
  84. return sock_connected;
  85. }
  86. virtual int connect(const char *host, uint16_t port) {
  87. return connect(host, port, 75);
  88. }
  89. virtual int connect(IPAddress ip, uint16_t port, int timeout_s) {
  90. if (timeout_s != 0) {
  91. DBG("Timeout [", timeout_s, "] doesn't apply here.");
  92. }
  93. // NOTE: Not caling stop() or yeild() here
  94. at->streamClear(); // Empty anything in the buffer before starting
  95. sock_connected = at->modemConnect(ip, port, mux, false);
  96. return sock_connected;
  97. }
  98. virtual int connect(IPAddress ip, uint16_t port) {
  99. return connect(ip, port, 0);
  100. }
  101. virtual void stop(uint32_t maxWaitMs) {
  102. at->streamClear(); // Empty anything in the buffer
  103. // empty the saved currently-in-use destination address
  104. at->modemStop(maxWaitMs);
  105. at->streamClear(); // Empty anything in the buffer
  106. sock_connected = false;
  107. // Note: because settings are saved in flash, the XBEE will attempt to
  108. // reconnect to the previous socket if it receives any outgoing data.
  109. // Setting sock_connected to false after the stop ensures that connected()
  110. // will return false after a stop has been ordered. This makes it play
  111. // much more nicely with libraries like PubSubClient.
  112. }
  113. virtual void stop() { stop(5000L); }
  114. virtual size_t write(const uint8_t *buf, size_t size) {
  115. TINY_GSM_YIELD();
  116. return at->modemSend(buf, size, mux);
  117. }
  118. virtual size_t write(uint8_t c) {
  119. return write(&c, 1);
  120. }
  121. virtual size_t write(const char *str) {
  122. if (str == NULL) return 0;
  123. return write((const uint8_t *)str, strlen(str));
  124. }
  125. virtual int available() {
  126. TINY_GSM_YIELD();
  127. return at->stream.available();
  128. /*
  129. if (!rx.size() || at->stream.available()) {
  130. at->maintain();
  131. }
  132. return at->stream.available() + rx.size();
  133. */
  134. }
  135. virtual int read(uint8_t *buf, size_t size) {
  136. TINY_GSM_YIELD();
  137. return at->stream.readBytes((char *)buf, size);
  138. /*
  139. size_t cnt = 0;
  140. uint32_t _startMillis = millis();
  141. while (cnt < size && millis() - _startMillis < _timeout) {
  142. size_t chunk = TinyGsmMin(size-cnt, rx.size());
  143. if (chunk > 0) {
  144. rx.get(buf, chunk);
  145. buf += chunk;
  146. cnt += chunk;
  147. continue;
  148. }
  149. // TODO: Read directly into user buffer?
  150. if (!rx.size() || at->stream.available()) {
  151. at->maintain();
  152. }
  153. }
  154. return cnt;
  155. */
  156. }
  157. virtual int read() {
  158. TINY_GSM_YIELD();
  159. return at->stream.read();
  160. /*
  161. uint8_t c;
  162. if (read(&c, 1) == 1) {
  163. return c;
  164. }
  165. return -1;
  166. */
  167. }
  168. virtual int peek() { return at->stream.peek(); }
  169. virtual void flush() { at->stream.flush(); }
  170. virtual uint8_t connected() {
  171. if (available()) {
  172. return true;
  173. // if we never got an IP, it can't be connected
  174. } else if (at->savedIP == IPAddress(0, 0, 0, 0)){
  175. return false;
  176. }
  177. return sock_connected;
  178. // NOTE: We don't check or return
  179. // modemGetConnected() because we don't
  180. // want to go into command mode.
  181. // return at->modemGetConnected();
  182. }
  183. virtual operator bool() { return connected(); }
  184. /*
  185. * Extended API
  186. */
  187. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  188. private:
  189. TinyGsmXBee* at;
  190. uint8_t mux;
  191. bool sock_connected;
  192. // RxFifo rx;
  193. };
  194. class GsmClientSecure : public GsmClient
  195. {
  196. public:
  197. GsmClientSecure() {}
  198. GsmClientSecure(TinyGsmXBee& modem, uint8_t mux = 0)
  199. : GsmClient(modem, mux)
  200. {}
  201. virtual ~GsmClientSecure(){}
  202. public:
  203. virtual int connect(const char *host, uint16_t port, int timeout_s) {
  204. // NOTE: Not caling stop() or yeild() here
  205. at->streamClear(); // Empty anything in the buffer before starting
  206. sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
  207. return sock_connected;
  208. }
  209. virtual int connect(IPAddress ip, uint16_t port, int timeout_s) {
  210. if (timeout_s != 0) {
  211. DBG("Timeout [", timeout_s, "] doesn't apply here.");
  212. }
  213. // NOTE: Not caling stop() or yeild() here
  214. at->streamClear(); // Empty anything in the buffer before starting
  215. sock_connected = at->modemConnect(ip, port, mux, true);
  216. return sock_connected;
  217. }
  218. };
  219. public:
  220. TinyGsmXBee(Stream& stream)
  221. : stream(stream)
  222. {
  223. beeType = XBEE_UNKNOWN; // Start not knowing what kind of bee it is
  224. guardTime = TINY_GSM_XBEE_GUARD_TIME; // Start with the default guard time of 1 second
  225. resetPin = -1;
  226. savedIP = IPAddress(0,0,0,0);
  227. savedHost = "";
  228. savedHostIP = IPAddress(0,0,0,0);
  229. inCommandMode = false;
  230. memset(sockets, 0, sizeof(sockets));
  231. }
  232. TinyGsmXBee(Stream& stream, int8_t resetPin)
  233. : stream(stream)
  234. {
  235. beeType = XBEE_UNKNOWN; // Start not knowing what kind of bee it is
  236. guardTime = TINY_GSM_XBEE_GUARD_TIME; // Start with the default guard time of 1 second
  237. this->resetPin = resetPin;
  238. savedIP = IPAddress(0,0,0,0);
  239. savedHost = "";
  240. savedHostIP = IPAddress(0,0,0,0);
  241. inCommandMode = false;
  242. memset(sockets, 0, sizeof(sockets));
  243. }
  244. virtual ~TinyGsmXBee() {}
  245. /*
  246. * Basic functions
  247. */
  248. bool begin(const char* pin = NULL) {
  249. return init(pin);
  250. }
  251. bool init(const char* pin = NULL) {
  252. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  253. if (resetPin >= 0) {
  254. pinMode(resetPin, OUTPUT);
  255. digitalWrite(resetPin, HIGH);
  256. }
  257. if (pin && strlen(pin) > 0) {
  258. DBG("XBee's do not support SIMs that require an unlock pin!");
  259. }
  260. XBEE_COMMAND_START_DECORATOR(10, false)
  261. sendAT(GF("AP0")); // Put in transparent mode
  262. bool ret_val = waitResponse() == 1;
  263. sendAT(GF("GT64")); // shorten the guard time to 100ms
  264. ret_val &= waitResponse() == 1;
  265. if (ret_val) guardTime = 110;
  266. // Make sure the command mode drop-out time is long enough that we won't fall
  267. // out of command mode without intentionally leaving it. This is the default
  268. // drop out time of 0x64 x 100ms (10 seconds)
  269. sendAT(GF("CT64"));
  270. ret_val &= waitResponse() == 1;
  271. ret_val &= writeChanges();
  272. getSeries(); // Get the "Hardware Series";
  273. XBEE_COMMAND_END_DECORATOR
  274. return ret_val;
  275. }
  276. String getModemName() {
  277. return getBeeName();
  278. }
  279. void setBaud(unsigned long baud) {
  280. XBEE_COMMAND_START_DECORATOR(5, )
  281. switch(baud)
  282. {
  283. case 2400: sendAT(GF("BD1")); break;
  284. case 4800: sendAT(GF("BD2")); break;
  285. case 9600: sendAT(GF("BD3")); break;
  286. case 19200: sendAT(GF("BD4")); break;
  287. case 38400: sendAT(GF("BD5")); break;
  288. case 57600: sendAT(GF("BD6")); break;
  289. case 115200: sendAT(GF("BD7")); break;
  290. case 230400: sendAT(GF("BD8")); break;
  291. case 460800: sendAT(GF("BD9")); break;
  292. case 921600: sendAT(GF("BDA")); break;
  293. default: {
  294. DBG(GF("Specified baud rate is unsupported! Setting to 9600 baud."));
  295. sendAT(GF("BD3")); // Set to default of 9600
  296. break;
  297. }
  298. }
  299. waitResponse();
  300. writeChanges();
  301. XBEE_COMMAND_END_DECORATOR
  302. }
  303. bool testAT(unsigned long timeout_ms = 10000L) {
  304. unsigned long start = millis();
  305. bool success = false;
  306. while (!success && millis() - start < timeout_ms) {
  307. if (!inCommandMode) {
  308. success = commandMode();
  309. if (success) exitCommand();
  310. }
  311. else {
  312. sendAT();
  313. if (waitResponse(200) == 1) {
  314. success = true;
  315. }
  316. // if we didn't respond to the AT, assume we're not in command mode
  317. else inCommandMode = false;
  318. }
  319. delay(250);
  320. }
  321. return success;
  322. }
  323. void maintain() {
  324. // this only happens OUTSIDE command mode, so if we're getting characters
  325. // they should be data received from the TCP connection
  326. // TINY_GSM_YIELD();
  327. // if (!inCommandMode) {
  328. // while (stream.available()) {
  329. // char c = stream.read();
  330. // if (c > 0) sockets[0]->rx.put(c);
  331. // }
  332. // }
  333. }
  334. bool factoryDefault() {
  335. XBEE_COMMAND_START_DECORATOR(5, false)
  336. sendAT(GF("RE"));
  337. bool ret_val = waitResponse() == 1;
  338. ret_val &= writeChanges();
  339. XBEE_COMMAND_END_DECORATOR
  340. // Make sure the guard time for the modem object is set back to default
  341. // otherwise communication would fail after the reset
  342. guardTime = 1010;
  343. return ret_val;
  344. }
  345. String getModemInfo() {
  346. return sendATGetString(GF("HS"));
  347. }
  348. bool hasSSL() {
  349. if (beeType == XBEE_S6B_WIFI) return false;
  350. else return true;
  351. }
  352. bool hasWifi() {
  353. if (beeType == XBEE_S6B_WIFI) return true;
  354. else return false;
  355. }
  356. bool hasGPRS() {
  357. if (beeType == XBEE_S6B_WIFI) return false;
  358. else return true;
  359. }
  360. XBeeType getBeeType() {
  361. return beeType;
  362. }
  363. String getBeeName() {
  364. switch (beeType){
  365. case XBEE_S6B_WIFI: return "Digi XBee Wi-Fi";
  366. case XBEE_LTE1_VZN: return "Digi XBee Cellular LTE Cat 1";
  367. case XBEE_3G: return "Digi XBee Cellular 3G";
  368. case XBEE3_LTE1_ATT: return "Digi XBee3 Cellular LTE CAT 1";
  369. case XBEE3_LTEM_ATT: return "Digi XBee3 Cellular LTE-M";
  370. default: return "Digi XBee";
  371. }
  372. }
  373. /*
  374. * Power functions
  375. */
  376. // The XBee's have a bad habit of getting into an unresponsive funk
  377. // This uses the board's hardware reset pin to force it to reset
  378. void pinReset() {
  379. if (resetPin >= 0) {
  380. DBG("### Forcing a modem reset!\r\n");
  381. digitalWrite(resetPin, LOW);
  382. delay(1);
  383. digitalWrite(resetPin, HIGH);
  384. }
  385. }
  386. bool restart() {
  387. if (!commandMode()) return false; // Return immediately
  388. if (beeType == XBEE_UNKNOWN) getSeries(); // how we restart depends on this
  389. if (beeType != XBEE_S6B_WIFI) {
  390. sendAT(GF("AM1")); // Digi suggests putting cellular modules into airplane mode before restarting
  391. // This allows the sockets and connections to close cleanly
  392. if (waitResponse() != 1) return exitAndFail();
  393. if (!writeChanges()) return exitAndFail();
  394. }
  395. sendAT(GF("FR"));
  396. if (waitResponse() != 1) return exitAndFail();
  397. else inCommandMode = false; // Reset effectively exits command mode
  398. if (beeType == XBEE_S6B_WIFI) delay(2000); // Wifi module actually resets about 2 seconds later
  399. else delay(100); // cellular modules wait 100ms before reset happens
  400. // Wait until reboot complete and responds to command mode call again
  401. for (unsigned long start = millis(); millis() - start < 60000L; ) {
  402. if (commandMode(1)) break;
  403. delay(250); // wait a litle before trying again
  404. }
  405. if (beeType != XBEE_S6B_WIFI) {
  406. sendAT(GF("AM0")); // Turn off airplane mode
  407. if (waitResponse() != 1) return exitAndFail();
  408. if (!writeChanges()) return exitAndFail();
  409. }
  410. exitCommand();
  411. return init();
  412. }
  413. void setupPinSleep(bool maintainAssociation = false) {
  414. XBEE_COMMAND_START_DECORATOR(5, )
  415. if (beeType == XBEE_UNKNOWN) getSeries(); // Command depends on series
  416. sendAT(GF("SM"),1); // Pin sleep
  417. waitResponse();
  418. if (beeType == XBEE_S6B_WIFI && !maintainAssociation) {
  419. sendAT(GF("SO"),200); // For lowest power, dissassociated deep sleep
  420. waitResponse();
  421. }
  422. else if (!maintainAssociation){
  423. sendAT(GF("SO"),1); // For supported cellular modules, maintain association
  424. // Not supported by all modules, will return "ERROR"
  425. waitResponse();
  426. }
  427. writeChanges();
  428. XBEE_COMMAND_END_DECORATOR
  429. }
  430. bool poweroff() { // NOTE: Not supported for WiFi or older cellular firmware
  431. XBEE_COMMAND_START_DECORATOR(5, false)
  432. sendAT(GF("SD"));
  433. bool ret_val = waitResponse(120000L) == 1;
  434. if (ret_val) {
  435. ret_val &= (sendATGetString(GF("AI")) == "2D");
  436. }
  437. XBEE_COMMAND_END_DECORATOR
  438. return ret_val;
  439. }
  440. bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  441. bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  442. /*
  443. * SIM card functions
  444. */
  445. bool simUnlock(const char *pin) { // Not supported
  446. if (pin && strlen(pin) > 0) {
  447. DBG("XBee's do not support SIMs that require an unlock pin!");
  448. }
  449. return false;
  450. }
  451. String getSimCCID() {
  452. return sendATGetString(GF("S#"));
  453. }
  454. String getIMEI() {
  455. return sendATGetString(GF("IM"));
  456. }
  457. SimStatus getSimStatus() {
  458. return SIM_READY; // unsupported
  459. }
  460. RegStatus getRegistrationStatus() {
  461. XBEE_COMMAND_START_DECORATOR(5, REG_UNKNOWN)
  462. if (!inCommandMode) return REG_UNKNOWN; // Return immediately
  463. if (beeType == XBEE_UNKNOWN) getSeries(); // Need to know the bee type to interpret response
  464. sendAT(GF("AI"));
  465. int16_t intRes = readResponseInt(10000L);
  466. RegStatus stat = REG_UNKNOWN;
  467. switch (beeType){
  468. case XBEE_S6B_WIFI: {
  469. switch (intRes) {
  470. case 0x00: // 0x00 Successfully joined an access point, established IP addresses and IP listening sockets
  471. stat = REG_OK;
  472. break;
  473. case 0x01: // 0x01 Wi-Fi transceiver initialization in progress.
  474. case 0x02: // 0x02 Wi-Fi transceiver initialized, but not yet scanning for access point.
  475. case 0x40: // 0x40 Waiting for WPA or WPA2 Authentication.
  476. case 0x41: // 0x41 Device joined a network and is waiting for IP configuration to complete
  477. case 0x42: // 0x42 Device is joined, IP is configured, and listening sockets are being set up.
  478. case 0xFF: // 0xFF Device is currently scanning for the configured SSID.
  479. stat = REG_SEARCHING;
  480. break;
  481. case 0x13: // 0x13 Disconnecting from access point.
  482. restart(); // Restart the device; the S6B tends to get stuck "disconnecting"
  483. stat = REG_UNREGISTERED;
  484. break;
  485. case 0x23: // 0x23 SSID not configured.
  486. stat = REG_UNREGISTERED;
  487. break;
  488. case 0x24: // 0x24 Encryption key invalid (either NULL or invalid length for WEP).
  489. case 0x27: // 0x27 SSID was found, but join failed.
  490. stat = REG_DENIED;
  491. break;
  492. default:
  493. stat = REG_UNKNOWN;
  494. break;
  495. }
  496. break;
  497. }
  498. default: { // Cellular XBee's
  499. switch (intRes) {
  500. case 0x00: // 0x00 Connected to the Internet.
  501. stat = REG_OK;
  502. break;
  503. case 0x22: // 0x22 Registering to cellular network.
  504. case 0x23: // 0x23 Connecting to the Internet.
  505. case 0xFF: // 0xFF Initializing.
  506. stat = REG_SEARCHING;
  507. break;
  508. case 0x24: // 0x24 The cellular component is missing, corrupt, or otherwise in error.
  509. case 0x2B: // 0x2B USB Direct active.
  510. case 0x2C: // 0x2C Cellular component is in PSM (power save mode).
  511. stat = REG_UNKNOWN;
  512. break;
  513. case 0x25: // 0x25 Cellular network registration denied.
  514. stat = REG_DENIED;
  515. break;
  516. case 0x2A: // 0x2A Airplane mode.
  517. sendAT(GF("AM0")); // Turn off airplane mode
  518. waitResponse();
  519. writeChanges();
  520. stat = REG_UNKNOWN;
  521. break;
  522. case 0x2F: // 0x2F Bypass mode active.
  523. sendAT(GF("AP0")); // Set back to transparent mode
  524. waitResponse();
  525. writeChanges();
  526. stat = REG_UNKNOWN;
  527. break;
  528. default:
  529. stat = REG_UNKNOWN;
  530. break;
  531. }
  532. break;
  533. }
  534. }
  535. XBEE_COMMAND_END_DECORATOR
  536. return stat;
  537. }
  538. String getOperator() {
  539. return sendATGetString(GF("MN"));
  540. }
  541. /*
  542. * Generic network functions
  543. */
  544. int16_t getSignalQuality() {
  545. XBEE_COMMAND_START_DECORATOR(5, 0);
  546. if (beeType == XBEE_UNKNOWN) getSeries(); // Need to know what type of bee so we know how to ask
  547. if (beeType == XBEE_S6B_WIFI) sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity
  548. else sendAT(GF("DB")); // ask for the cell strength in dBm
  549. int16_t intRes = readResponseInt();
  550. XBEE_COMMAND_END_DECORATOR
  551. if (beeType == XBEE3_LTEM_ATT && intRes == 105) intRes = 0; // tends to reply with "69" when signal is unknown
  552. if (beeType == XBEE_S6B_WIFI) {
  553. if (intRes == 0xFF) {
  554. return 0; // 0xFF returned for unknown
  555. } else {
  556. return -93 + intRes; // the maximum sensitivity is -93dBm
  557. }
  558. } else {
  559. return -1*intRes; // need to convert to negative number
  560. }
  561. }
  562. bool isNetworkConnected() {
  563. RegStatus s = getRegistrationStatus();
  564. if (s == REG_OK) {
  565. IPAddress ip = localIP();
  566. if (ip != IPAddress(0, 0, 0, 0)) {
  567. return true;
  568. } else {
  569. return false;
  570. }
  571. } else {
  572. return false;
  573. }
  574. }
  575. bool waitForNetwork(unsigned long timeout_ms = 60000L) {
  576. bool retVal = false;
  577. XBEE_COMMAND_START_DECORATOR(5, false)
  578. for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
  579. if (isNetworkConnected()) {
  580. retVal = true;
  581. break;
  582. }
  583. delay(250); // per Neil H. - more stable with delay
  584. }
  585. XBEE_COMMAND_END_DECORATOR
  586. return retVal;
  587. }
  588. /*
  589. * WiFi functions
  590. */
  591. bool networkConnect(const char* ssid, const char* pwd) {
  592. bool retVal = true;
  593. XBEE_COMMAND_START_DECORATOR(5, false)
  594. //nh For no pwd don't set setscurity or pwd
  595. if (ssid == NULL) retVal = false;;
  596. if (pwd && strlen(pwd) > 0)
  597. {
  598. sendAT(GF("EE"), 2); // Set security to WPA2
  599. if (waitResponse() != 1) retVal = false;
  600. sendAT(GF("PK"), pwd);
  601. } else {
  602. sendAT(GF("EE"), 0); // Set No security
  603. }
  604. if (waitResponse() != 1) retVal = false;
  605. sendAT(GF("ID"), ssid);
  606. if (waitResponse() != 1) retVal = false;
  607. if (!writeChanges()) retVal = false;
  608. XBEE_COMMAND_END_DECORATOR
  609. return retVal;
  610. }
  611. bool networkDisconnect() {
  612. XBEE_COMMAND_START_DECORATOR(5, false)
  613. sendAT(GF("NR0")); // Do a network reset in order to disconnect
  614. // WARNING: On wifi modules, using a network reset will not
  615. // allow the same ssid to re-join without rebooting the module.
  616. int8_t res = (1 == waitResponse(5000));
  617. writeChanges();
  618. XBEE_COMMAND_END_DECORATOR
  619. return res;
  620. }
  621. /*
  622. * IP Address functions
  623. */
  624. String getLocalIP() {
  625. XBEE_COMMAND_START_DECORATOR(5, "")
  626. sendAT(GF("MY"));
  627. String IPaddr; IPaddr.reserve(16);
  628. // wait for the response - this response can be very slow
  629. IPaddr = readResponseString(30000);
  630. XBEE_COMMAND_END_DECORATOR
  631. IPaddr.trim();
  632. return IPaddr;
  633. }
  634. IPAddress localIP() {
  635. return TinyGsmIpFromString(getLocalIP());
  636. }
  637. /*
  638. * GPRS functions
  639. */
  640. bool gprsConnect(const char* apn, const char* user = NULL,
  641. const char* pwd = NULL) {
  642. if (user && strlen(user) > 0) {
  643. DBG("XBee's do not support SIMs that a user name/password!");
  644. }
  645. if (pwd && strlen(pwd) > 0) {
  646. DBG("XBee's do not support SIMs that a user name/password!");
  647. }
  648. XBEE_COMMAND_START_DECORATOR(5, false)
  649. sendAT(GF("AN"), apn); // Set the APN
  650. bool success = waitResponse() == 1;
  651. sendAT(GF("AM0")); // Airplane mode off
  652. waitResponse(5000);
  653. writeChanges();
  654. XBEE_COMMAND_END_DECORATOR
  655. return success;
  656. }
  657. bool gprsDisconnect() {
  658. XBEE_COMMAND_START_DECORATOR(5, false)
  659. sendAT(GF("AM1")); // Cheating and disconnecting by turning on airplane mode
  660. int8_t res = (1 == waitResponse(5000));
  661. writeChanges();
  662. // sendAT(GF("AM0")); // Airplane mode off
  663. // waitResponse(5000);
  664. // writeChanges();
  665. XBEE_COMMAND_END_DECORATOR
  666. return res;
  667. }
  668. bool isGprsConnected() {
  669. return isNetworkConnected();
  670. }
  671. /*
  672. * Messaging functions
  673. */
  674. String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  675. bool sendSMS(const String& number, const String& text) {
  676. if (!commandMode()) return false; // Return immediately
  677. sendAT(GF("IP"), 2); // Put in text messaging mode
  678. if (waitResponse() !=1) return exitAndFail();
  679. sendAT(GF("PH"), number); // Set the phone number
  680. if (waitResponse() !=1) return exitAndFail();
  681. sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriage return)
  682. if (waitResponse() !=1) return exitAndFail();
  683. if (!writeChanges()) return exitAndFail();
  684. // Get out of command mode to actually send the text
  685. exitCommand();
  686. streamWrite(text);
  687. stream.write((char)0x0D); // close off with the carriage return
  688. return true;
  689. }
  690. /*
  691. * Location functions
  692. */
  693. String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
  694. /*
  695. * Battery & temperature functions
  696. */
  697. // Use: float vBatt = modem.getBattVoltage() / 1000.0;
  698. uint16_t getBattVoltage() {
  699. int16_t intRes = 0;
  700. XBEE_COMMAND_START_DECORATOR(5, false)
  701. if (beeType == XBEE_UNKNOWN) getSeries();
  702. if (beeType == XBEE_S6B_WIFI) {
  703. sendAT(GF("%V"));
  704. intRes = readResponseInt();
  705. }
  706. XBEE_COMMAND_END_DECORATOR
  707. return intRes;
  708. }
  709. int8_t getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
  710. uint8_t getBattChargeState() TINY_GSM_ATTR_NOT_AVAILABLE;
  711. bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
  712. chargeState = 0;
  713. percent = 0;
  714. milliVolts = getBattVoltage();
  715. return true;
  716. }
  717. float getTemperature() {
  718. XBEE_COMMAND_START_DECORATOR(5, (float)-9999)
  719. String res = sendATGetString(GF("TP"));
  720. if (res == "") {
  721. return (float)-9999;
  722. }
  723. char buf[5] = {0,};
  724. res.toCharArray(buf, 5);
  725. int8_t intRes = (int8_t)strtol(buf, 0, 16); // degrees Celsius displayed in 8-bit two's complement format.
  726. XBEE_COMMAND_END_DECORATOR
  727. return (float)intRes;
  728. }
  729. /*
  730. * Client related functions
  731. */
  732. protected:
  733. int16_t getConnectionIndicator() {
  734. XBEE_COMMAND_START_DECORATOR(5, false)
  735. sendAT(GF("CI"));
  736. int16_t intRes = readResponseInt();
  737. XBEE_COMMAND_END_DECORATOR
  738. return intRes;
  739. }
  740. IPAddress getOperatingIP() {
  741. String strIP;
  742. strIP.reserve(16);
  743. XBEE_COMMAND_START_DECORATOR(5, IPAddress(0, 0, 0, 0))
  744. sendAT(GF("OD"));
  745. strIP = stream.readStringUntil('\r'); // read result
  746. strIP.trim();
  747. XBEE_COMMAND_END_DECORATOR
  748. if (strIP != "" && strIP != GF("ERROR")) {
  749. return TinyGsmIpFromString(strIP);
  750. } else
  751. return IPAddress(0, 0, 0, 0);
  752. }
  753. IPAddress lookupHostIP(const char* host, int timeout_s = 45) {
  754. String strIP;
  755. strIP.reserve(16);
  756. unsigned long startMillis = millis();
  757. uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
  758. bool gotIP = false;
  759. XBEE_COMMAND_START_DECORATOR(5, IPAddress(0,0,0,0))
  760. // XBee's require a numeric IP address for connection, but do provide the
  761. // functionality to look up the IP address from a fully qualified domain name
  762. while ((millis() - startMillis) < timeout_ms) // the lookup can take a while
  763. {
  764. sendAT(GF("LA"), host);
  765. while (stream.available() < 4 && (millis() - startMillis < timeout_ms)) {TINY_GSM_YIELD()};
  766. strIP = stream.readStringUntil('\r'); // read result
  767. strIP.trim();
  768. if (strIP != "" && strIP != GF("ERROR")) {
  769. gotIP = true;
  770. break;
  771. }
  772. delay(2500); // wait a bit before trying again
  773. }
  774. XBEE_COMMAND_END_DECORATOR
  775. if (gotIP) {
  776. return TinyGsmIpFromString(strIP);
  777. }
  778. else return IPAddress(0,0,0,0);
  779. }
  780. bool modemConnect(const char* host, uint16_t port, uint8_t mux = 0,
  781. bool ssl = false, int timeout_s = 75)
  782. {
  783. bool retVal = false;
  784. XBEE_COMMAND_START_DECORATOR(5, false)
  785. // If this is a new host name, replace the saved host and wipe out the saved host IP
  786. if (this->savedHost != String(host)) {
  787. this->savedHost = String(host);
  788. savedHostIP = IPAddress(0,0,0,0);
  789. }
  790. // If we don't have a good IP for the host, we need to do a DNS search
  791. if (savedHostIP == IPAddress(0,0,0,0)) {
  792. savedHostIP = lookupHostIP(host, timeout_s); // This will return 0.0.0.0 if lookup fails
  793. }
  794. // If we now have a valid IP address, use it to connect
  795. if (savedHostIP != IPAddress(0,0,0,0)) { // Only re-set connection information if we have an IP address
  796. retVal = modemConnect(savedHostIP, port, mux, ssl);
  797. }
  798. XBEE_COMMAND_END_DECORATOR
  799. return retVal;
  800. }
  801. bool modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0,
  802. bool ssl = false) {
  803. bool success = true;
  804. if (mux != 0) {
  805. DBG("XBee only supports 1 IP channel in transparent mode!");
  806. }
  807. // empty the saved currelty-in-use destination address
  808. savedOperatingIP = IPAddress(0, 0, 0, 0);
  809. XBEE_COMMAND_START_DECORATOR(5, false)
  810. if (ip != savedIP) { // Can skip almost everything if there's no
  811. // change in the IP address
  812. savedIP = ip; // Set the newly requested IP address
  813. String host; host.reserve(16);
  814. host += ip[0];
  815. host += ".";
  816. host += ip[1];
  817. host += ".";
  818. host += ip[2];
  819. host += ".";
  820. host += ip[3];
  821. if (ssl) {
  822. sendAT(GF("IP"), 4); // Put in SSL over TCP communication mode
  823. success &= (1 == waitResponse());
  824. } else {
  825. sendAT(GF("IP"), 1); // Put in TCP mode
  826. success &= (1 == waitResponse());
  827. }
  828. sendAT(GF("DL"), host); // Set the "Destination Address Low"
  829. success &= (1 == waitResponse());
  830. sendAT(GF("DE"), String(port, HEX)); // Set the destination port
  831. success &= (1 == waitResponse());
  832. success &= writeChanges();
  833. }
  834. // we'll accept either unknown or connected
  835. if (beeType != XBEE_S6B_WIFI) {
  836. uint16_t ci = getConnectionIndicator();
  837. success &= (ci == 0x00 || ci == 0xFF || ci == 0x28);
  838. }
  839. if (success) {
  840. sockets[mux]->sock_connected = true;
  841. }
  842. XBEE_COMMAND_END_DECORATOR
  843. return success;
  844. }
  845. bool modemStop(uint32_t maxWaitMs) {
  846. streamClear(); // Empty anything in the buffer
  847. // empty the saved currently-in-use destination address
  848. savedOperatingIP = IPAddress(0, 0, 0, 0);
  849. XBEE_COMMAND_START_DECORATOR(5, false)
  850. // Get the current socket timeout
  851. sendAT(GF("TM"));
  852. String timeoutUsed = readResponseString(5000L);
  853. // For WiFi models, there's no direct way to close the socket. This is a
  854. // hack to shut the socket by setting the timeout to zero.
  855. if (beeType == XBEE_S6B_WIFI) {
  856. sendAT(GF("TM0")); // Set socket timeout to 0
  857. waitResponse(maxWaitMs); // This response can be slow
  858. writeChanges();
  859. }
  860. // For cellular models, per documentation: If you write the TM (socket
  861. // timeout) value while in Transparent Mode, the current connection is
  862. // immediately closed - this works even if the TM values is unchanged
  863. sendAT(GF("TM"), timeoutUsed); // Re-set socket timeout
  864. waitResponse(maxWaitMs); // This response can be slow
  865. writeChanges();
  866. XBEE_COMMAND_END_DECORATOR
  867. return true;
  868. }
  869. int16_t modemSend(const void* buff, size_t len, uint8_t mux = 0) {
  870. if (mux != 0) {
  871. DBG("XBee only supports 1 IP channel in transparent mode!");
  872. }
  873. stream.write((uint8_t*)buff, len);
  874. stream.flush();
  875. if (beeType != XBEE_S6B_WIFI) {
  876. // After a send, verify the outgoing ip if it isn't set
  877. if (savedOperatingIP == IPAddress(0, 0, 0, 0)) {
  878. modemGetConnected();
  879. }
  880. // After sending several characters, also re-check
  881. // NOTE: I'm intentionally not checking after every single character!
  882. else if (len > 5) {
  883. modemGetConnected();
  884. }
  885. }
  886. return len;
  887. }
  888. // NOTE: The CI command returns the status of the TCP connection as open only
  889. // after data has been sent on the socket. If it returns 0xFF the socket may
  890. // really be open, but no data has yet been sent. We return this unknown value
  891. // as true so there's a possibility it's wrong.
  892. bool modemGetConnected() {
  893. // If the IP address is 0, it's not valid so we can't be connected
  894. if (savedIP == IPAddress(0,0,0,0)) return false;
  895. XBEE_COMMAND_START_DECORATOR(5, false)
  896. if (beeType == XBEE_UNKNOWN) getSeries(); // Need to know the bee type to interpret response
  897. switch (beeType){
  898. // The wifi be can only say if it's connected to the netowrk
  899. case XBEE_S6B_WIFI: {
  900. RegStatus s = getRegistrationStatus();
  901. XBEE_COMMAND_END_DECORATOR
  902. if (s != REG_OK) {
  903. sockets[0]->sock_connected = false; // no multiplex
  904. }
  905. return (s == REG_OK); // if it's connected, we hope the sockets are too
  906. }
  907. // Cellular XBee's
  908. default: {
  909. int16_t ci = getConnectionIndicator();
  910. // Get the operating destination address
  911. IPAddress od = getOperatingIP();
  912. XBEE_COMMAND_END_DECORATOR
  913. switch(ci) {
  914. // 0x00 = The socket is definitely open
  915. case 0x00: {
  916. savedOperatingIP = od;
  917. // but it's possible the socket is set to the wrong place
  918. if (od != IPAddress(0, 0, 0, 0) && od != savedIP) {
  919. sockets[0]->stop();
  920. return false;
  921. }
  922. return true;
  923. }
  924. // 0x28 = "Unknown."
  925. // 0xFF = No known status - always returned prior to sending data
  926. case 0x28:
  927. case 0xFF: {
  928. // If we previously had an operating destination and we no longer do,
  929. // the socket must have closed
  930. if (od == IPAddress(0, 0, 0, 0) && savedOperatingIP != IPAddress(0, 0, 0, 0)) {
  931. savedOperatingIP = od;
  932. sockets[0]->sock_connected = false;
  933. return false;
  934. }
  935. // else if the operating destination exists, but is wrong
  936. // we need to close and re-open
  937. else if (od != IPAddress(0, 0, 0, 0) && od != savedIP) {
  938. sockets[0]->stop();
  939. return false;
  940. }
  941. // else if the operating destination exists and matches, we're
  942. // good to go
  943. else if (od != IPAddress(0, 0, 0, 0) && od == savedIP) {
  944. savedOperatingIP = od;
  945. return true;
  946. }
  947. // If we never had an operating destination, then sock may be open
  948. // but data never sent - this is the dreaded "we don't know"
  949. else {
  950. savedOperatingIP = od;
  951. return true;
  952. }
  953. // // Ask for information about any open sockets
  954. // sendAT(GF("SI"));
  955. // String open_socks = stream.readStringUntil('\r');
  956. // open_socks.replace(GSM_NL, "");
  957. // open_socks.trim();
  958. // if (open_socks != "") {
  959. // // In transparent mode, only socket 0 should be possible
  960. // sendAT(GF("SI0"));
  961. // // read socket it
  962. // String sock_id = stream.readStringUntil('\r');
  963. // // read socket state
  964. // String sock_state = stream.readStringUntil('\r');
  965. // // read socket protocol (TCP/UDP)
  966. // String sock_protocol = stream.readStringUntil('\r');
  967. // // read local port number
  968. // String local_port = stream.readStringUntil('\r');
  969. // // read remote port number
  970. // String remote_port = stream.readStringUntil('\r');
  971. // // read remote ip address
  972. // String remoted_address =
  973. // stream.readStringUntil('\r'); // read result
  974. // stream.readStringUntil('\r'); // final carriage return
  975. // }
  976. }
  977. // 0x21 = User closed
  978. // 0x27 = Connection lost
  979. // If the connection is lost or timed out on our side,
  980. // we force close so it can reopen
  981. case 0x21 :
  982. case 0x27 : {
  983. sendAT(GF("TM")); // Get socket timeout
  984. String timeoutUsed = readResponseString(5000L);
  985. sendAT(GF("TM"), timeoutUsed); // Re-set socket timeout
  986. waitResponse(5000L); // This response can be slow
  987. }
  988. // 0x02 = Invalid parameters (bad IP/host)
  989. // 0x12 = DNS query lookup failure
  990. // 0x25 = Unknown server - DNS lookup failed (0x22 for UDP socket!)
  991. // fall through
  992. case 0x02:
  993. case 0x12:
  994. case 0x25: {
  995. savedIP = IPAddress(0, 0, 0, 0); // force a lookup next time!
  996. }
  997. // If it's anything else (inc 0x02, 0x12, and 0x25)...
  998. // it's definitely NOT connected
  999. // fall through
  1000. default: {
  1001. sockets[0]->sock_connected = false;
  1002. savedOperatingIP = od;
  1003. return false;
  1004. }
  1005. }
  1006. }
  1007. }
  1008. }
  1009. public:
  1010. /*
  1011. Utilities
  1012. */
  1013. void streamClear(void) {
  1014. while (stream.available()) {
  1015. stream.read();
  1016. TINY_GSM_YIELD();
  1017. }
  1018. }
  1019. TINY_GSM_MODEM_STREAM_UTILITIES()
  1020. // TODO: Optimize this!
  1021. // NOTE: This function is used while INSIDE command mode, so we're only
  1022. // waiting for requested responses. The XBee has no unsoliliced responses
  1023. // (URC's) when in command mode.
  1024. uint8_t waitResponse(uint32_t timeout_ms, String& data,
  1025. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  1026. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  1027. {
  1028. /*String r1s(r1); r1s.trim();
  1029. String r2s(r2); r2s.trim();
  1030. String r3s(r3); r3s.trim();
  1031. String r4s(r4); r4s.trim();
  1032. String r5s(r5); r5s.trim();
  1033. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  1034. data.reserve(16); // Should never be getting much here for the XBee
  1035. int8_t index = 0;
  1036. unsigned long startMillis = millis();
  1037. do {
  1038. TINY_GSM_YIELD();
  1039. while (stream.available() > 0) {
  1040. TINY_GSM_YIELD();
  1041. int a = stream.read();
  1042. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  1043. data += (char)a;
  1044. if (r1 && data.endsWith(r1)) {
  1045. index = 1;
  1046. goto finish;
  1047. } else if (r2 && data.endsWith(r2)) {
  1048. index = 2;
  1049. goto finish;
  1050. } else if (r3 && data.endsWith(r3)) {
  1051. index = 3;
  1052. goto finish;
  1053. } else if (r4 && data.endsWith(r4)) {
  1054. index = 4;
  1055. goto finish;
  1056. } else if (r5 && data.endsWith(r5)) {
  1057. index = 5;
  1058. goto finish;
  1059. }
  1060. }
  1061. } while (millis() - startMillis < timeout_ms);
  1062. finish:
  1063. if (!index) {
  1064. data.trim();
  1065. data.replace(GSM_NL GSM_NL, GSM_NL);
  1066. data.replace(GSM_NL, "\r\n ");
  1067. if (data.length()) {
  1068. DBG("### Unhandled:", data, "\r\n");
  1069. } else {
  1070. DBG("### NO RESPONSE FROM MODEM!\r\n");
  1071. }
  1072. } else {
  1073. data.trim();
  1074. data.replace(GSM_NL GSM_NL, GSM_NL);
  1075. data.replace(GSM_NL, "\r\n ");
  1076. if (data.length()) {
  1077. }
  1078. }
  1079. //data.replace(GSM_NL, "/");
  1080. //DBG('<', index, '>', data);
  1081. return index;
  1082. }
  1083. uint8_t waitResponse(uint32_t timeout_ms,
  1084. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  1085. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  1086. {
  1087. String data;
  1088. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  1089. }
  1090. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  1091. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  1092. {
  1093. return waitResponse(1000, r1, r2, r3, r4, r5);
  1094. }
  1095. bool commandMode(uint8_t retries = 5) {
  1096. // If we're already in command mode, move on
  1097. if (inCommandMode && (millis() - lastCommandModeMillis) < 10000L) return true;
  1098. uint8_t triesMade = 0;
  1099. uint8_t triesUntilReset = 4; // only reset after 4 failures
  1100. bool success = false;
  1101. streamClear(); // Empty everything in the buffer before starting
  1102. while (!success and triesMade < retries) {
  1103. // Cannot send anything for 1 "guard time" before entering command mode
  1104. // Default guard time is 1s, but the init fxn decreases it to 100 ms
  1105. delay(guardTime + 10);
  1106. streamWrite(GF("+++")); // enter command mode
  1107. int res = waitResponse(guardTime*2);
  1108. success = (1 == res);
  1109. if (0 == res) {
  1110. triesUntilReset--;
  1111. if (triesUntilReset == 0) {
  1112. triesUntilReset = 4;
  1113. pinReset(); // if it's unresponsive, reset
  1114. delay(250); // a short delay to allow it to come back up
  1115. // TODO-optimize this
  1116. }
  1117. }
  1118. triesMade ++;
  1119. }
  1120. if (success) {
  1121. inCommandMode = true;
  1122. lastCommandModeMillis = millis();
  1123. }
  1124. return success;
  1125. }
  1126. bool writeChanges(void) {
  1127. sendAT(GF("WR")); // Write changes to flash
  1128. if (1 != waitResponse()) return false;
  1129. sendAT(GF("AC")); // Apply changes
  1130. if (1 != waitResponse()) return false;
  1131. return true;
  1132. }
  1133. void exitCommand(void) {
  1134. // NOTE: Here we explicitely try to exit command mode
  1135. // even if the internal flag inCommandMode was already false
  1136. sendAT(GF("CN")); // Exit command mode
  1137. waitResponse();
  1138. inCommandMode = false;
  1139. }
  1140. bool exitAndFail(void) {
  1141. exitCommand(); // Exit command mode
  1142. return false;
  1143. }
  1144. void getSeries(void) {
  1145. sendAT(GF("HS")); // Get the "Hardware Series";
  1146. int16_t intRes = readResponseInt();
  1147. beeType = (XBeeType)intRes;
  1148. DBG(GF("### Modem: "), getModemName());
  1149. }
  1150. String readResponseString(uint32_t timeout_ms = 1000) {
  1151. TINY_GSM_YIELD();
  1152. unsigned long startMillis = millis();
  1153. while (!stream.available() && millis() - startMillis < timeout_ms) {};
  1154. String res = stream.readStringUntil('\r'); // lines end with carriage returns
  1155. res.trim();
  1156. return res;
  1157. }
  1158. int16_t readResponseInt(uint32_t timeout_ms = 1000) {
  1159. String res = readResponseString(timeout_ms); // it just works better reading a string first
  1160. if (res == "") res = "FF";
  1161. char buf[5] = {0,};
  1162. res.toCharArray(buf, 5);
  1163. int16_t intRes = strtol(buf, 0, 16);
  1164. return intRes;
  1165. }
  1166. String sendATGetString(GsmConstStr cmd) {
  1167. XBEE_COMMAND_START_DECORATOR(5, "")
  1168. sendAT(cmd);
  1169. String res = readResponseString();
  1170. XBEE_COMMAND_END_DECORATOR
  1171. return res;
  1172. }
  1173. bool gotIPforSavedHost() {
  1174. if (savedHost != "" && savedHostIP != IPAddress(0,0,0,0)) return true;
  1175. else return false;
  1176. }
  1177. public:
  1178. Stream& stream;
  1179. protected:
  1180. int16_t guardTime;
  1181. int8_t resetPin;
  1182. XBeeType beeType;
  1183. IPAddress savedIP;
  1184. String savedHost;
  1185. IPAddress savedHostIP;
  1186. IPAddress savedOperatingIP;
  1187. bool inCommandMode;
  1188. uint32_t lastCommandModeMillis;
  1189. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  1190. };
  1191. #endif