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.

454 lines
13 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
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
8 years ago
8 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_CUR=1")); // Put into "station" mode
  130. if (waitResponse() != 1) { return false; }
  131. DBG(GF("### Modem:"), getModemName());
  132. return true;
  133. }
  134. String getModemNameImpl() {
  135. return "ESP8266";
  136. }
  137. void setBaudImpl(uint32_t baud) {
  138. sendAT(GF("+UART_CUR="), baud, "8,1,0,0");
  139. }
  140. bool factoryDefaultImpl() {
  141. sendAT(GF("+RESTORE"));
  142. return waitResponse() == 1;
  143. }
  144. String getModemInfoImpl() {
  145. sendAT(GF("+GMR"));
  146. String res;
  147. if (waitResponse(1000L, res) != 1) { return ""; }
  148. res.replace(GSM_NL "OK" GSM_NL, "");
  149. res.replace(GSM_NL, " ");
  150. res.trim();
  151. return res;
  152. }
  153. /*
  154. * Power functions
  155. */
  156. protected:
  157. bool restartImpl() {
  158. if (!testAT()) { return false; }
  159. sendAT(GF("+RST"));
  160. if (waitResponse(10000L) != 1) { return false; }
  161. if (waitResponse(10000L, GF(GSM_NL "ready" GSM_NL)) != 1) { return false; }
  162. delay(500);
  163. return init();
  164. }
  165. bool powerOffImpl() {
  166. sendAT(GF("+GSLP=0")); // Power down indefinitely - until manually reset!
  167. return waitResponse() == 1;
  168. }
  169. bool radioOffImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  170. bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE;
  171. /*
  172. * Generic network functions
  173. */
  174. public:
  175. RegStatus getRegistrationStatus() {
  176. sendAT(GF("+CIPSTATUS"));
  177. if (waitResponse(3000, GF("STATUS:")) != 1) return REG_UNKNOWN;
  178. int8_t status = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"),
  179. GF("5"));
  180. waitResponse(); // Returns an OK after the status
  181. return (RegStatus)status;
  182. }
  183. protected:
  184. int8_t getSignalQualityImpl() {
  185. sendAT(GF("+CWJAP_CUR?"));
  186. int8_t res1 = waitResponse(GF("No AP"), GF("+CWJAP_CUR:"));
  187. if (res1 != 2) {
  188. waitResponse();
  189. return 0;
  190. }
  191. streamSkipUntil(','); // Skip SSID
  192. streamSkipUntil(','); // Skip BSSID/MAC address
  193. streamSkipUntil(','); // Skip Chanel number
  194. int8_t res2 = stream.parseInt(); // Read RSSI
  195. waitResponse(); // Returns an OK after the value
  196. return res2;
  197. }
  198. bool isNetworkConnectedImpl() {
  199. RegStatus s = getRegistrationStatus();
  200. if (s == REG_OK_IP || s == REG_OK_TCP) {
  201. // with these, we're definitely connected
  202. return true;
  203. } else if (s == REG_OK_NO_TCP) {
  204. // with this, we may or may not be connected
  205. if (getLocalIP() == "") {
  206. return false;
  207. } else {
  208. return true;
  209. }
  210. } else {
  211. return false;
  212. }
  213. }
  214. String getLocalIPImpl() {
  215. sendAT(GF("+CIPSTA_CUR?"));
  216. int8_t res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:"));
  217. if (res1 != 2) { return ""; }
  218. String res2 = stream.readStringUntil('"');
  219. waitResponse();
  220. return res2;
  221. }
  222. /*
  223. * WiFi functions
  224. */
  225. protected:
  226. bool networkConnectImpl(const char* ssid, const char* pwd) {
  227. sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\""));
  228. if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) {
  229. return false;
  230. }
  231. return true;
  232. }
  233. bool networkDisconnectImpl() {
  234. sendAT(GF("+CWQAP"));
  235. bool retVal = waitResponse(10000L) == 1;
  236. waitResponse(GF("WIFI DISCONNECT"));
  237. return retVal;
  238. }
  239. /*
  240. * Client related functions
  241. */
  242. protected:
  243. bool modemConnect(const char* host, uint16_t port, uint8_t mux,
  244. bool ssl = false, int timeout_s = 75) {
  245. uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
  246. if (ssl) {
  247. sendAT(GF("+CIPSSLSIZE=4096"));
  248. waitResponse();
  249. }
  250. sendAT(GF("+CIPSTART="), mux, ',', ssl ? GF("\"SSL") : GF("\"TCP"),
  251. GF("\",\""), host, GF("\","), port, GF(","),
  252. TINY_GSM_TCP_KEEP_ALIVE);
  253. // TODO(?): Check mux
  254. int8_t rsp = waitResponse(timeout_ms, GFP(GSM_OK), GFP(GSM_ERROR),
  255. GF("ALREADY CONNECT"));
  256. // if (rsp == 3) waitResponse();
  257. // May return "ERROR" after the "ALREADY CONNECT"
  258. return (1 == rsp);
  259. }
  260. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  261. sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
  262. if (waitResponse(GF(">")) != 1) { return 0; }
  263. stream.write(reinterpret_cast<const uint8_t*>(buff), len);
  264. stream.flush();
  265. if (waitResponse(10000L, GF(GSM_NL "SEND OK" GSM_NL)) != 1) { return 0; }
  266. return len;
  267. }
  268. bool modemGetConnected(uint8_t mux) {
  269. sendAT(GF("+CIPSTATUS"));
  270. if (waitResponse(3000, GF("STATUS:")) != 1) { return false; }
  271. int8_t status = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"),
  272. GF("5"));
  273. if (status != 3) {
  274. // if the status is anything but 3, there are no connections open
  275. waitResponse(); // Returns an OK after the status
  276. for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
  277. if (sockets[muxNo]) {
  278. sockets[muxNo]->sock_connected = false;
  279. }
  280. }
  281. return false;
  282. }
  283. bool verified_connections[TINY_GSM_MUX_COUNT] = {0, 0, 0, 0, 0};
  284. for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
  285. uint8_t has_status = waitResponse(GF("+CIPSTATUS:"), GFP(GSM_OK),
  286. GFP(GSM_ERROR));
  287. if (has_status == 1) {
  288. int8_t returned_mux = streamGetIntBefore(',');
  289. streamSkipUntil(','); // Skip mux
  290. streamSkipUntil(','); // Skip type
  291. streamSkipUntil(','); // Skip remote IP
  292. streamSkipUntil(','); // Skip remote port
  293. streamSkipUntil(','); // Skip local port
  294. streamSkipUntil('\n'); // Skip client/server type
  295. verified_connections[returned_mux] = 1;
  296. }
  297. if (has_status == 2) break; // once we get to the ok, stop
  298. }
  299. for (int muxNo = 0; muxNo < TINY_GSM_MUX_COUNT; muxNo++) {
  300. if (sockets[muxNo]) {
  301. sockets[muxNo]->sock_connected = verified_connections[muxNo];
  302. }
  303. }
  304. return verified_connections[mux];
  305. }
  306. /*
  307. * Utilities
  308. */
  309. public:
  310. // TODO(vshymanskyy): Optimize this!
  311. int8_t waitResponse(uint32_t timeout_ms, String& data,
  312. GsmConstStr r1 = GFP(GSM_OK),
  313. GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL,
  314. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  315. /*String r1s(r1); r1s.trim();
  316. String r2s(r2); r2s.trim();
  317. String r3s(r3); r3s.trim();
  318. String r4s(r4); r4s.trim();
  319. String r5s(r5); r5s.trim();
  320. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  321. data.reserve(64);
  322. uint8_t index = 0;
  323. uint32_t startMillis = millis();
  324. do {
  325. TINY_GSM_YIELD();
  326. while (stream.available() > 0) {
  327. TINY_GSM_YIELD();
  328. int8_t a = stream.read();
  329. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  330. data += static_cast<char>(a);
  331. if (r1 && data.endsWith(r1)) {
  332. index = 1;
  333. goto finish;
  334. } else if (r2 && data.endsWith(r2)) {
  335. index = 2;
  336. goto finish;
  337. } else if (r3 && data.endsWith(r3)) {
  338. index = 3;
  339. goto finish;
  340. } else if (r4 && data.endsWith(r4)) {
  341. index = 4;
  342. goto finish;
  343. } else if (r5 && data.endsWith(r5)) {
  344. index = 5;
  345. goto finish;
  346. } else if (data.endsWith(GF("+IPD,"))) {
  347. int8_t mux = streamGetIntBefore(',');
  348. int16_t len = streamGetIntBefore(':');
  349. int16_t len_orig = len;
  350. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  351. if (len > sockets[mux]->rx.free()) {
  352. DBG("### Buffer overflow: ", len, "received vs",
  353. sockets[mux]->rx.free(), "available");
  354. } else {
  355. // DBG("### Got Data: ", len, "on", mux);
  356. }
  357. while (len--) {
  358. moveCharFromStreamToFifo(mux);
  359. }
  360. // TODO(SRGDamia1): deal with buffer overflow/missed characters
  361. if (len_orig > sockets[mux]->available()) {
  362. DBG("### Fewer characters received than expected: ",
  363. sockets[mux]->available(), " vs ", len_orig);
  364. }
  365. }
  366. data = "";
  367. } else if (data.endsWith(GF("CLOSED"))) {
  368. int8_t muxStart =
  369. TinyGsmMax(0, data.lastIndexOf(GSM_NL, data.length() - 8));
  370. int8_t coma = data.indexOf(',', muxStart);
  371. int8_t mux = data.substring(muxStart, coma).toInt();
  372. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  373. sockets[mux]->sock_connected = false;
  374. }
  375. data = "";
  376. DBG("### Closed: ", mux);
  377. }
  378. }
  379. } while (millis() - startMillis < timeout_ms);
  380. finish:
  381. if (!index) {
  382. data.trim();
  383. if (data.length()) { DBG("### Unhandled:", data); }
  384. data = "";
  385. }
  386. // data.replace(GSM_NL, "/");
  387. // DBG('<', index, '>', data);
  388. return index;
  389. }
  390. int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
  391. GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL,
  392. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  393. String data;
  394. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  395. }
  396. int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
  397. GsmConstStr r2 = GFP(GSM_ERROR), GsmConstStr r3 = NULL,
  398. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  399. return waitResponse(1000, r1, r2, r3, r4, r5);
  400. }
  401. public:
  402. Stream& stream;
  403. protected:
  404. GsmClientESP8266* sockets[TINY_GSM_MUX_COUNT];
  405. const char* gsmNL = GSM_NL;
  406. };
  407. #endif // SRC_TINYGSMCLIENTESP8266_H_