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.

707 lines
16 KiB

  1. /**
  2. * @file TinyGsmClientU201.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef TinyGsmClientU201_h
  9. #define TinyGsmClientU201_h
  10. //#define TINY_GSM_DEBUG Serial
  11. #if !defined(TINY_GSM_RX_BUFFER)
  12. #define TINY_GSM_RX_BUFFER 64
  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. enum SimStatus {
  20. SIM_ERROR = 0,
  21. SIM_READY = 1,
  22. SIM_LOCKED = 2,
  23. };
  24. enum RegStatus {
  25. REG_UNREGISTERED = 0,
  26. REG_SEARCHING = 2,
  27. REG_DENIED = 3,
  28. REG_OK_HOME = 1,
  29. REG_OK_ROAMING = 5,
  30. REG_UNKNOWN = 4,
  31. };
  32. class TinyGsmU201
  33. {
  34. public:
  35. class GsmClient : public Client
  36. {
  37. friend class TinyGsmU201;
  38. typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
  39. public:
  40. GsmClient() {}
  41. GsmClient(TinyGsmU201& modem, uint8_t mux = 1) {
  42. init(&modem, mux);
  43. }
  44. bool init(TinyGsmU201* modem, uint8_t mux = 1) {
  45. this->at = modem;
  46. this->mux = mux;
  47. sock_available = 0;
  48. sock_connected = false;
  49. got_data = false;
  50. return true;
  51. }
  52. public:
  53. virtual int connect(const char *host, uint16_t port) {
  54. TINY_GSM_YIELD();
  55. rx.clear();
  56. sock_connected = at->modemConnect(host, port, &mux);
  57. at->sockets[mux] = this;
  58. return sock_connected;
  59. }
  60. virtual int connect(IPAddress ip, uint16_t port) {
  61. String host; host.reserve(16);
  62. host += ip[0];
  63. host += ".";
  64. host += ip[1];
  65. host += ".";
  66. host += ip[2];
  67. host += ".";
  68. host += ip[3];
  69. return connect(host.c_str(), port);
  70. }
  71. virtual void stop() {
  72. TINY_GSM_YIELD();
  73. at->sendAT(GF("+USOCL="), mux);
  74. sock_connected = false;
  75. at->waitResponse();
  76. }
  77. virtual size_t write(const uint8_t *buf, size_t size) {
  78. TINY_GSM_YIELD();
  79. at->maintain();
  80. return at->modemSend(buf, size, mux);
  81. }
  82. virtual size_t write(uint8_t c) {
  83. return write(&c, 1);
  84. }
  85. virtual int available() {
  86. TINY_GSM_YIELD();
  87. if (!rx.size() && sock_connected) { // TODO
  88. at->maintain();
  89. }
  90. return rx.size() + sock_available;
  91. }
  92. virtual int read(uint8_t *buf, size_t size) {
  93. TINY_GSM_YIELD();
  94. at->maintain();
  95. size_t cnt = 0;
  96. while (cnt < size) {
  97. size_t chunk = TinyGsmMin(size-cnt, rx.size());
  98. if (chunk > 0) {
  99. rx.get(buf, chunk);
  100. buf += chunk;
  101. cnt += chunk;
  102. continue;
  103. }
  104. // TODO: Read directly into user buffer?
  105. at->maintain();
  106. if (sock_available > 0) {
  107. at->modemRead(rx.free(), mux);
  108. } else {
  109. break;
  110. }
  111. }
  112. return cnt;
  113. }
  114. virtual int read() {
  115. uint8_t c;
  116. if (read(&c, 1) == 1) {
  117. return c;
  118. }
  119. return -1;
  120. }
  121. virtual int peek() { return -1; } //TODO
  122. virtual void flush() { at->stream.flush(); }
  123. virtual uint8_t connected() {
  124. if (available()) {
  125. return true;
  126. }
  127. return sock_connected;
  128. }
  129. virtual operator bool() { return connected(); }
  130. /*
  131. * Extended API
  132. */
  133. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  134. private:
  135. TinyGsmU201* at;
  136. uint8_t mux;
  137. uint16_t sock_available;
  138. bool sock_connected;
  139. bool got_data;
  140. RxFifo rx;
  141. };
  142. class GsmClientSecure : public GsmClient
  143. {
  144. public:
  145. GsmClientSecure() {}
  146. GsmClientSecure(TinyGsmU201& modem, uint8_t mux = 1)
  147. : GsmClient(modem, mux)
  148. {}
  149. public:
  150. virtual int connect(const char *host, uint16_t port) {
  151. TINY_GSM_YIELD();
  152. rx.clear();
  153. sock_connected = at->modemConnect(host, port, &mux, true);
  154. at->sockets[mux] = this;
  155. return sock_connected;
  156. }
  157. };
  158. public:
  159. #ifdef GSM_DEFAULT_STREAM
  160. TinyGsmU201(Stream& stream = GSM_DEFAULT_STREAM)
  161. #else
  162. TinyGsmU201(Stream& stream)
  163. #endif
  164. : stream(stream)
  165. {
  166. memset(sockets, 0, sizeof(sockets));
  167. }
  168. /*
  169. * Basic functions
  170. */
  171. bool begin(const char* pin = NULL) {
  172. return init(pin);
  173. }
  174. bool init(const char* pin = NULL) {
  175. if (!testAT()) {
  176. return false;
  177. }
  178. sendAT(GF("E0")); // Echo Off
  179. if (waitResponse() != 1) {
  180. return false;
  181. }
  182. int ret = getSimStatus();
  183. if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
  184. simUnlock(pin);
  185. }
  186. return (getSimStatus() == SIM_READY);
  187. }
  188. void setBaud(unsigned long baud) {
  189. sendAT(GF("+IPR="), baud);
  190. }
  191. bool testAT(unsigned long timeout = 10000L) {
  192. for (unsigned long start = millis(); millis() - start < timeout; ) {
  193. sendAT(GF(""));
  194. if (waitResponse(200) == 1) {
  195. delay(100);
  196. return true;
  197. }
  198. delay(100);
  199. }
  200. return false;
  201. }
  202. void maintain() {
  203. for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
  204. GsmClient* sock = sockets[mux];
  205. if (sock && sock->got_data) {
  206. sock->got_data = false;
  207. sock->sock_available = modemGetAvailable(mux);
  208. }
  209. }
  210. while (stream.available()) {
  211. waitResponse(10, NULL, NULL);
  212. }
  213. }
  214. bool factoryDefault() {
  215. sendAT(GF("+UFACTORY=0,1")); // Factory + Reset + Echo Off
  216. waitResponse();
  217. sendAT(GF("+CFUN=16")); // Auto-baud
  218. return waitResponse() == 1;
  219. }
  220. /*
  221. * Power functions
  222. */
  223. bool restart() {
  224. if (!testAT()) {
  225. return false;
  226. }
  227. sendAT(GF("+CFUN=16"));
  228. if (waitResponse(10000L) != 1) {
  229. return false;
  230. }
  231. delay(3000);
  232. return init();
  233. }
  234. bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  235. /*
  236. * SIM card functions
  237. */
  238. bool simUnlock(const char *pin) {
  239. sendAT(GF("+CPIN=\""), pin, GF("\""));
  240. return waitResponse() == 1;
  241. }
  242. String getSimCCID() {
  243. sendAT(GF("+CCID"));
  244. if (waitResponse(GF(GSM_NL "+CCID:")) != 1) {
  245. return "";
  246. }
  247. String res = stream.readStringUntil('\n');
  248. waitResponse();
  249. res.trim();
  250. return res;
  251. }
  252. String getIMEI() {
  253. sendAT(GF("+CGSN"));
  254. if (waitResponse(GF(GSM_NL)) != 1) {
  255. return "";
  256. }
  257. String res = stream.readStringUntil('\n');
  258. waitResponse();
  259. res.trim();
  260. return res;
  261. }
  262. SimStatus getSimStatus(unsigned long timeout = 10000L) {
  263. for (unsigned long start = millis(); millis() - start < timeout; ) {
  264. sendAT(GF("+CPIN?"));
  265. if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
  266. delay(1000);
  267. continue;
  268. }
  269. int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED"));
  270. waitResponse();
  271. switch (status) {
  272. case 2:
  273. case 3: return SIM_LOCKED;
  274. case 1: return SIM_READY;
  275. default: return SIM_ERROR;
  276. }
  277. }
  278. return SIM_ERROR;
  279. }
  280. RegStatus getRegistrationStatus() {
  281. sendAT(GF("+CGREG?"));
  282. if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) {
  283. return REG_UNKNOWN;
  284. }
  285. streamSkipUntil(','); // Skip format (0)
  286. int status = stream.readStringUntil('\n').toInt();
  287. waitResponse();
  288. return (RegStatus)status;
  289. }
  290. String getOperator() {
  291. sendAT(GF("+COPS?"));
  292. if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
  293. return "";
  294. }
  295. streamSkipUntil('"'); // Skip mode and format
  296. String res = stream.readStringUntil('"');
  297. waitResponse();
  298. return res;
  299. }
  300. /*
  301. * Generic network functions
  302. */
  303. int getSignalQuality() {
  304. sendAT(GF("+CSQ"));
  305. if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
  306. return 99;
  307. }
  308. int res = stream.readStringUntil(',').toInt();
  309. waitResponse();
  310. return res;
  311. }
  312. bool isNetworkConnected() {
  313. RegStatus s = getRegistrationStatus();
  314. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  315. }
  316. bool waitForNetwork(unsigned long timeout = 60000L) {
  317. for (unsigned long start = millis(); millis() - start < timeout; ) {
  318. if (isNetworkConnected()) {
  319. return true;
  320. }
  321. delay(250);
  322. }
  323. return false;
  324. }
  325. /*
  326. * GPRS functions
  327. */
  328. bool gprsConnect(const char* apn, const char* user, const char* pwd) {
  329. gprsDisconnect();
  330. sendAT(GF("+CGATT=1"));
  331. waitResponse(5000L);
  332. sendAT(GF("+UPSD=0,1,\""), apn, '"');
  333. waitResponse();
  334. if (user && strlen(user) > 0) {
  335. sendAT(GF("+UPSD=0,2,\""), user, '"');
  336. waitResponse();
  337. }
  338. if (pwd && strlen(pwd) > 0) {
  339. sendAT(GF("+UPSD=0,3,\""), pwd, '"');
  340. waitResponse();
  341. }
  342. sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP
  343. waitResponse();
  344. sendAT(GF("+UPSDA=0,3"));
  345. waitResponse(6000L);
  346. // Open a GPRS context
  347. sendAT(GF("+UPSND=0,8"));
  348. if (waitResponse(GF(",8,1")) != 1) {
  349. return false;
  350. }
  351. return true;
  352. }
  353. bool gprsDisconnect() {
  354. sendAT(GF("+UPSDA=0,4"));
  355. if (waitResponse(60000L) != 1)
  356. return false;
  357. sendAT(GF("+CGATT=0"));
  358. if (waitResponse(60000L) != 1)
  359. return false;
  360. return true;
  361. }
  362. bool isGprsConnected() {
  363. sendAT(GF("+CGATT?"));
  364. if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
  365. return false;
  366. }
  367. int res = stream.readStringUntil('\n').toInt();
  368. waitResponse();
  369. if (res != 1)
  370. return false;
  371. sendAT(GF("+CIFSR"));
  372. if (waitResponse() != 1)
  373. return false;
  374. return true;
  375. }
  376. String getLocalIP() {
  377. sendAT(GF("+CIFSR;E0"));
  378. String res;
  379. if (waitResponse(10000L, res) != 1) {
  380. return "";
  381. }
  382. res.trim();
  383. return res;
  384. }
  385. IPAddress localIP() {
  386. return TinyGsmIpFromString(getLocalIP());
  387. }
  388. /*
  389. * Phone Call functions
  390. */
  391. bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  392. bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  393. bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  394. bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  395. /*
  396. * Messaging functions
  397. */
  398. String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  399. bool sendSMS(const String& number, const String& text) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  400. bool sendSMS_UTF16(const String& number, const void* text, size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  401. /*
  402. * Location functions
  403. */
  404. String getGsmLocation() {
  405. sendAT(GF("+ULOC=2,3,0,120,1"));
  406. if (waitResponse(GF(GSM_NL "+UULOC:")) != 1) {
  407. return "";
  408. }
  409. String res = stream.readStringUntil('\n');
  410. waitResponse();
  411. res.trim();
  412. return res;
  413. }
  414. /*
  415. * Battery functions
  416. */
  417. // Use: float vBatt = modem.getBattVoltage() / 1000.0;
  418. uint16_t getBattVoltage() {
  419. sendAT(GF("+CIND"));
  420. if (waitResponse(GF(GSM_NL "+CIND:")) != 1) {
  421. return 0;
  422. }
  423. uint16_t res = stream.readStringUntil(',').toInt();
  424. waitResponse();
  425. return res;
  426. }
  427. int getBattPercent() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  428. protected:
  429. bool modemConnect(const char* host, uint16_t port, uint8_t* mux, bool ssl = false) {
  430. sendAT(GF("+USOCR=6"));
  431. if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) {
  432. return false;
  433. }
  434. *mux = stream.readStringUntil('\n').toInt();
  435. waitResponse();
  436. if (ssl) {
  437. sendAT(GF("+USOSEC="), *mux, ",1");
  438. waitResponse();
  439. }
  440. sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port);
  441. int rsp = waitResponse(75000L);
  442. return (1 == rsp);
  443. }
  444. int modemSend(const void* buff, size_t len, uint8_t mux) {
  445. sendAT(GF("+USOWR="), mux, ',', len);
  446. if (waitResponse(GF("@")) != 1) {
  447. return -1;
  448. }
  449. // 50ms delay, see AT manual section 25.10.4
  450. delay(50);
  451. stream.write((uint8_t*)buff, len);
  452. stream.flush();
  453. if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) {
  454. return -1;
  455. }
  456. streamSkipUntil(','); // Skip mux
  457. return stream.readStringUntil('\n').toInt();
  458. }
  459. size_t modemRead(size_t size, uint8_t mux) {
  460. sendAT(GF("+USORD="), mux, ',', size);
  461. if (waitResponse(GF(GSM_NL "+USORD:")) != 1) {
  462. return 0;
  463. }
  464. streamSkipUntil(','); // Skip mux
  465. size_t len = stream.readStringUntil(',').toInt();
  466. streamSkipUntil('\"');
  467. for (size_t i=0; i<len; i++) {
  468. while (!stream.available()) { TINY_GSM_YIELD(); }
  469. char c = stream.read();
  470. sockets[mux]->rx.put(c);
  471. }
  472. streamSkipUntil('\"');
  473. waitResponse();
  474. return len;
  475. }
  476. size_t modemGetAvailable(uint8_t mux) {
  477. sendAT(GF("+USORD="), mux, ',', 0);
  478. size_t result = 0;
  479. if (waitResponse(GF(GSM_NL "+USORD:")) == 1) {
  480. streamSkipUntil(','); // Skip mux
  481. result = stream.readStringUntil('\n').toInt();
  482. waitResponse();
  483. }
  484. if (!result) {
  485. sockets[mux]->sock_connected = modemGetConnected(mux);
  486. }
  487. return result;
  488. }
  489. bool modemGetConnected(uint8_t mux) {
  490. sendAT(GF("+USOCTL="), mux, ",10");
  491. if (waitResponse(GF(GSM_NL "+USOCTL:")) != 1)
  492. return false;
  493. streamSkipUntil(','); // Skip mux
  494. streamSkipUntil(','); // Skip type
  495. int result = stream.readStringUntil('\n').toInt();
  496. return result != 0;
  497. }
  498. public:
  499. /* Utilities */
  500. template<typename T>
  501. void streamWrite(T last) {
  502. stream.print(last);
  503. }
  504. template<typename T, typename... Args>
  505. void streamWrite(T head, Args... tail) {
  506. stream.print(head);
  507. streamWrite(tail...);
  508. }
  509. bool streamSkipUntil(char c) { //TODO: timeout
  510. while (true) {
  511. while (!stream.available()) { TINY_GSM_YIELD(); }
  512. if (stream.read() == c)
  513. return true;
  514. }
  515. return false;
  516. }
  517. template<typename... Args>
  518. void sendAT(Args... cmd) {
  519. streamWrite("AT", cmd..., GSM_NL);
  520. stream.flush();
  521. TINY_GSM_YIELD();
  522. //DBG("### AT:", cmd...);
  523. }
  524. // TODO: Optimize this!
  525. uint8_t waitResponse(uint32_t timeout, String& data,
  526. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  527. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  528. {
  529. /*String r1s(r1); r1s.trim();
  530. String r2s(r2); r2s.trim();
  531. String r3s(r3); r3s.trim();
  532. String r4s(r4); r4s.trim();
  533. String r5s(r5); r5s.trim();
  534. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  535. data.reserve(64);
  536. int index = 0;
  537. unsigned long startMillis = millis();
  538. do {
  539. TINY_GSM_YIELD();
  540. while (stream.available() > 0) {
  541. int a = stream.read();
  542. if (a < 0) continue;
  543. data += (char)a;
  544. if (r1 && data.endsWith(r1)) {
  545. index = 1;
  546. goto finish;
  547. } else if (r2 && data.endsWith(r2)) {
  548. index = 2;
  549. goto finish;
  550. } else if (r3 && data.endsWith(r3)) {
  551. index = 3;
  552. goto finish;
  553. } else if (r4 && data.endsWith(r4)) {
  554. index = 4;
  555. goto finish;
  556. } else if (r5 && data.endsWith(r5)) {
  557. index = 5;
  558. goto finish;
  559. } else if (data.endsWith(GF(GSM_NL "+UUSORD:"))) {
  560. int mux = stream.readStringUntil(',').toInt();
  561. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  562. sockets[mux]->got_data = true;
  563. }
  564. data = "";
  565. } else if (data.endsWith(GF(GSM_NL "+UUSOCL:"))) {
  566. int mux = stream.readStringUntil('\n').toInt();
  567. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  568. sockets[mux]->sock_connected = false;
  569. }
  570. data = "";
  571. DBG("### Closed: ", mux);
  572. }
  573. }
  574. } while (millis() - startMillis < timeout);
  575. finish:
  576. if (!index) {
  577. data.trim();
  578. if (data.length()) {
  579. DBG("### Unhandled:", data);
  580. }
  581. data = "";
  582. }
  583. return index;
  584. }
  585. uint8_t waitResponse(uint32_t timeout,
  586. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  587. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  588. {
  589. String data;
  590. return waitResponse(timeout, data, r1, r2, r3, r4, r5);
  591. }
  592. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  593. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  594. {
  595. return waitResponse(1000, r1, r2, r3, r4, r5);
  596. }
  597. protected:
  598. Stream& stream;
  599. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  600. };
  601. #endif