Smart-Home am Beispiel der Präsenzerkennung im Raum Projektarbeit Lennart Heimbs, Johannes Krug, Sebastian Dohle und Kevin Holzschuh bei Prof. Oliver Hofmann SS2019
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.

TinyGsmClientESP8266.h 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. /**
  2. * file TinyGsmClientESP8266.h
  3. * author Volodymyr Shymanskyy
  4. * license LGPL-3.0
  5. * copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * date Nov 2016
  7. */
  8. #ifndef TinyGsmClientESP8266_h
  9. #define TinyGsmClientESP8266_h
  10. //#define TINY_GSM_DEBUG Serial
  11. #if !defined(TINY_GSM_RX_BUFFER)
  12. #define TINY_GSM_RX_BUFFER 512
  13. #endif
  14. #define TINY_GSM_MUX_COUNT 5
  15. #include "TinyGsmCommon.h"
  16. #define GSM_NL "\r\n"
  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. static unsigned TINY_GSM_TCP_KEEP_ALIVE = 120;
  20. class TinyGsm
  21. {
  22. public:
  23. class GsmClient : public Client
  24. {
  25. friend class TinyGsm;
  26. typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
  27. public:
  28. GsmClient() {}
  29. GsmClient(TinyGsm& modem, uint8_t mux = 1)
  30. {
  31. init(&modem, mux);
  32. }
  33. bool init(TinyGsm* modem, uint8_t mux = 1)
  34. {
  35. this->at = modem;
  36. this->mux = mux;
  37. sock_connected = false;
  38. at->sockets[mux] = this;
  39. return true;
  40. }
  41. public:
  42. virtual int connect(const char *host, uint16_t port)
  43. {
  44. stop();
  45. TINY_GSM_YIELD();
  46. rx.clear();
  47. sock_connected = at->modemConnect(host, port, mux);
  48. return sock_connected;
  49. }
  50. virtual int connect(IPAddress ip, uint16_t port)
  51. {
  52. String host;
  53. host.reserve(16);
  54. host += ip[0];
  55. host += ".";
  56. host += ip[1];
  57. host += ".";
  58. host += ip[2];
  59. host += ".";
  60. host += ip[3];
  61. return connect(host.c_str(), port);
  62. }
  63. virtual void stop()
  64. {
  65. TINY_GSM_YIELD();
  66. at->sendAT(GF("+CIPCLOSE="), mux);
  67. sock_connected = false;
  68. at->waitResponse();
  69. rx.clear();
  70. }
  71. virtual size_t write(const uint8_t *buf, size_t size)
  72. {
  73. TINY_GSM_YIELD();
  74. //at->maintain();
  75. return at->modemSend(buf, size, mux);
  76. }
  77. virtual size_t write(uint8_t c)
  78. {
  79. return write(&c, 1);
  80. }
  81. virtual int available()
  82. {
  83. TINY_GSM_YIELD();
  84. if (!rx.size() && sock_connected) {
  85. at->maintain();
  86. }
  87. return rx.size();
  88. }
  89. virtual int read(uint8_t *buf, size_t size)
  90. {
  91. TINY_GSM_YIELD();
  92. size_t cnt = 0;
  93. while (cnt < size) {
  94. size_t chunk = TinyGsmMin(size-cnt, rx.size());
  95. if (chunk > 0) {
  96. rx.get(buf, chunk);
  97. buf += chunk;
  98. cnt += chunk;
  99. continue;
  100. }
  101. // TODO: Read directly into user buffer?
  102. if (!rx.size() && sock_connected) {
  103. at->maintain();
  104. //break;
  105. }
  106. }
  107. return cnt;
  108. }
  109. virtual int read()
  110. {
  111. uint8_t c;
  112. if (read(&c, 1) == 1) {
  113. return c;
  114. }
  115. return -1;
  116. }
  117. virtual int peek()
  118. {
  119. return -1; //TODO
  120. }
  121. virtual void flush()
  122. {
  123. at->stream.flush();
  124. }
  125. virtual uint8_t connected()
  126. {
  127. if (available()) {
  128. return true;
  129. }
  130. return sock_connected;
  131. }
  132. virtual operator bool()
  133. {
  134. return connected();
  135. }
  136. /*
  137. * Extended API
  138. */
  139. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  140. private:
  141. TinyGsm* at;
  142. uint8_t mux;
  143. bool sock_connected;
  144. RxFifo rx;
  145. };
  146. class GsmClientSecure : public GsmClient
  147. {
  148. public:
  149. GsmClientSecure() {}
  150. GsmClientSecure(TinyGsm& modem, uint8_t mux = 1)
  151. : GsmClient(modem, mux)
  152. {}
  153. public:
  154. virtual int connect(const char *host, uint16_t port)
  155. {
  156. stop();
  157. TINY_GSM_YIELD();
  158. rx.clear();
  159. sock_connected = at->modemConnect(host, port, mux, true);
  160. return sock_connected;
  161. }
  162. };
  163. public:
  164. explicit TinyGsm(Stream& stream)
  165. : stream(stream)
  166. {
  167. memset(sockets, 0, sizeof(sockets));
  168. }
  169. /*
  170. * Basic functions
  171. */
  172. bool begin()
  173. {
  174. return init();
  175. }
  176. bool init()
  177. {
  178. if (!testAT()) {
  179. return false;
  180. }
  181. sendAT(GF("E0")); // Echo Off
  182. if (waitResponse() != 1) {
  183. return false;
  184. }
  185. return true;
  186. }
  187. void setBaud(unsigned long baud)
  188. {
  189. sendAT(GF("+IPR="), baud);
  190. }
  191. bool testAT(unsigned long timeout = 10000L)
  192. {
  193. for (unsigned long start = millis(); millis() - start < timeout; ) {
  194. sendAT(GF(""));
  195. if (waitResponse(200) == 1) {
  196. delay(100);
  197. return true;
  198. }
  199. delay(100);
  200. }
  201. return false;
  202. }
  203. void maintain()
  204. {
  205. waitResponse(10, NULL, NULL);
  206. }
  207. bool factoryDefault()
  208. {
  209. sendAT(GF("+RESTORE"));
  210. return waitResponse() == 1;
  211. }
  212. String getModemInfo()
  213. {
  214. sendAT(GF("+GMR"));
  215. String res;
  216. if (waitResponse(1000L, res) != 1) {
  217. return "";
  218. }
  219. res.replace(GSM_NL "OK" GSM_NL, "");
  220. res.replace(GSM_NL, " ");
  221. res.trim();
  222. return res;
  223. }
  224. bool hasSSL()
  225. {
  226. return true;
  227. }
  228. /*
  229. * Power functions
  230. */
  231. bool restart()
  232. {
  233. if (!testAT()) {
  234. return false;
  235. }
  236. sendAT(GF("+RST"));
  237. if (waitResponse(10000L) != 1) {
  238. return false;
  239. }
  240. if (waitResponse(10000L, GF(GSM_NL "ready" GSM_NL)) != 1) {
  241. return false;
  242. }
  243. delay(500);
  244. return init();
  245. }
  246. /*
  247. * Generic network functions
  248. */
  249. int getSignalQuality()
  250. {
  251. sendAT(GF("+CWJAP_CUR?"));
  252. int res1 = waitResponse(GF("No AP"), GF("+CWJAP_CUR:"));
  253. if (res1 != 2) {
  254. waitResponse();
  255. return 0;
  256. }
  257. streamSkipUntil(','); // Skip SSID
  258. streamSkipUntil(','); // Skip BSSID/MAC address
  259. streamSkipUntil(','); // Skip Chanel number
  260. int res2 = stream.parseInt(); // Read RSSI
  261. waitResponse(); // Returns an OK after the value
  262. return res2;
  263. }
  264. bool isNetworkConnected()
  265. {
  266. sendAT(GF("+CIPSTATUS"));
  267. int res1 = waitResponse(3000, GF("STATUS:"));
  268. int res2 = 0;
  269. if (res1 == 1) {
  270. res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
  271. }
  272. // <stat> status of ESP8266 station interface
  273. // 2 : ESP8266 station connected to an AP and has obtained IP
  274. // 3 : ESP8266 station created a TCP or UDP transmission
  275. // 4 : the TCP or UDP transmission of ESP8266 station disconnected (but AP is connected)
  276. // 5 : ESP8266 station did NOT connect to an AP
  277. waitResponse(); // Returns an OK after the status
  278. if (res2 == 2 || res2 == 3 || res2 == 4) {
  279. return true;
  280. } else {
  281. return false;
  282. }
  283. }
  284. bool waitForNetwork(unsigned long timeout = 60000L)
  285. {
  286. for (unsigned long start = millis(); millis() - start < timeout; ) {
  287. sendAT(GF("+CIPSTATUS"));
  288. int res1 = waitResponse(3000, GF("busy p..."), GF("STATUS:"));
  289. if (res1 == 2) {
  290. int res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
  291. if (res2 == 2 || res2 == 3 || res2 == 4) {
  292. waitResponse();
  293. return true;
  294. }
  295. }
  296. delay(250);
  297. }
  298. return false;
  299. }
  300. /*
  301. * WiFi functions
  302. */
  303. bool networkConnect(const char* ssid, const char* pwd)
  304. {
  305. sendAT(GF("+CIPMUX=1"));
  306. if (waitResponse() != 1) {
  307. return false;
  308. }
  309. sendAT(GF("+CWMODE_CUR=1"));
  310. if (waitResponse() != 1) {
  311. return false;
  312. }
  313. sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\""));
  314. if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) {
  315. return false;
  316. }
  317. return true;
  318. }
  319. bool networkDisconnect()
  320. {
  321. sendAT(GF("+CWQAP"));
  322. bool retVal = waitResponse(10000L) == 1;
  323. waitResponse(GF("WIFI DISCONNECT"));
  324. return retVal;
  325. }
  326. String getLocalIP()
  327. {
  328. sendAT(GF("+CIPSTA_CUR??"));
  329. int res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:"));
  330. if (res1 != 2) {
  331. return "";
  332. }
  333. String res2 = stream.readStringUntil('"');
  334. waitResponse();
  335. return res2;
  336. }
  337. IPAddress localIP()
  338. {
  339. return TinyGsmIpFromString(getLocalIP());
  340. }
  341. protected:
  342. bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false)
  343. {
  344. if (ssl) {
  345. sendAT(GF("+CIPSSLSIZE=4096"));
  346. waitResponse();
  347. }
  348. sendAT(GF("+CIPSTART="), mux, ',', ssl ? GF("\"SSL") : GF("\"TCP"), GF("\",\""), host, GF("\","),
  349. port, GF(","), TINY_GSM_TCP_KEEP_ALIVE);
  350. // TODO: Check mux
  351. int rsp = waitResponse(75000L,
  352. GFP(GSM_OK),
  353. GFP(GSM_ERROR),
  354. GF("ALREADY CONNECT"));
  355. // if (rsp == 3) waitResponse(); // May return "ERROR" after the "ALREADY CONNECT"
  356. return (1 == rsp);
  357. }
  358. int modemSend(const void* buff, size_t len, uint8_t mux)
  359. {
  360. sendAT(GF("+CIPSEND="), mux, ',', len);
  361. if (waitResponse(GF(">")) != 1) {
  362. return 0;
  363. }
  364. stream.write((uint8_t*)buff, len);
  365. stream.flush();
  366. if (waitResponse(10000L, GF(GSM_NL "SEND OK" GSM_NL)) != 1) {
  367. return 0;
  368. }
  369. return len;
  370. }
  371. bool modemGetConnected(uint8_t mux)
  372. {
  373. // TODO: re-check this
  374. sendAT(GF("+CIPSTATUS="), mux);
  375. int res1 = waitResponse(3000, GF("STATUS:"));
  376. int res2;
  377. if (res1 == 1) {
  378. res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
  379. }
  380. // <stat> status of ESP8266 station interface
  381. // 2 : ESP8266 station connected to an AP and has obtained IP
  382. // 3 : ESP8266 station created a TCP or UDP transmission
  383. // 4 : the TCP or UDP transmission of ESP8266 station disconnected (but AP is connected)
  384. // 5 : ESP8266 station did NOT connect to an AP
  385. waitResponse(); // Returns an OK after the status
  386. if (res2 == 2 || res2 == 3 || res2 == 4) {
  387. return true;
  388. } else {
  389. return false;
  390. }
  391. }
  392. public:
  393. /* Utilities */
  394. template<typename T>
  395. void streamWrite(T last)
  396. {
  397. stream.print(last);
  398. }
  399. template<typename T, typename... Args>
  400. void streamWrite(T head, Args... tail)
  401. {
  402. stream.print(head);
  403. streamWrite(tail...);
  404. }
  405. bool streamSkipUntil(char c) //TODO: timeout
  406. {
  407. while (true) {
  408. while (!stream.available()) {
  409. TINY_GSM_YIELD();
  410. }
  411. if (stream.read() == c) {
  412. return true;
  413. }
  414. }
  415. return false;
  416. }
  417. template<typename... Args>
  418. void sendAT(Args... cmd)
  419. {
  420. streamWrite("AT", cmd..., GSM_NL);
  421. stream.flush();
  422. TINY_GSM_YIELD();
  423. //DBG("### AT:", cmd...);
  424. }
  425. // TODO: Optimize this!
  426. uint8_t waitResponse(uint32_t timeout, String& data,
  427. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  428. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  429. {
  430. /*String r1s(r1); r1s.trim();
  431. String r2s(r2); r2s.trim();
  432. String r3s(r3); r3s.trim();
  433. String r4s(r4); r4s.trim();
  434. String r5s(r5); r5s.trim();
  435. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  436. data.reserve(64);
  437. int index = 0;
  438. unsigned long startMillis = millis();
  439. do {
  440. TINY_GSM_YIELD();
  441. while (stream.available() > 0) {
  442. int a = stream.read();
  443. if (a <= 0) {
  444. continue; // Skip 0x00 bytes, just in case
  445. }
  446. data += (char)a;
  447. if (r1 && data.endsWith(r1)) {
  448. index = 1;
  449. goto finish;
  450. } else if (r2 && data.endsWith(r2)) {
  451. index = 2;
  452. goto finish;
  453. } else if (r3 && data.endsWith(r3)) {
  454. index = 3;
  455. goto finish;
  456. } else if (r4 && data.endsWith(r4)) {
  457. index = 4;
  458. goto finish;
  459. } else if (r5 && data.endsWith(r5)) {
  460. index = 5;
  461. goto finish;
  462. } else if (data.endsWith(GF(GSM_NL "+IPD,"))) {
  463. int mux = stream.readStringUntil(',').toInt();
  464. int len = stream.readStringUntil(':').toInt();
  465. int len_orig = len;
  466. if (len > sockets[mux]->rx.free()) {
  467. DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
  468. } else {
  469. DBG("### Got: ", len, "->", sockets[mux]->rx.free());
  470. }
  471. while (len--) {
  472. while (!stream.available()) {
  473. TINY_GSM_YIELD();
  474. }
  475. sockets[mux]->rx.put(stream.read());
  476. }
  477. if (len_orig > sockets[mux]->available()) { // TODO
  478. DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
  479. }
  480. data = "";
  481. } else if (data.endsWith(GF("CLOSED"))) {
  482. int muxStart = max(0,data.lastIndexOf(GSM_NL, data.length()-8));
  483. int coma = data.indexOf(',', muxStart);
  484. int mux = data.substring(muxStart, coma).toInt();
  485. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  486. sockets[mux]->sock_connected = false;
  487. }
  488. data = "";
  489. DBG("### Closed: ", mux);
  490. }
  491. }
  492. } while (millis() - startMillis < timeout);
  493. finish:
  494. if (!index) {
  495. data.trim();
  496. if (data.length()) {
  497. DBG("### Unhandled:", data);
  498. }
  499. data = "";
  500. }
  501. return index;
  502. }
  503. uint8_t waitResponse(uint32_t timeout,
  504. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  505. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  506. {
  507. String data;
  508. return waitResponse(timeout, data, r1, r2, r3, r4, r5);
  509. }
  510. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  511. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  512. {
  513. return waitResponse(1000, r1, r2, r3, r4, r5);
  514. }
  515. public:
  516. Stream& stream;
  517. protected:
  518. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  519. };
  520. #endif