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.

719 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(500);
  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;E0")); // Another option is to use AT+CGPADDR=1
  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) {
  392. sendAT(GF("+GSMBUSY="), busy ? 1 : 0);
  393. return waitResponse() == 1;
  394. }
  395. bool callAnswer() {
  396. sendAT(GF("A"));
  397. return waitResponse() == 1;
  398. }
  399. // Returns true on pick-up, false on error/busy
  400. bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  401. //bool callRedial() {
  402. // sendAT(GF("DL"));
  403. // return waitResponse() == 1;
  404. //}
  405. bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  406. /*
  407. * Messaging functions
  408. */
  409. String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  410. bool sendSMS(const String& number, const String& text) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  411. bool sendSMS_UTF16(const String& number, const void* text, size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  412. /*
  413. * Location functions
  414. */
  415. String getGsmLocation() {
  416. sendAT(GF("+ULOC=2,3,0,120,1"));
  417. if (waitResponse(GF(GSM_NL "+UULOC:")) != 1) {
  418. return "";
  419. }
  420. String res = stream.readStringUntil('\n');
  421. waitResponse();
  422. res.trim();
  423. return res;
  424. }
  425. /*
  426. * Battery functions
  427. */
  428. // Use: float vBatt = modem.getBattVoltage() / 1000.0;
  429. uint16_t getBattVoltage() {
  430. sendAT(GF("+CIND"));
  431. if (waitResponse(GF(GSM_NL "+CIND:")) != 1) {
  432. return 0;
  433. }
  434. uint16_t res = stream.readStringUntil(',').toInt();
  435. waitResponse();
  436. return res;
  437. }
  438. int getBattPercent() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  439. protected:
  440. bool modemConnect(const char* host, uint16_t port, uint8_t* mux, bool ssl = false) {
  441. sendAT(GF("+USOCR=6"));
  442. if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) {
  443. return false;
  444. }
  445. *mux = stream.readStringUntil('\n').toInt();
  446. waitResponse();
  447. if (ssl) {
  448. sendAT(GF("+USOSEC="), *mux, ",1");
  449. waitResponse();
  450. }
  451. sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port);
  452. int rsp = waitResponse(75000L);
  453. return (1 == rsp);
  454. }
  455. int modemSend(const void* buff, size_t len, uint8_t mux) {
  456. sendAT(GF("+USOWR="), mux, ',', len);
  457. if (waitResponse(GF("@")) != 1) {
  458. return -1;
  459. }
  460. // 50ms delay, see AT manual section 25.10.4
  461. delay(50);
  462. stream.write((uint8_t*)buff, len);
  463. stream.flush();
  464. if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) {
  465. return -1;
  466. }
  467. streamSkipUntil(','); // Skip mux
  468. return stream.readStringUntil('\n').toInt();
  469. }
  470. size_t modemRead(size_t size, uint8_t mux) {
  471. sendAT(GF("+USORD="), mux, ',', size);
  472. if (waitResponse(GF(GSM_NL "+USORD:")) != 1) {
  473. return 0;
  474. }
  475. streamSkipUntil(','); // Skip mux
  476. size_t len = stream.readStringUntil(',').toInt();
  477. streamSkipUntil('\"');
  478. for (size_t i=0; i<len; i++) {
  479. while (!stream.available()) { TINY_GSM_YIELD(); }
  480. char c = stream.read();
  481. sockets[mux]->rx.put(c);
  482. }
  483. streamSkipUntil('\"');
  484. waitResponse();
  485. return len;
  486. }
  487. size_t modemGetAvailable(uint8_t mux) {
  488. sendAT(GF("+USORD="), mux, ',', 0);
  489. size_t result = 0;
  490. if (waitResponse(GF(GSM_NL "+USORD:")) == 1) {
  491. streamSkipUntil(','); // Skip mux
  492. result = stream.readStringUntil('\n').toInt();
  493. waitResponse();
  494. }
  495. if (!result) {
  496. sockets[mux]->sock_connected = modemGetConnected(mux);
  497. }
  498. return result;
  499. }
  500. bool modemGetConnected(uint8_t mux) {
  501. sendAT(GF("+USOCTL="), mux, ",10");
  502. if (waitResponse(GF(GSM_NL "+USOCTL:")) != 1)
  503. return false;
  504. streamSkipUntil(','); // Skip mux
  505. streamSkipUntil(','); // Skip type
  506. int result = stream.readStringUntil('\n').toInt();
  507. return result != 0;
  508. }
  509. public:
  510. /* Utilities */
  511. template<typename T>
  512. void streamWrite(T last) {
  513. stream.print(last);
  514. }
  515. template<typename T, typename... Args>
  516. void streamWrite(T head, Args... tail) {
  517. stream.print(head);
  518. streamWrite(tail...);
  519. }
  520. bool streamSkipUntil(char c) { //TODO: timeout
  521. while (true) {
  522. while (!stream.available()) { TINY_GSM_YIELD(); }
  523. if (stream.read() == c)
  524. return true;
  525. }
  526. return false;
  527. }
  528. template<typename... Args>
  529. void sendAT(Args... cmd) {
  530. streamWrite("AT", cmd..., GSM_NL);
  531. stream.flush();
  532. TINY_GSM_YIELD();
  533. //DBG("### AT:", cmd...);
  534. }
  535. // TODO: Optimize this!
  536. uint8_t waitResponse(uint32_t timeout, String& data,
  537. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  538. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  539. {
  540. /*String r1s(r1); r1s.trim();
  541. String r2s(r2); r2s.trim();
  542. String r3s(r3); r3s.trim();
  543. String r4s(r4); r4s.trim();
  544. String r5s(r5); r5s.trim();
  545. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  546. data.reserve(64);
  547. int index = 0;
  548. unsigned long startMillis = millis();
  549. do {
  550. TINY_GSM_YIELD();
  551. while (stream.available() > 0) {
  552. int a = stream.read();
  553. if (a < 0) continue;
  554. data += (char)a;
  555. if (r1 && data.endsWith(r1)) {
  556. index = 1;
  557. goto finish;
  558. } else if (r2 && data.endsWith(r2)) {
  559. index = 2;
  560. goto finish;
  561. } else if (r3 && data.endsWith(r3)) {
  562. index = 3;
  563. goto finish;
  564. } else if (r4 && data.endsWith(r4)) {
  565. index = 4;
  566. goto finish;
  567. } else if (r5 && data.endsWith(r5)) {
  568. index = 5;
  569. goto finish;
  570. } else if (data.endsWith(GF(GSM_NL "+UUSORD:"))) {
  571. int mux = stream.readStringUntil(',').toInt();
  572. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  573. sockets[mux]->got_data = true;
  574. }
  575. data = "";
  576. } else if (data.endsWith(GF(GSM_NL "+UUSOCL:"))) {
  577. int mux = stream.readStringUntil('\n').toInt();
  578. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  579. sockets[mux]->sock_connected = false;
  580. }
  581. data = "";
  582. DBG("### Closed: ", mux);
  583. }
  584. }
  585. } while (millis() - startMillis < timeout);
  586. finish:
  587. if (!index) {
  588. data.trim();
  589. if (data.length()) {
  590. DBG("### Unhandled:", data);
  591. }
  592. data = "";
  593. }
  594. return index;
  595. }
  596. uint8_t waitResponse(uint32_t timeout,
  597. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  598. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  599. {
  600. String data;
  601. return waitResponse(timeout, data, r1, r2, r3, r4, r5);
  602. }
  603. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  604. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  605. {
  606. return waitResponse(1000, r1, r2, r3, r4, r5);
  607. }
  608. protected:
  609. Stream& stream;
  610. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  611. };
  612. #endif