MySensors Library & Examples  2.3.2
TinyGsmClientA6.h
1 
9 #ifndef TinyGsmClientA6_h
10 #define TinyGsmClientA6_h
11 
12 //#define TINY_GSM_DEBUG Serial
13 
14 #if !defined(TINY_GSM_RX_BUFFER)
15 #define TINY_GSM_RX_BUFFER 256
16 #endif
17 
18 #define TINY_GSM_MUX_COUNT 8
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 
26 enum SimStatus {
27  SIM_ERROR = 0,
28  SIM_READY = 1,
29  SIM_LOCKED = 2,
30 };
31 
32 enum RegStatus {
33  REG_UNREGISTERED = 0,
34  REG_SEARCHING = 2,
35  REG_DENIED = 3,
36  REG_OK_HOME = 1,
37  REG_OK_ROAMING = 5,
38  REG_UNKNOWN = 4,
39 };
40 
41 
42 class TinyGsm
43 {
44 
45 public:
46 
47  class GsmClient : public Client
48  {
49  friend class TinyGsm;
51 
52  public:
53  GsmClient() {}
54 
55  GsmClient(TinyGsm& modem)
56  {
57  init(&modem);
58  }
59 
60  bool init(TinyGsm* modem)
61  {
62  this->at = modem;
63  this->mux = -1;
64  sock_connected = false;
65 
66  return true;
67  }
68 
69  public:
70  virtual int connect(const char *host, uint16_t port)
71  {
72  stop();
73  TINY_GSM_YIELD();
74  rx.clear();
75  uint8_t newMux = -1;
76  sock_connected = at->modemConnect(host, port, &newMux);
77  if (sock_connected) {
78  mux = newMux;
79  at->sockets[mux] = this;
80  }
81  return sock_connected;
82  }
83 
84  virtual int connect(IPAddress ip, uint16_t port)
85  {
86  String host;
87  host.reserve(16);
88  host += ip[0];
89  host += ".";
90  host += ip[1];
91  host += ".";
92  host += ip[2];
93  host += ".";
94  host += ip[3];
95  return connect(host.c_str(), port);
96  }
97 
98  virtual void stop()
99  {
100  TINY_GSM_YIELD();
101  at->sendAT(GF("+CIPCLOSE="), mux);
102  sock_connected = false;
103  at->waitResponse();
104  rx.clear();
105  }
106 
107  virtual size_t write(const uint8_t *buf, size_t size)
108  {
109  TINY_GSM_YIELD();
110  //at->maintain();
111  return at->modemSend(buf, size, mux);
112  }
113 
114  virtual size_t write(uint8_t c)
115  {
116  return write(&c, 1);
117  }
118 
119  virtual int available()
120  {
121  TINY_GSM_YIELD();
122  if (!rx.size() && sock_connected) {
123  at->maintain();
124  }
125  return rx.size();
126  }
127 
128  virtual int read(uint8_t *buf, size_t size)
129  {
130  TINY_GSM_YIELD();
131  size_t cnt = 0;
132  while (cnt < size) {
133  size_t chunk = TinyGsmMin(size-cnt, rx.size());
134  if (chunk > 0) {
135  rx.get(buf, chunk);
136  buf += chunk;
137  cnt += chunk;
138  continue;
139  }
140  // TODO: Read directly into user buffer?
141  if (!rx.size() && sock_connected) {
142  at->maintain();
143  //break;
144  }
145  }
146  return cnt;
147  }
148 
149  virtual int read()
150  {
151  uint8_t c;
152  if (read(&c, 1) == 1) {
153  return c;
154  }
155  return -1;
156  }
157 
158  virtual int peek()
159  {
160  return -1; //TODO
161  }
162  virtual void flush()
163  {
164  at->stream.flush();
165  }
166 
167  virtual uint8_t connected()
168  {
169  if (available()) {
170  return true;
171  }
172  return sock_connected;
173  }
174  virtual operator bool()
175  {
176  return connected();
177  }
178 
179  /*
180  * Extended API
181  */
182 
183  String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
184 
185  private:
186  TinyGsm* at;
187  uint8_t mux;
188  bool sock_connected;
189  RxFifo rx;
190  };
191 
192 public:
193 
194  explicit TinyGsm(Stream& stream)
195  : stream(stream)
196  {
197  memset(sockets, 0, sizeof(sockets));
198  }
199 
200  /*
201  * Basic functions
202  */
203  bool begin()
204  {
205  return init();
206  }
207 
208  bool init()
209  {
210  if (!testAT()) {
211  return false;
212  }
213  sendAT(GF("&FZE0")); // Factory + Reset + Echo Off
214  if (waitResponse() != 1) {
215  return false;
216  }
217  sendAT(GF("+CMEE=0"));
218  waitResponse();
219 
220  sendAT(GF("+CMER=3,0,0,2"));
221  waitResponse();
222 
223  getSimStatus();
224  return true;
225  }
226 
227  void setBaud(unsigned long baud)
228  {
229  sendAT(GF("+IPR="), baud);
230  }
231 
232  bool testAT(unsigned long timeout = 10000L)
233  {
234  for (unsigned long start = millis(); millis() - start < timeout; ) {
235  sendAT(GF(""));
236  if (waitResponse(200) == 1) {
237  delay(100);
238  return true;
239  }
240  delay(100);
241  }
242  return false;
243  }
244 
245  void maintain()
246  {
247  waitResponse(10, NULL, NULL);
248  }
249 
250  bool factoryDefault()
251  {
252  sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
253  waitResponse();
254  sendAT(GF("&W")); // Write configuration
255  return waitResponse() == 1;
256  }
257 
258  String getModemInfo()
259  {
260  sendAT(GF("I"));
261  String res;
262  if (waitResponse(1000L, res) != 1) {
263  return "";
264  }
265  res.replace(GSM_NL "OK" GSM_NL, "");
266  res.replace(GSM_NL, " ");
267  res.trim();
268  return res;
269  }
270 
271  bool hasSSL()
272  {
273  return false;
274  }
275 
276  /*
277  * Power functions
278  */
279 
280  bool restart()
281  {
282  if (!testAT()) {
283  return false;
284  }
285  sendAT(GF("+RST=1"));
286  delay(3000);
287  return init();
288  }
289 
290  bool poweroff()
291  {
292  sendAT(GF("+CPOF"));
293  return waitResponse() == 1;
294  }
295 
296  bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
297 
298  bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
299 
300  /*
301  * SIM card functions
302  */
303 
304  bool simUnlock(const char *pin)
305  {
306  sendAT(GF("+CPIN=\""), pin, GF("\""));
307  return waitResponse() == 1;
308  }
309 
310  String getSimCCID()
311  {
312  sendAT(GF("+CCID"));
313  if (waitResponse(GF(GSM_NL "+SCID: SIM Card ID:")) != 1) {
314  return "";
315  }
316  String res = stream.readStringUntil('\n');
317  waitResponse();
318  res.trim();
319  return res;
320  }
321 
322  String getIMEI()
323  {
324  sendAT(GF("+GSN"));
325  if (waitResponse(GF(GSM_NL)) != 1) {
326  return "";
327  }
328  String res = stream.readStringUntil('\n');
329  waitResponse();
330  res.trim();
331  return res;
332  }
333 
334  SimStatus getSimStatus(unsigned long timeout = 10000L)
335  {
336  for (unsigned long start = millis(); millis() - start < timeout; ) {
337  sendAT(GF("+CPIN?"));
338  if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
339  delay(1000);
340  continue;
341  }
342  int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"));
343  waitResponse();
344  switch (status) {
345  case 2:
346  case 3:
347  return SIM_LOCKED;
348  case 1:
349  return SIM_READY;
350  default:
351  return SIM_ERROR;
352  }
353  }
354  return SIM_ERROR;
355  }
356 
357  RegStatus getRegistrationStatus()
358  {
359  sendAT(GF("+CREG?"));
360  if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
361  return REG_UNKNOWN;
362  }
363  streamSkipUntil(','); // Skip format (0)
364  int status = stream.readStringUntil('\n').toInt();
365  waitResponse();
366  return (RegStatus)status;
367  }
368 
369  String getOperator()
370  {
371  sendAT(GF("+COPS=3,0")); // Set format
372  waitResponse();
373 
374  sendAT(GF("+COPS?"));
375  if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
376  return "";
377  }
378  streamSkipUntil('"'); // Skip mode and format
379  String res = stream.readStringUntil('"');
380  waitResponse();
381  return res;
382  }
383 
384  /*
385  * Generic network functions
386  */
387 
388  int getSignalQuality()
389  {
390  sendAT(GF("+CSQ"));
391  if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
392  return 99;
393  }
394  int res = stream.readStringUntil(',').toInt();
395  waitResponse();
396  return res;
397  }
398 
399  bool isNetworkConnected()
400  {
401  RegStatus s = getRegistrationStatus();
402  return (s == REG_OK_HOME || s == REG_OK_ROAMING);
403  }
404 
405  bool waitForNetwork(unsigned long timeout = 60000L)
406  {
407  for (unsigned long start = millis(); millis() - start < timeout; ) {
408  if (isNetworkConnected()) {
409  return true;
410  }
411  delay(250);
412  }
413  return false;
414  }
415 
416  /*
417  * GPRS functions
418  */
419  bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL)
420  {
421  gprsDisconnect();
422 
423  sendAT(GF("+CGATT=1"));
424  if (waitResponse(60000L) != 1) {
425  return false;
426  }
427 
428  // TODO: wait AT+CGATT?
429 
430  sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
431  waitResponse();
432 
433  if (!user) {
434  user = "";
435  }
436  if (!pwd) {
437  pwd = "";
438  }
439  sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
440  if (waitResponse(60000L) != 1) {
441  return false;
442  }
443 
444  sendAT(GF("+CGACT=1,1"));
445  waitResponse(60000L);
446 
447  sendAT(GF("+CIPMUX=1"));
448  if (waitResponse() != 1) {
449  return false;
450  }
451 
452  return true;
453  }
454 
455  bool gprsDisconnect()
456  {
457  // Shut the TCP/IP connection
458  sendAT(GF("+CIPSHUT"));
459  if (waitResponse(60000L) != 1) {
460  return false;
461  }
462 
463  for (int i = 0; i<3; i++) {
464  sendAT(GF("+CGATT=0"));
465  if (waitResponse(5000L) == 1) {
466  return true;
467  }
468  }
469 
470  return false;
471  }
472 
473  bool isGprsConnected()
474  {
475  sendAT(GF("+CGATT?"));
476  if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
477  return false;
478  }
479  int res = stream.readStringUntil('\n').toInt();
480  waitResponse();
481  return (res == 1);
482  }
483 
484  String getLocalIP()
485  {
486  sendAT(GF("+CIFSR"));
487  String res;
488  if (waitResponse(10000L, res) != 1) {
489  return "";
490  }
491  res.replace(GSM_NL "OK" GSM_NL, "");
492  res.replace(GSM_NL, "");
493  res.trim();
494  return res;
495  }
496 
497  IPAddress localIP()
498  {
499  return TinyGsmIpFromString(getLocalIP());
500  }
501 
502  /*
503  * Phone Call functions
504  */
505 
506  bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
507 
508  bool callAnswer()
509  {
510  sendAT(GF("A"));
511  return waitResponse() == 1;
512  }
513 
514  // Returns true on pick-up, false on error/busy
515  bool callNumber(const String& number)
516  {
517  if (number == GF("last")) {
518  sendAT(GF("DLST"));
519  } else {
520  sendAT(GF("D\""), number, "\";");
521  }
522 
523  if (waitResponse(5000L) != 1) {
524  return false;
525  }
526 
527  if (waitResponse(60000L,
528  GF(GSM_NL "+CIEV: \"CALL\",1"),
529  GF(GSM_NL "+CIEV: \"CALL\",0"),
530  GFP(GSM_ERROR)) != 1) {
531  return false;
532  }
533 
534  int rsp = waitResponse(60000L,
535  GF(GSM_NL "+CIEV: \"SOUNDER\",0"),
536  GF(GSM_NL "+CIEV: \"CALL\",0"));
537 
538  int rsp2 = waitResponse(300L, GF(GSM_NL "BUSY" GSM_NL), GF(GSM_NL "NO ANSWER" GSM_NL));
539 
540  return rsp == 1 && rsp2 == 0;
541  }
542 
543  bool callHangup()
544  {
545  sendAT(GF("H"));
546  return waitResponse() == 1;
547  }
548 
549  // 0-9,*,#,A,B,C,D
550  bool dtmfSend(char cmd, unsigned duration_ms = 100)
551  {
552  duration_ms = constrain(duration_ms, 100, 1000);
553 
554  // The duration parameter is not working, so we simulate it using delay..
555  // TODO: Maybe there's another way...
556 
557  //sendAT(GF("+VTD="), duration_ms / 100);
558  //waitResponse();
559 
560  sendAT(GF("+VTS="), cmd);
561  if (waitResponse(10000L) == 1) {
562  delay(duration_ms);
563  return true;
564  }
565  return false;
566  }
567 
568  /*
569  * Audio functions
570  */
571 
572  bool audioSetHeadphones()
573  {
574  sendAT(GF("+SNFS=0"));
575  return waitResponse() == 1;
576  }
577 
578  bool audioSetSpeaker()
579  {
580  sendAT(GF("+SNFS=1"));
581  return waitResponse() == 1;
582  }
583 
584  bool audioMuteMic(bool mute)
585  {
586  sendAT(GF("+CMUT="), mute);
587  return waitResponse() == 1;
588  }
589 
590  /*
591  * Messaging functions
592  */
593 
594  String sendUSSD(const String& code)
595  {
596  sendAT(GF("+CMGF=1"));
597  waitResponse();
598  sendAT(GF("+CSCS=\"HEX\""));
599  waitResponse();
600  sendAT(GF("+CUSD=1,\""), code, GF("\",15"));
601  if (waitResponse(10000L) != 1) {
602  return "";
603  }
604  if (waitResponse(GF(GSM_NL "+CUSD:")) != 1) {
605  return "";
606  }
607  stream.readStringUntil('"');
608  String hex = stream.readStringUntil('"');
609  stream.readStringUntil(',');
610  int dcs = stream.readStringUntil('\n').toInt();
611 
612  if (dcs == 15) {
613  return TinyGsmDecodeHex7bit(hex);
614  } else if (dcs == 72) {
615  return TinyGsmDecodeHex16bit(hex);
616  } else {
617  return hex;
618  }
619  }
620 
621  bool sendSMS(const String& number, const String& text)
622  {
623  sendAT(GF("+CMGF=1"));
624  waitResponse();
625  sendAT(GF("+CMGS=\""), number, GF("\""));
626  if (waitResponse(GF(">")) != 1) {
627  return false;
628  }
629  stream.print(text);
630  stream.write((char)0x1A);
631  stream.flush();
632  return waitResponse(60000L) == 1;
633  }
634 
635 
636  /*
637  * Location functions
638  */
639 
640  String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
641 
642  /*
643  * Battery functions
644  */
645 
646  uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
647 
648  int getBattPercent()
649  {
650  sendAT(GF("+CBC?"));
651  if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
652  return false;
653  }
654  stream.readStringUntil(',');
655  int res = stream.readStringUntil('\n').toInt();
656  waitResponse();
657  return res;
658  }
659 
660 protected:
661 
662  bool modemConnect(const char* host, uint16_t port, uint8_t* mux)
663  {
664  sendAT(GF("+CIPSTART="), GF("\"TCP"), GF("\",\""), host, GF("\","), port);
665 
666  if (waitResponse(75000L, GF(GSM_NL "+CIPNUM:")) != 1) {
667  return false;
668  }
669  int newMux = stream.readStringUntil('\n').toInt();
670 
671  int rsp = waitResponse(75000L,
672  GF("CONNECT OK" GSM_NL),
673  GF("CONNECT FAIL" GSM_NL),
674  GF("ALREADY CONNECT" GSM_NL));
675  if (waitResponse() != 1) {
676  return false;
677  }
678  *mux = newMux;
679 
680  return (1 == rsp);
681  }
682 
683  int modemSend(const void* buff, size_t len, uint8_t mux)
684  {
685  sendAT(GF("+CIPSEND="), mux, ',', len);
686  if (waitResponse(2000L, GF(GSM_NL ">")) != 1) {
687  return 0;
688  }
689  stream.write((uint8_t*)buff, len);
690  stream.flush();
691  if (waitResponse(10000L, GFP(GSM_OK), GF(GSM_NL "FAIL")) != 1) {
692  return 0;
693  }
694  return len;
695  }
696 
697  bool modemGetConnected(uint8_t mux)
698  {
699  sendAT(GF("+CIPSTATUS")); //TODO mux?
700  int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""),
701  GF(",\"INITIAL\""));
702  waitResponse();
703  return 1 == res;
704  }
705 
706 public:
707 
708  /* Utilities */
709 
710  template<typename T>
711  void streamWrite(T last)
712  {
713  stream.print(last);
714  }
715 
716  template<typename T, typename... Args>
717  void streamWrite(T head, Args... tail)
718  {
719  stream.print(head);
720  streamWrite(tail...);
721  }
722 
723  bool streamSkipUntil(char c) //TODO: timeout
724  {
725  while (true) {
726  while (!stream.available()) {
727  TINY_GSM_YIELD();
728  }
729  if (stream.read() == c) {
730  return true;
731  }
732  }
733  return false;
734  }
735 
736  template<typename... Args>
737  void sendAT(Args... cmd)
738  {
739  streamWrite("AT", cmd..., GSM_NL);
740  stream.flush();
741  TINY_GSM_YIELD();
742  //DBG("### AT:", cmd...);
743  }
744 
745  // TODO: Optimize this!
746  uint8_t waitResponse(uint32_t timeout, String& data,
747  GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
748  GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
749  {
750  /*String r1s(r1); r1s.trim();
751  String r2s(r2); r2s.trim();
752  String r3s(r3); r3s.trim();
753  String r4s(r4); r4s.trim();
754  String r5s(r5); r5s.trim();
755  DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
756  data.reserve(64);
757  int index = 0;
758  unsigned long startMillis = millis();
759  do {
760  TINY_GSM_YIELD();
761  while (stream.available() > 0) {
762  int a = stream.read();
763  if (a <= 0) {
764  continue; // Skip 0x00 bytes, just in case
765  }
766  data += (char)a;
767  if (r1 && data.endsWith(r1)) {
768  index = 1;
769  goto finish;
770  } else if (r2 && data.endsWith(r2)) {
771  index = 2;
772  goto finish;
773  } else if (r3 && data.endsWith(r3)) {
774  index = 3;
775  goto finish;
776  } else if (r4 && data.endsWith(r4)) {
777  index = 4;
778  goto finish;
779  } else if (r5 && data.endsWith(r5)) {
780  index = 5;
781  goto finish;
782  } else if (data.endsWith(GF("+CIPRCV:"))) {
783  int mux = stream.readStringUntil(',').toInt();
784  int len = stream.readStringUntil(',').toInt();
785  int len_orig = len;
786  if (len > sockets[mux]->rx.free()) {
787  DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
788  } else {
789  DBG("### Got: ", len, "->", sockets[mux]->rx.free());
790  }
791  while (len--) {
792  while (!stream.available()) {
793  TINY_GSM_YIELD();
794  }
795  sockets[mux]->rx.put(stream.read());
796  }
797  if (len_orig > sockets[mux]->available()) { // TODO
798  DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
799  }
800  data = "";
801  } else if (data.endsWith(GF("+TCPCLOSED:"))) {
802  int mux = stream.readStringUntil('\n').toInt();
803  if (mux >= 0 && mux < TINY_GSM_MUX_COUNT) {
804  sockets[mux]->sock_connected = false;
805  }
806  data = "";
807  DBG("### Closed: ", mux);
808  }
809  }
810  } while (millis() - startMillis < timeout);
811 finish:
812  if (!index) {
813  data.trim();
814  if (data.length()) {
815  DBG("### Unhandled:", data);
816  }
817  data = "";
818  }
819  return index;
820  }
821 
822  uint8_t waitResponse(uint32_t timeout,
823  GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
824  GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
825  {
826  String data;
827  return waitResponse(timeout, data, r1, r2, r3, r4, r5);
828  }
829 
830  uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
831  GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
832  {
833  return waitResponse(1000, r1, r2, r3, r4, r5);
834  }
835 
836 public:
837  Stream& stream;
838 
839 protected:
840  GsmClient* sockets[TINY_GSM_MUX_COUNT];
841 };
842 
843 #endif
data
char data[MAX_PAYLOAD_SIZE+1]
Buffer for raw payload data.
Definition: MyMessage.h:653
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
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