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.

759 lines
17 KiB

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