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.

706 lines
18 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. /**
  2. * @file TinyGsmClientXBee.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef TinyGsmClientXBee_h
  9. #define TinyGsmClientXBee_h
  10. // #define TINY_GSM_DEBUG Serial
  11. #if !defined(TINY_GSM_RX_BUFFER)
  12. #define TINY_GSM_RX_BUFFER 256
  13. #endif
  14. #define TINY_GSM_MUX_COUNT 1 // Multi-plexing isn't supported using command mode
  15. #include <TinyGsmCommon.h>
  16. #define GSM_NL "\r"
  17. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  18. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  19. enum SimStatus {
  20. SIM_ERROR = 0,
  21. SIM_READY = 1,
  22. SIM_LOCKED = 2,
  23. };
  24. enum XBeeType {
  25. CELL = 0,
  26. WIFI = 1,
  27. };
  28. enum RegStatus {
  29. REG_UNREGISTERED = 0,
  30. REG_SEARCHING = 2,
  31. REG_DENIED = 3,
  32. REG_OK_HOME = 1,
  33. REG_OK_ROAMING = 5,
  34. REG_UNKNOWN = 4,
  35. };
  36. class TinyGsm
  37. {
  38. public:
  39. class GsmClient : public Client
  40. {
  41. friend class TinyGsm;
  42. public:
  43. GsmClient() {}
  44. GsmClient(TinyGsm& modem, uint8_t mux = 0) {
  45. init(&modem, mux);
  46. }
  47. bool init(TinyGsm* modem, uint8_t mux = 0) {
  48. this->at = modem;
  49. this->mux = mux;
  50. sock_connected = false;
  51. at->sockets[mux] = this;
  52. return true;
  53. }
  54. public:
  55. virtual int connect(const char *host, uint16_t port) {
  56. at->streamClear(); // Empty anything remaining in the buffer;
  57. bool sock_connected = false;
  58. if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
  59. sock_connected = at->modemConnect(host, port, mux, false);
  60. at->writeChanges();
  61. at->exitCommand();
  62. }
  63. at->streamClear(); // Empty anything remaining in the buffer;
  64. return sock_connected;
  65. }
  66. virtual int connect(IPAddress ip, uint16_t port) {
  67. at->streamClear(); // Empty anything remaining in the buffer;
  68. bool sock_connected = false;
  69. if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
  70. sock_connected = at->modemConnect(ip, port, mux, false);
  71. at->writeChanges();
  72. at->exitCommand();
  73. }
  74. at->streamClear(); // Empty anything remaining in the buffer;
  75. return sock_connected;
  76. }
  77. // This is a hack to shut the socket by setting the timeout to zero and
  78. // then sending an empty line to the server.
  79. virtual void stop() {
  80. at->streamClear(); // Empty anything remaining in the buffer;
  81. at->commandMode();
  82. at->sendAT(GF("TM0")); // Set socket timeout to 0;
  83. at->waitResponse();
  84. at->writeChanges();
  85. at->exitCommand();
  86. at->modemSend("", 1, mux);
  87. at->commandMode();
  88. at->sendAT(GF("TM64")); // Set socket timeout back to 10 seconds;
  89. at->waitResponse();
  90. at->writeChanges();
  91. at->exitCommand();
  92. at->streamClear(); // Empty anything remaining in the buffer;
  93. sock_connected = false;
  94. }
  95. virtual size_t write(const uint8_t *buf, size_t size) {
  96. TINY_GSM_YIELD();
  97. //at->maintain();
  98. return at->modemSend(buf, size, mux);
  99. }
  100. virtual size_t write(uint8_t c) {
  101. return write(&c, 1);
  102. }
  103. virtual int available() {
  104. TINY_GSM_YIELD();
  105. return at->stream.available();
  106. }
  107. virtual int read(uint8_t *buf, size_t size) {
  108. TINY_GSM_YIELD();
  109. return at->stream.readBytes(buf, size);
  110. }
  111. virtual int read() {
  112. TINY_GSM_YIELD();
  113. return at->stream.read();
  114. }
  115. virtual int peek() { return at->stream.peek(); }
  116. virtual void flush() { at->stream.flush(); }
  117. virtual uint8_t connected() {
  118. if (available()) {
  119. return true;
  120. }
  121. return sock_connected;
  122. }
  123. virtual operator bool() { return connected(); }
  124. /*
  125. * Extended API
  126. */
  127. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  128. private:
  129. TinyGsm* at;
  130. uint8_t mux;
  131. bool sock_connected;
  132. };
  133. class GsmClientSecure : public GsmClient
  134. {
  135. public:
  136. GsmClientSecure() {}
  137. GsmClientSecure(TinyGsm& modem, uint8_t mux = 1)
  138. : GsmClient(modem, mux)
  139. {}
  140. public:
  141. virtual int connect(const char *host, uint16_t port) {
  142. at->streamClear(); // Empty anything remaining in the buffer;
  143. bool sock_connected = false;
  144. if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
  145. sock_connected = at->modemConnect(host, port, mux, true);
  146. at->writeChanges();
  147. at->exitCommand();
  148. }
  149. at->streamClear(); // Empty anything remaining in the buffer;
  150. return sock_connected;
  151. }
  152. virtual int connect(IPAddress ip, uint16_t port) {
  153. at->streamClear(); // Empty anything remaining in the buffer;
  154. bool sock_connected = false;
  155. if (at->commandMode()) { // Don't try if we didn't successfully get into command mode
  156. sock_connected = at->modemConnect(ip, port, mux, true);
  157. at->writeChanges();
  158. at->exitCommand();
  159. }
  160. at->streamClear(); // Empty anything remaining in the buffer;
  161. return sock_connected;
  162. }
  163. };
  164. public:
  165. TinyGsm(Stream& stream)
  166. : stream(stream)
  167. {
  168. memset(sockets, 0, sizeof(sockets));
  169. }
  170. /*
  171. * Basic functions
  172. */
  173. bool begin() {
  174. return init();
  175. }
  176. bool init() {
  177. guardTime = 1100; // Start with a default guard time of 1 second
  178. if (!commandMode()) return false;
  179. sendAT(GF("AP0")); // Put in transparent mode
  180. waitResponse();
  181. writeChanges();
  182. sendAT(GF("GT64")); // shorten the guard time to 100ms
  183. if (1 == waitResponse() && writeChanges()) guardTime = 125;
  184. sendAT(GF("HS")); // Get the "Hardware Series"; 0x601 for S6B (Wifi)
  185. int res = waitResponse(GF("601"));
  186. if (res == 1) beeType = WIFI;
  187. else beeType = CELL;
  188. exitCommand();
  189. return true;
  190. }
  191. bool testAT(unsigned long timeout = 10000L) {
  192. for (unsigned long start = millis(); millis() - start < timeout; ) {
  193. if (commandMode())
  194. {
  195. sendAT();
  196. if (waitResponse(200) == 1) {
  197. return true;
  198. }
  199. exitCommand();
  200. }
  201. delay(100);
  202. }
  203. return false;
  204. }
  205. void maintain() {}
  206. bool factoryDefault() {
  207. if (!commandMode()) return false; // Return immediately
  208. sendAT(GF("RE"));
  209. bool ret_val = waitResponse() == 1;
  210. writeChanges();
  211. exitCommand();
  212. return ret_val;
  213. }
  214. bool hasSSL() {
  215. if (beeType == WIFI) return false;
  216. else return true;
  217. }
  218. /*
  219. * Power functions
  220. */
  221. bool restart() {
  222. if (!commandMode()) return false; // Return immediately
  223. sendAT(GF("FR"));
  224. if (waitResponse() != 1) goto fail;
  225. delay (2000); // Actually resets about 2 seconds later
  226. // Wait until reboot complete and responds to command mode call again
  227. for (unsigned long start = millis(); millis() - start < 60000L; ) {
  228. if (commandMode(1)) {
  229. exitCommand();
  230. delay(250); // wait a litle before trying again
  231. }
  232. }
  233. return true;
  234. fail:
  235. exitCommand();
  236. return false;
  237. }
  238. void setupPinSleep(bool maintainAssociation = false) {
  239. if (!commandMode()) return; // Return immediately
  240. sendAT(GF("SM"),1); // Pin sleep
  241. waitResponse();
  242. if (beeType == WIFI && !maintainAssociation) {
  243. sendAT(GF("SO"),200); // For lowest power, dissassociated deep sleep
  244. waitResponse();
  245. }
  246. else if (!maintainAssociation){
  247. sendAT(GF("SO"),1); // For lowest power, dissassociated deep sleep
  248. waitResponse();
  249. }
  250. writeChanges();
  251. exitCommand();
  252. }
  253. /*
  254. * SIM card functions
  255. */
  256. bool simUnlock(const char *pin) { // Not supported
  257. return false;
  258. }
  259. String getSimCCID() {
  260. if (!commandMode()) return ""; // Return immediately
  261. sendAT(GF("S#"));
  262. String res = readResponse();
  263. exitCommand();
  264. return res;
  265. }
  266. String getIMEI() {
  267. if (!commandMode()) return ""; // Return immediately
  268. sendAT(GF("IM"));
  269. String res = readResponse();
  270. exitCommand();
  271. return res;
  272. }
  273. SimStatus getSimStatus(unsigned long timeout = 10000L) {
  274. return SIM_READY; // unsupported
  275. }
  276. RegStatus getRegistrationStatus() {
  277. if (!commandMode()) return REG_UNREGISTERED; // Return immediately
  278. sendAT(GF("AI"));
  279. String res = readResponse();
  280. exitCommand();
  281. if(res == GF("0"))
  282. return REG_OK_HOME;
  283. else if(res == GF("13") || res == GF("2A"))
  284. return REG_UNREGISTERED;
  285. else if(res == GF("FF") || res == GF("22") || res == GF("23") ||
  286. res == GF("40") || res == GF("41") || res == GF("42"))
  287. return REG_SEARCHING;
  288. else if(res == GF("24") || res == GF("25") || res == GF("27"))
  289. return REG_DENIED;
  290. else return REG_UNKNOWN;
  291. }
  292. String getOperator() {
  293. if (!commandMode()) return ""; // Return immediately
  294. sendAT(GF("MN"));
  295. String res = readResponse();
  296. exitCommand();
  297. return res;
  298. }
  299. /*
  300. * Generic network functions
  301. */
  302. int getSignalQuality() {
  303. if (!commandMode()) return 0; // Return immediately
  304. if (beeType == WIFI) sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity
  305. else sendAT(GF("DB")); // ask for the cell strength in dBm
  306. // wait for the response
  307. unsigned long startMillis = millis();
  308. while (!stream.available() && millis() - startMillis < 1000) {};
  309. char buf[2] = {0}; // Set up buffer for response
  310. buf[0] = streamRead();
  311. buf[1] = streamRead();
  312. // DBG(buf[0], buf[1], "\n");
  313. exitCommand();
  314. int intr = strtol(buf, 0, 16);
  315. if (beeType == WIFI) return -93 + intr; // the maximum sensitivity is -93dBm
  316. else return -1*intr; // need to convert to negative number
  317. }
  318. bool isNetworkConnected() {
  319. RegStatus s = getRegistrationStatus();
  320. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  321. }
  322. bool waitForNetwork(unsigned long timeout = 60000L) {
  323. for (unsigned long start = millis(); millis() - start < timeout; ) {
  324. if (isNetworkConnected()) {
  325. return true;
  326. }
  327. delay(250);
  328. }
  329. return false;
  330. }
  331. /*
  332. * WiFi functions
  333. */
  334. bool networkConnect(const char* ssid, const char* pwd) {
  335. if (!commandMode()) return false; // return immediately
  336. sendAT(GF("EE"), 2); // Set security to WPA2
  337. if (waitResponse() != 1) goto fail;
  338. sendAT(GF("ID"), ssid);
  339. if (waitResponse() != 1) goto fail;
  340. sendAT(GF("PK"), pwd);
  341. if (waitResponse() != 1) goto fail;
  342. writeChanges();
  343. exitCommand();
  344. return true;
  345. fail:
  346. exitCommand();
  347. return false;
  348. }
  349. bool networkDisconnect() {
  350. return false; // Doesn't support disconnecting
  351. }
  352. String getLocalIP() {
  353. if (!commandMode()) return ""; // Return immediately
  354. sendAT(GF("MY"));
  355. String IPaddr; IPaddr.reserve(16);
  356. // wait for the response - this response can be very slow
  357. IPaddr = readResponse(30000);
  358. exitCommand();
  359. return IPaddr;
  360. }
  361. IPAddress localIP() {
  362. return TinyGsmIpFromString(getLocalIP());
  363. }
  364. /*
  365. * GPRS functions
  366. */
  367. bool gprsConnect(const char* apn, const char* user = "", const char* pw = "") {
  368. if (!commandMode()) return false; // Return immediately
  369. sendAT(GF("AN"), apn); // Set the APN
  370. waitResponse();
  371. writeChanges();
  372. exitCommand();
  373. return true;
  374. }
  375. bool gprsDisconnect() { // TODO
  376. return false;
  377. }
  378. /*
  379. * Messaging functions
  380. */
  381. void sendUSSD() {
  382. }
  383. void sendSMS() {
  384. }
  385. bool sendSMS(const String& number, const String& text) {
  386. if (!commandMode()) return false; // Return immediately
  387. sendAT(GF("IP"), 2); // Put in text messaging mode
  388. if (waitResponse() !=1) goto fail;
  389. sendAT(GF("PH"), number); // Set the phone number
  390. if (waitResponse() !=1) goto fail;
  391. sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriage return)
  392. if (waitResponse() !=1) goto fail;
  393. if (!writeChanges()) goto fail;
  394. exitCommand();
  395. stream.print(text);
  396. stream.write((char)0x0D); // close off with the carriage return
  397. return true;
  398. fail:
  399. exitCommand();
  400. return false;
  401. }
  402. private:
  403. int modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false) {
  404. String strIP; strIP.reserve(16);
  405. unsigned long startMillis = millis();
  406. bool gotIP = false;
  407. while (!gotIP && millis() - startMillis < 30000) // the lookup can take a while
  408. {
  409. sendAT(GF("LA"), host);
  410. while (stream.available() < 4) {};// wait for the response
  411. strIP = streamReadUntil('\r'); // read result
  412. if (!strIP.endsWith(GF("ERROR"))) gotIP = true;
  413. delay(100); // short wait before trying again
  414. }
  415. if (gotIP) { // No reason to continue if we don't know the IP address
  416. IPAddress ip = TinyGsmIpFromString(strIP);
  417. return modemConnect(ip, port, mux, ssl);
  418. }
  419. else return false;
  420. }
  421. int modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false) {
  422. String host; host.reserve(16);
  423. host += ip[0];
  424. host += ".";
  425. host += ip[1];
  426. host += ".";
  427. host += ip[2];
  428. host += ".";
  429. host += ip[3];
  430. if (ssl) {
  431. sendAT(GF("IP"), 4); // Put in TCP mode
  432. waitResponse();
  433. } else {
  434. sendAT(GF("IP"), 1); // Put in TCP mode
  435. waitResponse();
  436. }
  437. sendAT(GF("DL"), host); // Set the "Destination Address Low"
  438. waitResponse();
  439. sendAT(GF("DE"), String(port, HEX)); // Set the destination port
  440. int rsp = waitResponse();
  441. return rsp;
  442. }
  443. int modemSend(const void* buff, size_t len, uint8_t mux = 0) {
  444. stream.write((uint8_t*)buff, len);
  445. stream.flush();
  446. return len;
  447. }
  448. bool modemGetConnected(uint8_t mux = 0) {
  449. if (!commandMode()) return false;
  450. sendAT(GF("AI"));
  451. int res = waitResponse(GF("0"));
  452. exitCommand();
  453. return 1 == res;
  454. }
  455. public:
  456. /* Utilities */
  457. template<typename T>
  458. void streamWrite(T last) {
  459. stream.print(last);
  460. }
  461. template<typename T, typename... Args>
  462. void streamWrite(T head, Args... tail) {
  463. stream.print(head);
  464. streamWrite(tail...);
  465. }
  466. int streamRead() { return stream.read(); }
  467. String streamReadUntil(char c) {
  468. TINY_GSM_YIELD();
  469. String return_string = stream.readStringUntil(c);
  470. return_string.trim();
  471. // DBG(return_string, c);
  472. return return_string;
  473. }
  474. void streamClear(void) {
  475. while (stream.available()) { streamRead(); }
  476. }
  477. bool commandMode(int retries = 2) {
  478. int triesMade = 0;
  479. bool success = false;
  480. while (!success and triesMade < retries) {
  481. // Cannot send anything for 1 "guard time" before entering command mode
  482. // Default guard time is 1s, but the init fxn decreases it to 250 ms
  483. delay(guardTime);
  484. streamWrite(GF("+++")); // enter command mode
  485. // DBG("\r\n+++");
  486. success = (1 == waitResponse(guardTime*2));
  487. triesMade ++;
  488. }
  489. return success;
  490. }
  491. bool writeChanges(void) {
  492. sendAT(GF("WR")); // Write changes to flash
  493. if (1 != waitResponse()) return false;
  494. sendAT(GF("AC")); // Apply changes
  495. if (1 != waitResponse()) return false;
  496. return true;
  497. }
  498. void exitCommand(void) {
  499. sendAT(GF("CN")); // Exit command mode
  500. waitResponse();
  501. }
  502. String readResponse(uint32_t timeout = 1000) {
  503. unsigned long startMillis = millis();
  504. while (!stream.available() && millis() - startMillis < timeout) {};
  505. String res = streamReadUntil('\r'); // lines end with carriage returns
  506. return res;
  507. }
  508. template<typename... Args>
  509. void sendAT(Args... cmd) {
  510. streamWrite("AT", cmd..., GSM_NL);
  511. stream.flush();
  512. TINY_GSM_YIELD();
  513. // DBG("### AT:", cmd...);
  514. }
  515. // TODO: Optimize this!
  516. uint8_t waitResponse(uint32_t timeout, String& data,
  517. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  518. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  519. {
  520. /*String r1s(r1); r1s.trim();
  521. String r2s(r2); r2s.trim();
  522. String r3s(r3); r3s.trim();
  523. String r4s(r4); r4s.trim();
  524. String r5s(r5); r5s.trim();
  525. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  526. data.reserve(16); // Should never be getting much here for the XBee
  527. int index = 0;
  528. unsigned long startMillis = millis();
  529. do {
  530. TINY_GSM_YIELD();
  531. while (stream.available() > 0) {
  532. int a = streamRead();
  533. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  534. data += (char)a;
  535. if (r1 && data.endsWith(r1)) {
  536. index = 1;
  537. goto finish;
  538. } else if (r2 && data.endsWith(r2)) {
  539. index = 2;
  540. goto finish;
  541. } else if (r3 && data.endsWith(r3)) {
  542. index = 3;
  543. goto finish;
  544. } else if (r4 && data.endsWith(r4)) {
  545. index = 4;
  546. goto finish;
  547. } else if (r5 && data.endsWith(r5)) {
  548. index = 5;
  549. goto finish;
  550. }
  551. }
  552. } while (millis() - startMillis < timeout);
  553. finish:
  554. if (!index) {
  555. data.trim();
  556. data.replace(GSM_NL GSM_NL, GSM_NL);
  557. data.replace(GSM_NL, "\r\n" " ");
  558. if (data.length()) {
  559. DBG("### Unhandled:", data, "\r\n");
  560. } else {
  561. DBG("### NO RESPONSE!\r\n");
  562. }
  563. } else {
  564. data.trim();
  565. data.replace(GSM_NL GSM_NL, GSM_NL);
  566. data.replace(GSM_NL, "\r\n ");
  567. if (data.length()) {
  568. // DBG("<<< ", data);
  569. }
  570. }
  571. return index;
  572. }
  573. uint8_t waitResponse(uint32_t timeout,
  574. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  575. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  576. {
  577. String data;
  578. return waitResponse(timeout, data, r1, r2, r3, r4, r5);
  579. }
  580. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  581. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  582. {
  583. return waitResponse(1000, r1, r2, r3, r4, r5);
  584. }
  585. private:
  586. int guardTime;
  587. XBeeType beeType;
  588. Stream& stream;
  589. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  590. };
  591. #endif