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