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.

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