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.

1460 lines
44 KiB

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