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.

1463 lines
44 KiB

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