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

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
5 years ago
5 years ago
5 years ago
6 years ago
5 years ago
5 years ago
  1. /**
  2. * @file TinyGsmClientBG96.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Apr 2018
  7. */
  8. #ifndef TinyGsmClientBG96_h
  9. #define TinyGsmClientBG96_h
  10. //#pragma message("TinyGSM: TinyGsmClientBG96")
  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 12
  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. enum SimStatus {
  21. SIM_ERROR = 0,
  22. SIM_READY = 1,
  23. SIM_LOCKED = 2,
  24. };
  25. enum RegStatus {
  26. REG_UNREGISTERED = 0,
  27. REG_SEARCHING = 2,
  28. REG_DENIED = 3,
  29. REG_OK_HOME = 1,
  30. REG_OK_ROAMING = 5,
  31. REG_UNKNOWN = 4,
  32. };
  33. class TinyGsmBG96
  34. {
  35. public:
  36. class GsmClient : public Client
  37. {
  38. friend class TinyGsmBG96;
  39. typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
  40. public:
  41. GsmClient() {}
  42. GsmClient(TinyGsmBG96& modem, uint8_t mux = 1) {
  43. init(&modem, mux);
  44. }
  45. virtual ~GsmClient(){}
  46. bool init(TinyGsmBG96* modem, uint8_t mux = 1) {
  47. this->at = modem;
  48. this->mux = mux;
  49. sock_available = 0;
  50. prev_check = 0;
  51. sock_connected = false;
  52. got_data = false;
  53. at->sockets[mux] = this;
  54. return true;
  55. }
  56. public:
  57. virtual int connect(const char *host, uint16_t port, int timeout_s) {
  58. stop();
  59. TINY_GSM_YIELD();
  60. rx.clear();
  61. sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
  62. return sock_connected;
  63. }
  64. TINY_GSM_CLIENT_CONNECT_OVERLOADS()
  65. virtual void stop(uint32_t maxWaitMs) {
  66. TINY_GSM_CLIENT_DUMP_MODEM_BUFFER()
  67. at->sendAT(GF("+QICLOSE="), mux);
  68. sock_connected = false;
  69. at->waitResponse();
  70. }
  71. virtual void stop() { stop(15000L); }
  72. TINY_GSM_CLIENT_WRITE()
  73. TINY_GSM_CLIENT_AVAILABLE_WITH_BUFFER_CHECK()
  74. TINY_GSM_CLIENT_READ_WITH_BUFFER_CHECK()
  75. TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
  76. /*
  77. * Extended API
  78. */
  79. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  80. private:
  81. TinyGsmBG96* at;
  82. uint8_t mux;
  83. uint16_t sock_available;
  84. uint32_t prev_check;
  85. bool sock_connected;
  86. bool got_data;
  87. RxFifo rx;
  88. };
  89. // class GsmClientSecure : public GsmClient
  90. // {
  91. // public:
  92. // GsmClientSecure() {}
  93. //
  94. // GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 1)
  95. // : GsmClient(modem, mux)
  96. // {}
  97. //
  98. // virtual ~GsmClientSecure(){}
  99. //
  100. // public:
  101. // virtual int connect(const char *host, uint16_t port, int timeout_s) {
  102. // stop();
  103. // TINY_GSM_YIELD();
  104. // rx.clear();
  105. // sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
  106. // return sock_connected;
  107. // }
  108. // };
  109. public:
  110. TinyGsmBG96(Stream& stream)
  111. : stream(stream)
  112. {
  113. memset(sockets, 0, sizeof(sockets));
  114. }
  115. virtual ~TinyGsmBG96() {}
  116. /*
  117. * Basic functions
  118. */
  119. bool begin(const char* pin = NULL) {
  120. return init(pin);
  121. }
  122. bool init(const char* pin = NULL) {
  123. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  124. if (!testAT()) {
  125. return false;
  126. }
  127. sendAT(GF("&FZE0")); // Factory + Reset + Echo Off
  128. if (waitResponse() != 1) {
  129. return false;
  130. }
  131. DBG(GF("### Modem:"), getModemName());
  132. int ret = getSimStatus();
  133. // if the sim isn't ready and a pin has been provided, try to unlock the sim
  134. if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
  135. simUnlock(pin);
  136. return (getSimStatus() == SIM_READY);
  137. }
  138. // if the sim is ready, or it's locked but no pin has been provided, return
  139. // true
  140. else {
  141. return (ret == SIM_READY || ret == SIM_LOCKED);
  142. }
  143. }
  144. String getModemName() {
  145. return "Quectel BG96";
  146. }
  147. TINY_GSM_MODEM_SET_BAUD_IPR()
  148. TINY_GSM_MODEM_TEST_AT()
  149. TINY_GSM_MODEM_MAINTAIN_CHECK_SOCKS()
  150. bool factoryDefault() {
  151. sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
  152. waitResponse();
  153. sendAT(GF("+IPR=0")); // Auto-baud
  154. waitResponse();
  155. sendAT(GF("&W")); // Write configuration
  156. return waitResponse() == 1;
  157. }
  158. TINY_GSM_MODEM_GET_INFO_ATI()
  159. bool hasSSL() {
  160. return false; // TODO: For now
  161. }
  162. bool hasWifi() {
  163. return false;
  164. }
  165. bool hasGPRS() {
  166. return true;
  167. }
  168. /*
  169. * Power functions
  170. */
  171. bool restart() {
  172. if (!testAT()) {
  173. return false;
  174. }
  175. sendAT(GF("+CFUN=1,1"));
  176. if (waitResponse(60000L, GF("POWERED DOWN")) != 1) {
  177. return false;
  178. }
  179. delay(3000);
  180. return init();
  181. }
  182. bool poweroff() {
  183. sendAT(GF("+QPOWD=1"));
  184. waitResponse(300); // returns OK first
  185. return waitResponse(300, GF("POWERED DOWN")) == 1;
  186. }
  187. bool radioOff() {
  188. sendAT(GF("+CFUN=0"));
  189. if (waitResponse(10000L) != 1) {
  190. return false;
  191. }
  192. delay(3000);
  193. return true;
  194. }
  195. /*
  196. * SIM card functions
  197. */
  198. TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
  199. String getSimCCID() {
  200. sendAT(GF("+QCCID"));
  201. if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) {
  202. return "";
  203. }
  204. String res = stream.readStringUntil('\n');
  205. waitResponse();
  206. res.trim();
  207. return res;
  208. }
  209. TINY_GSM_MODEM_GET_IMEI_GSN()
  210. SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
  211. for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
  212. sendAT(GF("+CPIN?"));
  213. if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
  214. delay(1000);
  215. continue;
  216. }
  217. int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED"));
  218. waitResponse();
  219. switch (status) {
  220. case 2:
  221. case 3: return SIM_LOCKED;
  222. case 1: return SIM_READY;
  223. default: return SIM_ERROR;
  224. }
  225. }
  226. return SIM_ERROR;
  227. }
  228. TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG)
  229. TINY_GSM_MODEM_GET_OPERATOR_COPS()
  230. /*
  231. * Generic network functions
  232. */
  233. TINY_GSM_MODEM_GET_CSQ()
  234. bool isNetworkConnected() {
  235. RegStatus s = getRegistrationStatus();
  236. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  237. }
  238. TINY_GSM_MODEM_WAIT_FOR_NETWORK()
  239. /*
  240. * GPRS functions
  241. */
  242. bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
  243. gprsDisconnect();
  244. //Configure the TCPIP Context
  245. sendAT(GF("+QICSGP=1,1,\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
  246. if (waitResponse() != 1) {
  247. return false;
  248. }
  249. //Activate GPRS/CSD Context
  250. sendAT(GF("+QIACT=1"));
  251. if (waitResponse(150000L) != 1) {
  252. return false;
  253. }
  254. //Attach to Packet Domain service - is this necessary?
  255. sendAT(GF("+CGATT=1"));
  256. if (waitResponse(60000L) != 1) {
  257. return false;
  258. }
  259. return true;
  260. }
  261. bool gprsDisconnect() {
  262. sendAT(GF("+QIDEACT=1")); // Deactivate the bearer context
  263. if (waitResponse(40000L) != 1)
  264. return false;
  265. return true;
  266. }
  267. TINY_GSM_MODEM_GET_GPRS_IP_CONNECTED()
  268. /*
  269. * IP Address functions
  270. */
  271. String getLocalIP() {
  272. sendAT(GF("+QILOCIP"));
  273. stream.readStringUntil('\n');
  274. String res = stream.readStringUntil('\n');
  275. if (waitResponse() != 1) {
  276. return "";
  277. }
  278. return res;
  279. }
  280. IPAddress localIP() {
  281. return TinyGsmIpFromString(getLocalIP());
  282. }
  283. /*
  284. * Phone Call functions
  285. */
  286. bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
  287. bool callAnswer() {
  288. sendAT(GF("A"));
  289. return waitResponse() == 1;
  290. }
  291. // Returns true on pick-up, false on error/busy
  292. bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  293. bool callHangup() {
  294. sendAT(GF("H"));
  295. return waitResponse() == 1;
  296. }
  297. // 0-9,*,#,A,B,C,D
  298. bool dtmfSend(char cmd, int duration_ms = 100) { // TODO: check
  299. duration_ms = constrain(duration_ms, 100, 1000);
  300. sendAT(GF("+VTD="), duration_ms / 100); // VTD accepts in 1/10 of a second
  301. waitResponse();
  302. sendAT(GF("+VTS="), cmd);
  303. return waitResponse(10000L) == 1;
  304. }
  305. /*
  306. * Messaging functions
  307. */
  308. String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  309. bool sendSMS(const String& number, const String& text) {
  310. sendAT(GF("+CMGF=1"));
  311. waitResponse();
  312. //Set GSM 7 bit default alphabet (3GPP TS 23.038)
  313. sendAT(GF("+CSCS=\"GSM\""));
  314. waitResponse();
  315. sendAT(GF("+CMGS=\""), number, GF("\""));
  316. if (waitResponse(GF(">")) != 1) {
  317. return false;
  318. }
  319. stream.print(text);
  320. stream.write((char)0x1A);
  321. stream.flush();
  322. return waitResponse(60000L) == 1;
  323. }
  324. bool sendSMS_UTF16(const String& number, const void* text, size_t len) {
  325. sendAT(GF("+CMGF=1"));
  326. waitResponse();
  327. sendAT(GF("+CSCS=\"HEX\""));
  328. waitResponse();
  329. sendAT(GF("+CSMP=17,167,0,8"));
  330. waitResponse();
  331. sendAT(GF("+CMGS=\""), number, GF("\""));
  332. if (waitResponse(GF(">")) != 1) {
  333. return false;
  334. }
  335. uint16_t* t = (uint16_t*)text;
  336. for (size_t i=0; i<len; i++) {
  337. uint8_t c = t[i] >> 8;
  338. if (c < 0x10) { stream.print('0'); }
  339. stream.print(c, HEX);
  340. c = t[i] & 0xFF;
  341. if (c < 0x10) { stream.print('0'); }
  342. stream.print(c, HEX);
  343. }
  344. stream.write((char)0x1A);
  345. stream.flush();
  346. return waitResponse(60000L) == 1;
  347. }
  348. /*
  349. * Location functions
  350. */
  351. String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
  352. /*
  353. * Battery & temperature functions
  354. */
  355. // Use: float vBatt = modem.getBattVoltage() / 1000.0;
  356. uint16_t getBattVoltage() {
  357. sendAT(GF("+CBC"));
  358. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  359. return 0;
  360. }
  361. streamSkipUntil(','); // Skip battery charge status
  362. streamSkipUntil(','); // Skip battery charge level
  363. // return voltage in mV
  364. uint16_t res = stream.readStringUntil(',').toInt();
  365. // Wait for final OK
  366. waitResponse();
  367. return res;
  368. }
  369. int8_t getBattPercent() {
  370. sendAT(GF("+CBC"));
  371. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  372. return false;
  373. }
  374. streamSkipUntil(','); // Skip battery charge status
  375. // Read battery charge level
  376. int res = stream.readStringUntil(',').toInt();
  377. // Wait for final OK
  378. waitResponse();
  379. return res;
  380. }
  381. uint8_t getBattChargeState() {
  382. sendAT(GF("+CBC?"));
  383. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  384. return false;
  385. }
  386. // Read battery charge status
  387. int res = stream.readStringUntil(',').toInt();
  388. // Wait for final OK
  389. waitResponse();
  390. return res;
  391. }
  392. bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
  393. sendAT(GF("+CBC?"));
  394. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  395. return false;
  396. }
  397. chargeState = stream.readStringUntil(',').toInt();
  398. percent = stream.readStringUntil(',').toInt();
  399. milliVolts = stream.readStringUntil('\n').toInt();
  400. // Wait for final OK
  401. waitResponse();
  402. return true;
  403. }
  404. float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE;
  405. /*
  406. * Client related functions
  407. */
  408. protected:
  409. bool modemConnect(const char* host, uint16_t port, uint8_t mux,
  410. bool ssl = false, int timeout_s = 20) {
  411. if (ssl) {
  412. DBG("SSL not yet supported on this module!");
  413. }
  414. int rsp;
  415. uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
  416. // <PDPcontextID>(1-16), <connectID>(0-11),"TCP/UDP/TCP LISTENER/UDP SERVICE",
  417. // "<IP_address>/<domain_name>",<remote_port>,<local_port>,<access_mode>(0-2 0=buffer)
  418. sendAT(GF("+QIOPEN=1,"), mux, ',', GF("\"TCP"), GF("\",\""), host, GF("\","), port, GF(",0,0"));
  419. rsp = waitResponse();
  420. if (waitResponse(timeout_ms, GF(GSM_NL "+QIOPEN:")) != 1) {
  421. return false;
  422. }
  423. if (stream.readStringUntil(',').toInt() != mux) {
  424. return false;
  425. }
  426. // Read status
  427. rsp = stream.readStringUntil('\n').toInt();
  428. return (0 == rsp);
  429. }
  430. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  431. sendAT(GF("+QISEND="), mux, ',', (uint16_t)len);
  432. if (waitResponse(GF(">")) != 1) {
  433. return 0;
  434. }
  435. stream.write((uint8_t*)buff, len);
  436. stream.flush();
  437. if (waitResponse(GF(GSM_NL "SEND OK")) != 1) {
  438. return 0;
  439. }
  440. // TODO: Wait for ACK? AT+QISEND=id,0
  441. return len;
  442. }
  443. size_t modemRead(size_t size, uint8_t mux) {
  444. sendAT(GF("+QIRD="), mux, ',', (uint16_t)size);
  445. if (waitResponse(GF("+QIRD:")) != 1) {
  446. return 0;
  447. }
  448. int len = stream.readStringUntil('\n').toInt();
  449. for (int i=0; i<len; i++) {
  450. TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT
  451. }
  452. waitResponse();
  453. DBG("### READ:", len, "from", mux);
  454. sockets[mux]->sock_available = modemGetAvailable(mux);
  455. return len;
  456. }
  457. size_t modemGetAvailable(uint8_t mux) {
  458. sendAT(GF("+QIRD="), mux, GF(",0"));
  459. size_t result = 0;
  460. if (waitResponse(GF("+QIRD:")) == 1) {
  461. streamSkipUntil(','); // Skip total received
  462. streamSkipUntil(','); // Skip have read
  463. result = stream.readStringUntil('\n').toInt();
  464. if (result) {
  465. DBG("### DATA AVAILABLE:", result, "on", mux);
  466. }
  467. waitResponse();
  468. }
  469. if (!result) {
  470. sockets[mux]->sock_connected = modemGetConnected(mux);
  471. }
  472. return result;
  473. }
  474. bool modemGetConnected(uint8_t mux) {
  475. sendAT(GF("+QISTATE=1,"), mux);
  476. //+QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1"
  477. if (waitResponse(GF("+QISTATE:")))
  478. return false;
  479. streamSkipUntil(','); // Skip mux
  480. streamSkipUntil(','); // Skip socket type
  481. streamSkipUntil(','); // Skip remote ip
  482. streamSkipUntil(','); // Skip remote port
  483. streamSkipUntil(','); // Skip local port
  484. int res = stream.readStringUntil(',').toInt(); // socket state
  485. waitResponse();
  486. // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
  487. return 2 == res;
  488. }
  489. public:
  490. /*
  491. Utilities
  492. */
  493. TINY_GSM_MODEM_STREAM_UTILITIES()
  494. // TODO: Optimize this!
  495. uint8_t waitResponse(uint32_t timeout_ms, String& data,
  496. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  497. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  498. {
  499. /*String r1s(r1); r1s.trim();
  500. String r2s(r2); r2s.trim();
  501. String r3s(r3); r3s.trim();
  502. String r4s(r4); r4s.trim();
  503. String r5s(r5); r5s.trim();
  504. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  505. data.reserve(64);
  506. int index = 0;
  507. unsigned long startMillis = millis();
  508. do {
  509. TINY_GSM_YIELD();
  510. while (stream.available() > 0) {
  511. TINY_GSM_YIELD();
  512. int a = stream.read();
  513. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  514. data += (char)a;
  515. if (r1 && data.endsWith(r1)) {
  516. index = 1;
  517. goto finish;
  518. } else if (r2 && data.endsWith(r2)) {
  519. index = 2;
  520. goto finish;
  521. } else if (r3 && data.endsWith(r3)) {
  522. index = 3;
  523. goto finish;
  524. } else if (r4 && data.endsWith(r4)) {
  525. index = 4;
  526. goto finish;
  527. } else if (r5 && data.endsWith(r5)) {
  528. index = 5;
  529. goto finish;
  530. } else if (data.endsWith(GF(GSM_NL "+QIURC:"))) {
  531. stream.readStringUntil('\"');
  532. String urc = stream.readStringUntil('\"');
  533. stream.readStringUntil(',');
  534. if (urc == "recv") {
  535. int mux = stream.readStringUntil('\n').toInt();
  536. DBG("### URC RECV:", mux);
  537. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  538. sockets[mux]->got_data = true;
  539. }
  540. } else if (urc == "closed") {
  541. int mux = stream.readStringUntil('\n').toInt();
  542. DBG("### URC CLOSE:", mux);
  543. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  544. sockets[mux]->sock_connected = false;
  545. }
  546. } else {
  547. stream.readStringUntil('\n');
  548. }
  549. data = "";
  550. }
  551. }
  552. } while (millis() - startMillis < timeout_ms);
  553. finish:
  554. if (!index) {
  555. data.trim();
  556. if (data.length()) {
  557. DBG("### Unhandled:", data);
  558. }
  559. data = "";
  560. }
  561. //data.replace(GSM_NL, "/");
  562. //DBG('<', index, '>', data);
  563. return index;
  564. }
  565. uint8_t waitResponse(uint32_t timeout_ms,
  566. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  567. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  568. {
  569. String data;
  570. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  571. }
  572. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  573. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  574. {
  575. return waitResponse(1000, r1, r2, r3, r4, r5);
  576. }
  577. public:
  578. Stream& stream;
  579. protected:
  580. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  581. };
  582. #endif