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.

410 lines
9.1 KiB

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