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.

485 lines
15 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
4 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
4 years ago
8 years ago
8 years ago
8 years ago
4 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
4 years ago
8 years ago
  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 SRC_TINYGSMCLIENTESP8266_H_
  9. #define SRC_TINYGSMCLIENTESP8266_H_
  10. // #pragma message("TinyGSM: TinyGsmClientESP8266")
  11. // #define TINY_GSM_DEBUG Serial
  12. #define TINY_GSM_MUX_COUNT 5
  13. #define TINY_GSM_NO_MODEM_BUFFER
  14. #include "TinyGsmModem.tpp"
  15. #include "TinyGsmSSL.tpp"
  16. #include "TinyGsmTCP.tpp"
  17. #include "TinyGsmWifi.tpp"
  18. #define GSM_NL "\r\n"
  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. static uint8_t TINY_GSM_TCP_KEEP_ALIVE = 120;
  22. // <stat> status of ESP8266 station interface
  23. // 2 : ESP8266 station connected to an AP and has obtained IP
  24. // 3 : ESP8266 station created a TCP or UDP transmission
  25. // 4 : the TCP or UDP transmission of ESP8266 station disconnected
  26. // 5 : ESP8266 station did NOT connect to an AP
  27. enum RegStatus {
  28. REG_OK_IP = 2,
  29. REG_OK_TCP = 3,
  30. REG_OK_NO_TCP = 4,
  31. REG_DENIED = 5,
  32. REG_UNKNOWN = 6,
  33. };
  34. class TinyGsmESP8266 : public TinyGsmModem<TinyGsmESP8266>,
  35. public TinyGsmWifi<TinyGsmESP8266>,
  36. public TinyGsmTCP<TinyGsmESP8266, TINY_GSM_MUX_COUNT>,
  37. public TinyGsmSSL<TinyGsmESP8266> {
  38. friend class TinyGsmModem<TinyGsmESP8266>;
  39. friend class TinyGsmWifi<TinyGsmESP8266>;
  40. friend class TinyGsmTCP<TinyGsmESP8266, TINY_GSM_MUX_COUNT>;
  41. friend class TinyGsmSSL<TinyGsmESP8266>;
  42. /*
  43. * Inner Client
  44. */
  45. public:
  46. class GsmClientESP8266 : public GsmClient {
  47. friend class TinyGsmESP8266;
  48. public:
  49. GsmClientESP8266() {}
  50. explicit GsmClientESP8266(TinyGsmESP8266& modem, uint8_t mux = 0) {
  51. init(&modem, mux);
  52. }
  53. bool init(TinyGsmESP8266* modem, uint8_t mux = 0) {
  54. this->at = modem;
  55. sock_connected = false;
  56. if (mux < TINY_GSM_MUX_COUNT) {
  57. this->mux = mux;
  58. } else {
  59. this->mux = (mux % TINY_GSM_MUX_COUNT);
  60. }
  61. at->sockets[this->mux] = this;
  62. return true;
  63. }
  64. public:
  65. virtual int connect(const char* host, uint16_t port, int timeout_s) {
  66. stop();
  67. TINY_GSM_YIELD();
  68. rx.clear();
  69. sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
  70. return sock_connected;
  71. }
  72. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  73. void stop(uint32_t maxWaitMs) {
  74. TINY_GSM_YIELD();
  75. at->sendAT(GF("+CIPCLOSE="), mux);
  76. sock_connected = false;
  77. at->waitResponse(maxWaitMs);
  78. rx.clear();
  79. }
  80. void stop() override {
  81. stop(5000L);
  82. }
  83. /*
  84. * Extended API
  85. */
  86. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  87. };
  88. /*
  89. * Inner Secure Client
  90. */
  91. public:
  92. class GsmClientSecureESP8266 : public GsmClientESP8266 {
  93. public:
  94. GsmClientSecureESP8266() {}
  95. explicit GsmClientSecureESP8266(TinyGsmESP8266& modem, uint8_t mux = 0)
  96. : GsmClientESP8266(modem, mux) {}
  97. public:
  98. int connect(const char* host, uint16_t port, int timeout_s) override {
  99. stop();
  100. TINY_GSM_YIELD();
  101. rx.clear();
  102. sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
  103. return sock_connected;
  104. }
  105. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  106. };
  107. /*
  108. * Constructor
  109. */
  110. public:
  111. explicit TinyGsmESP8266(Stream& stream) : stream(stream) {
  112. memset(sockets, 0, sizeof(sockets));
  113. }
  114. /*
  115. * Basic functions
  116. */
  117. protected:
  118. bool initImpl(const char* pin = NULL) {
  119. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  120. DBG(GF("### TinyGSM Compiled Module: TinyGsmClientESP8266"));
  121. if (!testAT()) { return false; }
  122. if (pin && strlen(pin) > 0) {
  123. DBG("ESP8266 modules do not use an unlock pin!");
  124. }
  125. sendAT(GF("E0")); // Echo Off
  126. if (waitResponse() != 1) { return false; }
  127. sendAT(GF("+CIPMUX=1")); // Enable Multiple Connections
  128. if (waitResponse() != 1) { return false; }
  129. sendAT(GF("+CWMODE=1")); // Put into "station" mode
  130. if (waitResponse() != 1) {
  131. sendAT(GF("+CWMODE_CUR=1")); // Attempt "current" station mode command
  132. // for some firmware variants if needed
  133. if (waitResponse() != 1) { return false; }
  134. }
  135. DBG(GF("### Modem:"), getModemName());
  136. return true;
  137. }
  138. String getModemNameImpl() {
  139. return "ESP8266";
  140. }
  141. void setBaudImpl(uint32_t baud) {
  142. sendAT(GF("+UART_CUR="), baud, "8,1,0,0");
  143. if (waitResponse() != 1) {
  144. sendAT(GF("+UART="), baud,
  145. "8,1,0,0"); // Really old firmwares might need this
  146. // if (waitResponse() != 1) {
  147. // sendAT(GF("+IPR="), baud); // First release firmwares might need
  148. // this
  149. waitResponse();
  150. // }
  151. }
  152. }
  153. bool factoryDefaultImpl() {
  154. sendAT(GF("+RESTORE"));
  155. return waitResponse() == 1;
  156. }
  157. String getModemInfoImpl() {
  158. sendAT(GF("+GMR"));
  159. String res;
  160. if (waitResponse(1000L, res) != 1) { return ""; }
  161. res.replace(GSM_NL "OK" GSM_NL, "");
  162. res.replace(GSM_NL, " ");
  163. res.trim();
  164. return res;
  165. }
  166. /*
  167. * Power functions
  168. */
  169. protected:
  170. bool restartImpl(const char* pin = NULL) {
  171. if (!testAT()) { return false; }
  172. sendAT(GF("+RST"));
  173. if (waitResponse(10000L) != 1) { return false; }
  174. if (waitResponse(10000L, GF(GSM_NL "ready" GSM_NL)) != 1) { return false; }
  175. delay(500);
  176. return init(pin);
  177. }
  178. bool powerOffImpl() {
  179. sendAT(GF("+GSLP=0")); // Power down indefinitely - until manually reset!
  180. return waitResponse() == 1;
  181. }
  182. bool radioOffImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  183. bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE;
  184. bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false)
  185. TINY_GSM_ATTR_NOT_IMPLEMENTED;
  186. /*
  187. * Generic network functions
  188. */
  189. public:
  190. RegStatus getRegistrationStatus() {
  191. sendAT(GF("+CIPSTATUS"));
  192. if (waitResponse(3000, GF("STATUS:")) != 1) return REG_UNKNOWN;
  193. int8_t status = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"),
  194. GF("5"));
  195. waitResponse(); // Returns an OK after the status
  196. return (RegStatus)status;
  197. }
  198. protected:
  199. int8_t getSignalQualityImpl() {
  200. sendAT(GF("+CWJAP?"));
  201. int8_t res1 = waitResponse(GF("No AP"), GF("+CWJAP:"));
  202. if (res1 != 2) {
  203. waitResponse();
  204. sendAT(GF("+CWJAP_CUR?")); // attempt "current" as used by some firmware
  205. // versions
  206. int8_t res1 = waitResponse(GF("No AP"), GF("+CWJAP_CUR:"));
  207. if (res1 != 2) {
  208. waitResponse();
  209. return 0;
  210. }
  211. }
  212. streamSkipUntil(','); // Skip SSID
  213. streamSkipUntil(','); // Skip BSSID/MAC address
  214. streamSkipUntil(','); // Skip Chanel number
  215. int8_t res2 = stream.parseInt(); // Read RSSI
  216. waitResponse(); // Returns an OK after the value
  217. return res2;
  218. }
  219. bool isNetworkConnectedImpl() {
  220. RegStatus s = getRegistrationStatus();
  221. if (s == REG_OK_IP || s == REG_OK_TCP) {
  222. // with these, we're definitely connected
  223. return true;
  224. } else if (s == REG_OK_NO_TCP) {
  225. // with this, we may or may not be connected
  226. if (getLocalIP() == "") {
  227. return false;
  228. } else {
  229. return true;
  230. }
  231. } else {
  232. return false;
  233. }
  234. }
  235. String getLocalIPImpl() {
  236. // attempt with and without 'current' flag
  237. sendAT(GF("+CIPSTA?"));
  238. int8_t res1 = waitResponse(GF("ERROR"), GF("+CIPSTA:"));
  239. if (res1 != 2) {
  240. sendAT(GF("+CIPSTA_CUR?"));
  241. res1 = waitResponse(GF("ERROR"), GF("+CIPSTA_CUR:"));
  242. if (res1 != 2) { return ""; }
  243. }
  244. String res2 = stream.readStringUntil('\n');
  245. res2.replace("ip:", ""); // newer firmwares have this
  246. res2.replace("\"", "");
  247. res2.trim();
  248. waitResponse();
  249. return res2;
  250. }
  251. /*
  252. * WiFi functions
  253. */
  254. protected:
  255. bool networkConnectImpl(const char* ssid, const char* pwd) {
  256. // attempt first without than with the 'current' flag used in some firmware
  257. // versions
  258. sendAT(GF("+CWJAP=\""), ssid, GF("\",\""), pwd, GF("\""));
  259. if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) {
  260. sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\""));
  261. if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) {
  262. return false;
  263. }
  264. }
  265. return true;
  266. }
  267. bool networkDisconnectImpl() {
  268. sendAT(GF("+CWQAP"));
  269. bool retVal = waitResponse(10000L) == 1;
  270. waitResponse(GF("WIFI DISCONNECT"));
  271. return retVal;
  272. }
  273. /*
  274. * Client related functions
  275. */
  276. protected:
  277. bool modemConnect(const char* host, uint16_t port, uint8_t mux,
  278. bool ssl = false, int timeout_s = 75) {
  279. uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
  280. if (ssl) {
  281. sendAT(GF("+CIPSSLSIZE=4096"));
  282. waitResponse();
  283. }
  284. sendAT(GF("+CIPSTART="), mux, ',', ssl ? GF("\"SSL") : GF("\"TCP"),
  285. GF("\",\""), host, GF("\","), port, GF(","),
  286. TINY_GSM_TCP_KEEP_ALIVE);
  287. // TODO(?): Check mux
  288. int8_t rsp = waitResponse(timeout_ms, GFP(GSM_OK), GFP(GSM_ERROR),
  289. GF("ALREADY CONNECT"));
  290. // if (rsp == 3) waitResponse();
  291. // May return "ERROR" after the "ALREADY CONNECT"
  292. return (1 == rsp);
  293. }
  294. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  295. sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
  296. if (waitResponse(GF(">")) != 1) { return 0; }
  297. stream.write(reinterpret_cast<const uint8_t*>(buff), len);
  298. stream.flush();
  299. if (waitResponse(10000L, GF(GSM_NL "SEND OK" GSM_NL)) != 1) { return 0; }
  300. return len;
  301. }
  302. bool modemGetConnected(uint8_t mux) {
  303. sendAT(GF("+CIPSTATUS"));
  304. if (waitResponse(3000, GF("STATUS:")) != 1) { return false; }
  305. int8_t status = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"),
  306. GF("5"));
  307. if (status != 3) {
  308. // if the status is anything but 3, there are no connections open
  309. waitResponse(); // Returns an OK after the status
  310. for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
  311. if (sockets[muxNo]) { sockets[muxNo]->sock_connected = false; }
  312. }
  313. return false;
  314. }
  315. bool verified_connections[TINY_GSM_MUX_COUNT] = {0, 0, 0, 0, 0};
  316. for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
  317. uint8_t has_status = waitResponse(GF("+CIPSTATUS:"), GFP(GSM_OK),
  318. GFP(GSM_ERROR));
  319. if (has_status == 1) {
  320. int8_t returned_mux = streamGetIntBefore(',');
  321. streamSkipUntil(','); // Skip mux
  322. streamSkipUntil(','); // Skip type
  323. streamSkipUntil(','); // Skip remote IP
  324. streamSkipUntil(','); // Skip remote port
  325. streamSkipUntil(','); // Skip local port
  326. streamSkipUntil('\n'); // Skip client/server type
  327. verified_connections[returned_mux] = 1;
  328. }
  329. if (has_status == 2) break; // once we get to the ok, stop
  330. }
  331. for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
  332. if (sockets[muxNo]) {
  333. sockets[muxNo]->sock_connected = verified_connections[muxNo];
  334. }
  335. }
  336. return verified_connections[mux];
  337. }
  338. /*
  339. * Utilities
  340. */
  341. public:
  342. // TODO(vshymanskyy): Optimize this!
  343. int8_t waitResponse(uint32_t timeout_ms, String& data,
  344. GsmConstStr r1 = GFP(GSM_OK),
  345. GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL,
  346. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  347. /*String r1s(r1); r1s.trim();
  348. String r2s(r2); r2s.trim();
  349. String r3s(r3); r3s.trim();
  350. String r4s(r4); r4s.trim();
  351. String r5s(r5); r5s.trim();
  352. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  353. data.reserve(64);
  354. uint8_t index = 0;
  355. uint32_t startMillis = millis();
  356. do {
  357. TINY_GSM_YIELD();
  358. while (stream.available() > 0) {
  359. TINY_GSM_YIELD();
  360. int8_t a = stream.read();
  361. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  362. data += static_cast<char>(a);
  363. if (r1 && data.endsWith(r1)) {
  364. index = 1;
  365. goto finish;
  366. } else if (r2 && data.endsWith(r2)) {
  367. index = 2;
  368. goto finish;
  369. } else if (r3 && data.endsWith(r3)) {
  370. index = 3;
  371. goto finish;
  372. } else if (r4 && data.endsWith(r4)) {
  373. index = 4;
  374. goto finish;
  375. } else if (r5 && data.endsWith(r5)) {
  376. index = 5;
  377. goto finish;
  378. } else if (data.endsWith(GF("+IPD,"))) {
  379. int8_t mux = streamGetIntBefore(',');
  380. int16_t len = streamGetIntBefore(':');
  381. int16_t len_orig = len;
  382. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  383. if (len > sockets[mux]->rx.free()) {
  384. DBG("### Buffer overflow: ", len, "received vs",
  385. sockets[mux]->rx.free(), "available");
  386. } else {
  387. // DBG("### Got Data: ", len, "on", mux);
  388. }
  389. while (len--) { moveCharFromStreamToFifo(mux); }
  390. // TODO(SRGDamia1): deal with buffer overflow/missed characters
  391. if (len_orig > sockets[mux]->available()) {
  392. DBG("### Fewer characters received than expected: ",
  393. sockets[mux]->available(), " vs ", len_orig);
  394. }
  395. }
  396. data = "";
  397. } else if (data.endsWith(GF("CLOSED"))) {
  398. int8_t muxStart =
  399. TinyGsmMax(0, data.lastIndexOf(GSM_NL, data.length() - 8));
  400. int8_t coma = data.indexOf(',', muxStart);
  401. int8_t mux = data.substring(muxStart, coma).toInt();
  402. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  403. sockets[mux]->sock_connected = false;
  404. }
  405. data = "";
  406. DBG("### Closed: ", mux);
  407. }
  408. }
  409. } while (millis() - startMillis < timeout_ms);
  410. finish:
  411. if (!index) {
  412. data.trim();
  413. if (data.length()) { DBG("### Unhandled:", data); }
  414. data = "";
  415. }
  416. // data.replace(GSM_NL, "/");
  417. // DBG('<', index, '>', data);
  418. return index;
  419. }
  420. int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
  421. GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL,
  422. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  423. String data;
  424. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  425. }
  426. int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
  427. GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL,
  428. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  429. return waitResponse(1000, r1, r2, r3, r4, r5);
  430. }
  431. public:
  432. Stream& stream;
  433. protected:
  434. GsmClientESP8266* sockets[TINY_GSM_MUX_COUNT];
  435. const char* gsmNL = GSM_NL;
  436. };
  437. #endif // SRC_TINYGSMCLIENTESP8266_H_