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.

1161 lines
34 KiB

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