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.

383 lines
8.6 KiB

8 years ago
8 years ago
8 years ago
  1. /**
  2. * @file TinyWiFiClientESP8266.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef TinyWiFiClientESP8266_h
  9. #define TinyWiFiClientESP8266_h
  10. //#define GSM_DEBUG Serial
  11. //#define GSM_USE_HEX
  12. #if !defined(TINY_GSM_RX_BUFFER)
  13. #define TINY_GSM_RX_BUFFER 256
  14. #endif
  15. #define GSM_NL "\r\n"
  16. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  17. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  18. class TinyGsm
  19. {
  20. #ifdef GSM_DEBUG
  21. template<typename T>
  22. static void DBG(T last) {
  23. GSM_DEBUG.println(last);
  24. }
  25. template<typename T, typename... Args>
  26. static void DBG(T head, Args... tail) {
  27. GSM_DEBUG.print(head);
  28. GSM_DEBUG.print(' ');
  29. DBG(tail...);
  30. }
  31. #else
  32. #define DBG(...)
  33. #endif
  34. public:
  35. TinyGsm(Stream& stream)
  36. : stream(stream)
  37. {}
  38. public:
  39. class GsmClient : public Client
  40. {
  41. friend class TinyGsm;
  42. typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
  43. public:
  44. GsmClient() {
  45. init(NULL, -1);
  46. }
  47. GsmClient(TinyGsm& at, uint8_t mux = 1) {
  48. init(&at, mux);
  49. }
  50. bool init(TinyGsm* at, uint8_t mux = 1) {
  51. this->at = at;
  52. this->mux = mux;
  53. at->sockets[mux] = this;
  54. sock_connected = false;
  55. return true;
  56. }
  57. public:
  58. virtual int connect(const char *host, uint16_t port) {
  59. rx.clear();
  60. sock_connected = at->modemConnect(host, port, mux);
  61. return sock_connected;
  62. }
  63. virtual int connect(IPAddress ip, uint16_t port) {
  64. String host; host.reserve(16);
  65. host += ip[0];
  66. host += ".";
  67. host += ip[1];
  68. host += ".";
  69. host += ip[2];
  70. host += ".";
  71. host += ip[3];
  72. return connect(host.c_str(), port);
  73. }
  74. virtual void stop() {
  75. at->sendAT(GF("+CIPCLOSE="), mux);
  76. sock_connected = false;
  77. at->waitResponse();
  78. }
  79. virtual size_t write(const uint8_t *buf, size_t size) {
  80. //at->maintain();
  81. return at->modemSend(buf, size, mux);
  82. }
  83. virtual size_t write(uint8_t c) {
  84. return write(&c, 1);
  85. }
  86. virtual int available() {
  87. if (!rx.size()) {
  88. at->maintain();
  89. }
  90. return rx.size();
  91. }
  92. virtual int read(uint8_t *buf, size_t size) {
  93. size_t cnt = 0;
  94. while (cnt < size) {
  95. size_t chunk = min(size-cnt, rx.size());
  96. if (chunk > 0) {
  97. rx.get(buf, chunk);
  98. buf += chunk;
  99. cnt += chunk;
  100. continue;
  101. }
  102. // TODO: Read directly into user buffer?
  103. if (!rx.size()) {
  104. at->maintain();
  105. //break;
  106. }
  107. }
  108. return cnt;
  109. }
  110. virtual int read() {
  111. uint8_t c;
  112. if (read(&c, 1) == 1) {
  113. return c;
  114. }
  115. return -1;
  116. }
  117. virtual int peek() { return -1; } //TODO
  118. virtual void flush() { at->stream.flush(); }
  119. virtual uint8_t connected() {
  120. if (available()) {
  121. return true;
  122. }
  123. return sock_connected;
  124. }
  125. virtual operator bool() { return connected(); }
  126. private:
  127. TinyGsm* at;
  128. uint8_t mux;
  129. bool sock_connected;
  130. RxFifo rx;
  131. };
  132. public:
  133. /*
  134. * Basic functions
  135. */
  136. bool begin() {
  137. return init();
  138. }
  139. bool init() {
  140. if (!autoBaud()) {
  141. return false;
  142. }
  143. return true;
  144. }
  145. bool autoBaud(unsigned long timeout = 10000L) {
  146. for (unsigned long start = millis(); millis() - start < timeout; ) {
  147. sendAT(GF("E0"));
  148. if (waitResponse(200) == 1) {
  149. delay(100);
  150. return true;
  151. }
  152. delay(100);
  153. }
  154. return false;
  155. }
  156. void maintain() {
  157. //while (stream.available()) {
  158. waitResponse(10, NULL, NULL);
  159. //}
  160. }
  161. bool factoryDefault() {
  162. sendAT(GF("+RESTORE"));
  163. return waitResponse() == 1;
  164. }
  165. /*
  166. * Power functions
  167. */
  168. bool restart() {
  169. if (!autoBaud()) {
  170. return false;
  171. }
  172. sendAT(GF("+RST"));
  173. if (waitResponse(10000L) != 1) {
  174. return false;
  175. }
  176. if (waitResponse(10000L, GF(GSM_NL "ready" GSM_NL)) != 1) {
  177. return false;
  178. }
  179. delay(500);
  180. return autoBaud();
  181. }
  182. bool waitForNetwork(unsigned long timeout = 60000L) {
  183. return true;
  184. }
  185. /*
  186. * WiFi functions
  187. */
  188. bool networkConnect(const char* ssid, const char* pwd) {
  189. sendAT(GF("+CIPMUX=1"));
  190. if (waitResponse() != 1) {
  191. return false;
  192. }
  193. sendAT(GF("+CWMODE_CUR=1"));
  194. if (waitResponse() != 1) {
  195. return false;
  196. }
  197. sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\""));
  198. if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) {
  199. return false;
  200. }
  201. return true;
  202. }
  203. bool networkDisconnect() {
  204. sendAT(GF("+CWQAP"));
  205. return waitResponse(10000L) == 1;
  206. }
  207. private:
  208. int modemConnect(const char* host, uint16_t port, uint8_t mux) {
  209. sendAT(GF("+CIPSTART="), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(",120"));
  210. int rsp = waitResponse(75000L,
  211. GFP(GSM_OK),
  212. GFP(GSM_ERROR),
  213. GF(GSM_NL "ALREADY CONNECT" GSM_NL));
  214. return (1 == rsp);
  215. }
  216. int modemSend(const void* buff, size_t len, uint8_t mux) {
  217. sendAT(GF("+CIPSEND="), mux, ',', len);
  218. if (waitResponse(GF(">")) != 1) {
  219. return -1;
  220. }
  221. stream.write((uint8_t*)buff, len);
  222. if (waitResponse(GF(GSM_NL "SEND OK" GSM_NL)) != 1) {
  223. return -1;
  224. }
  225. return len;
  226. }
  227. bool modemGetConnected(uint8_t mux) {
  228. sendAT(GF("+CIPSTATUS="), mux);
  229. int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\""));
  230. waitResponse();
  231. return 1 == res;
  232. }
  233. /* Utilities */
  234. template<typename T>
  235. void streamWrite(T last) {
  236. stream.print(last);
  237. }
  238. template<typename T, typename... Args>
  239. void streamWrite(T head, Args... tail) {
  240. stream.print(head);
  241. streamWrite(tail...);
  242. }
  243. int streamRead() { return stream.read(); }
  244. template<typename... Args>
  245. void sendAT(Args... cmd) {
  246. streamWrite("AT", cmd..., GSM_NL);
  247. stream.flush();
  248. TINY_GSM_YIELD();
  249. //DBG("### AT:", cmd...);
  250. }
  251. // TODO: Optimize this!
  252. uint8_t waitResponse(uint32_t timeout, String& data,
  253. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  254. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  255. {
  256. /*String r1s(r1); r1s.trim();
  257. String r2s(r2); r2s.trim();
  258. String r3s(r3); r3s.trim();
  259. String r4s(r4); r4s.trim();
  260. String r5s(r5); r5s.trim();
  261. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  262. data.reserve(64);
  263. int index = 0;
  264. unsigned long startMillis = millis();
  265. do {
  266. TINY_GSM_YIELD();
  267. while (stream.available() > 0) {
  268. int a = streamRead();
  269. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  270. data += (char)a;
  271. if (r1 && data.endsWith(r1)) {
  272. index = 1;
  273. goto finish;
  274. } else if (r2 && data.endsWith(r2)) {
  275. index = 2;
  276. goto finish;
  277. } else if (r3 && data.endsWith(r3)) {
  278. index = 3;
  279. goto finish;
  280. } else if (r4 && data.endsWith(r4)) {
  281. index = 4;
  282. goto finish;
  283. } else if (r5 && data.endsWith(r5)) {
  284. index = 5;
  285. goto finish;
  286. } else if (data.endsWith(GF(GSM_NL "+IPD,"))) {
  287. int mux = stream.readStringUntil(',').toInt();
  288. int len = stream.readStringUntil(':').toInt();
  289. if (len > sockets[mux]->rx.free()) {
  290. DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
  291. } else {
  292. DBG("### Got: ", len, "->", sockets[mux]->rx.free());
  293. }
  294. while (len--) {
  295. while (!stream.available()) {}
  296. sockets[mux]->rx.put(stream.read());
  297. }
  298. data = "";
  299. return index;
  300. } else if (data.endsWith(GF(GSM_NL "1,CLOSED" GSM_NL))) { //TODO: use mux
  301. sockets[1]->sock_connected = false;
  302. data = "";
  303. }
  304. }
  305. } while (millis() - startMillis < timeout);
  306. finish:
  307. if (!index) {
  308. if (data.length()) {
  309. DBG("### Unhandled:", data);
  310. }
  311. data = "";
  312. }
  313. return index;
  314. }
  315. uint8_t waitResponse(uint32_t timeout,
  316. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  317. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  318. {
  319. String data;
  320. return waitResponse(timeout, data, r1, r2, r3, r4, r5);
  321. }
  322. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  323. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  324. {
  325. return waitResponse(1000, r1, r2, r3, r4, r5);
  326. }
  327. private:
  328. Stream& stream;
  329. GsmClient* sockets[5];
  330. };
  331. typedef TinyGsm::GsmClient TinyGsmClient;
  332. #endif