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

7 years ago
5 years ago
7 years ago
7 years ago
7 years ago
7 years ago
5 years ago
7 years ago
7 years ago
5 years ago
  1. /**
  2. * @file TinyGsmClientA6.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef TinyGsmClientA6_h
  9. #define TinyGsmClientA6_h
  10. //#pragma message("TinyGSM: TinyGsmClientA6")
  11. //#define TINY_GSM_DEBUG Serial
  12. #if !defined(TINY_GSM_RX_BUFFER)
  13. #define TINY_GSM_RX_BUFFER 256
  14. #endif
  15. #define TINY_GSM_MUX_COUNT 8
  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 TinyGsmA6
  34. {
  35. public:
  36. class GsmClient : public Client
  37. {
  38. friend class TinyGsmA6;
  39. typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
  40. public:
  41. GsmClient() {}
  42. GsmClient(TinyGsmA6& modem) {
  43. init(&modem);
  44. }
  45. virtual ~GsmClient(){}
  46. bool init(TinyGsmA6* modem) {
  47. this->at = modem;
  48. this->mux = -1;
  49. sock_connected = false;
  50. return true;
  51. }
  52. public:
  53. virtual int connect(const char *host, uint16_t port, int timeout_s) {
  54. stop();
  55. TINY_GSM_YIELD();
  56. rx.clear();
  57. uint8_t newMux = -1;
  58. sock_connected = at->modemConnect(host, port, &newMux, timeout_s);
  59. if (sock_connected) {
  60. mux = newMux;
  61. at->sockets[mux] = this;
  62. }
  63. return sock_connected;
  64. }
  65. TINY_GSM_CLIENT_CONNECT_OVERLOADS()
  66. virtual void stop(uint32_t maxWaitMs) {
  67. TINY_GSM_YIELD();
  68. at->sendAT(GF("+CIPCLOSE="), mux);
  69. sock_connected = false;
  70. at->waitResponse(maxWaitMs);
  71. rx.clear();
  72. }
  73. virtual void stop() { stop(1000L); }
  74. TINY_GSM_CLIENT_WRITE()
  75. TINY_GSM_CLIENT_AVAILABLE_NO_MODEM_FIFO()
  76. TINY_GSM_CLIENT_READ_NO_MODEM_FIFO()
  77. TINY_GSM_CLIENT_PEEK_FLUSH_CONNECTED()
  78. /*
  79. * Extended API
  80. */
  81. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  82. private:
  83. TinyGsmA6* at;
  84. uint8_t mux;
  85. bool sock_connected;
  86. RxFifo rx;
  87. };
  88. public:
  89. TinyGsmA6(Stream& stream)
  90. : stream(stream)
  91. {
  92. memset(sockets, 0, sizeof(sockets));
  93. }
  94. virtual ~TinyGsmA6() {}
  95. /*
  96. * Basic functions
  97. */
  98. bool begin(const char* pin = NULL) {
  99. return init(pin);
  100. }
  101. bool init(const char* pin = NULL) {
  102. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  103. if (!testAT()) {
  104. return false;
  105. }
  106. sendAT(GF("&FZE0")); // Factory + Reset + Echo Off
  107. if (waitResponse() != 1) {
  108. return false;
  109. }
  110. #ifdef TINY_GSM_DEBUG
  111. sendAT(GF("+CMEE=2")); // turn on verbose error codes
  112. #else
  113. sendAT(GF("+CMEE=0")); // turn off error codes
  114. #endif
  115. waitResponse();
  116. sendAT(GF("+CMER=3,0,0,2")); // Set unsolicited result code output destination
  117. waitResponse();
  118. DBG(GF("### Modem:"), getModemName());
  119. int ret = getSimStatus();
  120. // if the sim isn't ready and a pin has been provided, try to unlock the sim
  121. if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
  122. simUnlock(pin);
  123. return (getSimStatus() == SIM_READY);
  124. }
  125. // if the sim is ready, or it's locked but no pin has been provided, return
  126. // true
  127. else {
  128. return (ret == SIM_READY || ret == SIM_LOCKED);
  129. }
  130. }
  131. String getModemName() {
  132. #if defined(TINY_GSM_MODEM_A6)
  133. return "AI-Thinker A6";
  134. #elif defined(TINY_GSM_MODEM_A7)
  135. return "AI-Thinker A7";
  136. #endif
  137. return "AI-Thinker A6";
  138. }
  139. TINY_GSM_MODEM_SET_BAUD_IPR()
  140. TINY_GSM_MODEM_TEST_AT()
  141. TINY_GSM_MODEM_MAINTAIN_LISTEN()
  142. bool factoryDefault() {
  143. sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
  144. waitResponse();
  145. sendAT(GF("&W")); // Write configuration
  146. return waitResponse() == 1;
  147. }
  148. TINY_GSM_MODEM_GET_INFO_ATI()
  149. bool hasSSL() {
  150. return false;
  151. }
  152. bool hasWifi() {
  153. return false;
  154. }
  155. bool hasGPRS() {
  156. return true;
  157. }
  158. /*
  159. * Power functions
  160. */
  161. bool restart() {
  162. if (!testAT()) {
  163. return false;
  164. }
  165. sendAT(GF("+RST=1"));
  166. delay(3000);
  167. return init();
  168. }
  169. bool poweroff() {
  170. sendAT(GF("+CPOF"));
  171. return waitResponse() == 1;
  172. }
  173. bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  174. bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
  175. /*
  176. * SIM card functions
  177. */
  178. TINY_GSM_MODEM_SIM_UNLOCK_CPIN()
  179. String getSimCCID() {
  180. sendAT(GF("+CCID"));
  181. if (waitResponse(GF(GSM_NL "+SCID: SIM Card ID:")) != 1) {
  182. return "";
  183. }
  184. String res = stream.readStringUntil('\n');
  185. waitResponse();
  186. res.trim();
  187. return res;
  188. }
  189. TINY_GSM_MODEM_GET_IMEI_GSN()
  190. SimStatus getSimStatus(unsigned long timeout_ms = 10000L) {
  191. for (unsigned long start = millis(); millis() - start < timeout_ms; ) {
  192. sendAT(GF("+CPIN?"));
  193. if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
  194. delay(1000);
  195. continue;
  196. }
  197. int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"));
  198. waitResponse();
  199. switch (status) {
  200. case 2:
  201. case 3: return SIM_LOCKED;
  202. case 1: return SIM_READY;
  203. default: return SIM_ERROR;
  204. }
  205. }
  206. return SIM_ERROR;
  207. }
  208. TINY_GSM_MODEM_GET_REGISTRATION_XREG(CREG)
  209. String getOperator() {
  210. sendAT(GF("+COPS=3,0")); // Set format
  211. waitResponse();
  212. sendAT(GF("+COPS?"));
  213. if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
  214. return "";
  215. }
  216. streamSkipUntil('"'); // Skip mode and format
  217. String res = stream.readStringUntil('"');
  218. waitResponse();
  219. return res;
  220. }
  221. /*
  222. * Generic network functions
  223. */
  224. TINY_GSM_MODEM_GET_CSQ()
  225. bool isNetworkConnected() {
  226. RegStatus s = getRegistrationStatus();
  227. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  228. }
  229. TINY_GSM_MODEM_WAIT_FOR_NETWORK()
  230. /*
  231. * GPRS functions
  232. */
  233. bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL) {
  234. gprsDisconnect();
  235. sendAT(GF("+CGATT=1"));
  236. if (waitResponse(60000L) != 1)
  237. return false;
  238. // TODO: wait AT+CGATT?
  239. sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
  240. waitResponse();
  241. if (!user) user = "";
  242. if (!pwd) pwd = "";
  243. sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
  244. if (waitResponse(60000L) != 1) {
  245. return false;
  246. }
  247. sendAT(GF("+CGACT=1,1"));
  248. waitResponse(60000L);
  249. sendAT(GF("+CIPMUX=1"));
  250. if (waitResponse() != 1) {
  251. return false;
  252. }
  253. return true;
  254. }
  255. bool gprsDisconnect() {
  256. // Shut the TCP/IP connection
  257. sendAT(GF("+CIPSHUT"));
  258. if (waitResponse(60000L) != 1)
  259. return false;
  260. for (int i = 0; i<3; i++) {
  261. sendAT(GF("+CGATT=0"));
  262. if (waitResponse(5000L) == 1)
  263. return true;
  264. }
  265. return false;
  266. }
  267. bool isGprsConnected() {
  268. sendAT(GF("+CGATT?"));
  269. if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
  270. return false;
  271. }
  272. int res = stream.readStringUntil('\n').toInt();
  273. waitResponse();
  274. return (res == 1);
  275. }
  276. /*
  277. * IP Address functions
  278. */
  279. String getLocalIP() {
  280. sendAT(GF("+CIFSR"));
  281. String res;
  282. if (waitResponse(10000L, res) != 1) {
  283. return "";
  284. }
  285. res.replace(GSM_NL "OK" GSM_NL, "");
  286. res.replace(GSM_NL, "");
  287. res.trim();
  288. return res;
  289. }
  290. IPAddress localIP() {
  291. return TinyGsmIpFromString(getLocalIP());
  292. }
  293. /*
  294. * Phone Call functions
  295. */
  296. bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
  297. bool callAnswer() {
  298. sendAT(GF("A"));
  299. return waitResponse() == 1;
  300. }
  301. // Returns true on pick-up, false on error/busy
  302. bool callNumber(const String& number) {
  303. if (number == GF("last")) {
  304. sendAT(GF("DLST"));
  305. } else {
  306. sendAT(GF("D\""), number, "\";");
  307. }
  308. if (waitResponse(5000L) != 1) {
  309. return false;
  310. }
  311. if (waitResponse(60000L,
  312. GF(GSM_NL "+CIEV: \"CALL\",1"),
  313. GF(GSM_NL "+CIEV: \"CALL\",0"),
  314. GFP(GSM_ERROR)) != 1)
  315. {
  316. return false;
  317. }
  318. int rsp = waitResponse(60000L,
  319. GF(GSM_NL "+CIEV: \"SOUNDER\",0"),
  320. GF(GSM_NL "+CIEV: \"CALL\",0"));
  321. int rsp2 = waitResponse(300L, GF(GSM_NL "BUSY" GSM_NL), GF(GSM_NL "NO ANSWER" GSM_NL));
  322. return rsp == 1 && rsp2 == 0;
  323. }
  324. bool callHangup() {
  325. sendAT(GF("H"));
  326. return waitResponse() == 1;
  327. }
  328. // 0-9,*,#,A,B,C,D
  329. bool dtmfSend(char cmd, unsigned duration_ms = 100) {
  330. duration_ms = constrain(duration_ms, 100, 1000);
  331. // The duration parameter is not working, so we simulate it using delay..
  332. // TODO: Maybe there's another way...
  333. //sendAT(GF("+VTD="), duration_ms / 100);
  334. //waitResponse();
  335. sendAT(GF("+VTS="), cmd);
  336. if (waitResponse(10000L) == 1) {
  337. delay(duration_ms);
  338. return true;
  339. }
  340. return false;
  341. }
  342. /*
  343. * Audio functions
  344. */
  345. bool audioSetHeadphones() {
  346. sendAT(GF("+SNFS=0"));
  347. return waitResponse() == 1;
  348. }
  349. bool audioSetSpeaker() {
  350. sendAT(GF("+SNFS=1"));
  351. return waitResponse() == 1;
  352. }
  353. bool audioMuteMic(bool mute) {
  354. sendAT(GF("+CMUT="), mute);
  355. return waitResponse() == 1;
  356. }
  357. /*
  358. * Messaging functions
  359. */
  360. String sendUSSD(const String& code) {
  361. sendAT(GF("+CMGF=1"));
  362. waitResponse();
  363. sendAT(GF("+CSCS=\"HEX\""));
  364. waitResponse();
  365. sendAT(GF("+CUSD=1,\""), code, GF("\",15"));
  366. if (waitResponse(10000L) != 1) {
  367. return "";
  368. }
  369. if (waitResponse(GF(GSM_NL "+CUSD:")) != 1) {
  370. return "";
  371. }
  372. stream.readStringUntil('"');
  373. String hex = stream.readStringUntil('"');
  374. stream.readStringUntil(',');
  375. int dcs = stream.readStringUntil('\n').toInt();
  376. if (dcs == 15) {
  377. return TinyGsmDecodeHex7bit(hex);
  378. } else if (dcs == 72) {
  379. return TinyGsmDecodeHex16bit(hex);
  380. } else {
  381. return hex;
  382. }
  383. }
  384. bool sendSMS(const String& number, const String& text) {
  385. sendAT(GF("+CMGF=1"));
  386. waitResponse();
  387. sendAT(GF("+CMGS=\""), number, GF("\""));
  388. if (waitResponse(GF(">")) != 1) {
  389. return false;
  390. }
  391. stream.print(text);
  392. stream.write((char)0x1A);
  393. stream.flush();
  394. return waitResponse(60000L) == 1;
  395. }
  396. /*
  397. * Location functions
  398. */
  399. String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
  400. /*
  401. * Battery & temperature functions
  402. */
  403. uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
  404. int8_t getBattPercent() {
  405. sendAT(GF("+CBC?"));
  406. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  407. return false;
  408. }
  409. streamSkipUntil(','); // Skip battery charge status
  410. // Read battery charge level
  411. int res = stream.readStringUntil('\n').toInt();
  412. // Wait for final OK
  413. waitResponse();
  414. return res;
  415. }
  416. uint8_t getBattChargeState() {
  417. sendAT(GF("+CBC?"));
  418. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  419. return false;
  420. }
  421. // Read battery charge status
  422. int res = stream.readStringUntil(',').toInt();
  423. // Wait for final OK
  424. waitResponse();
  425. return res;
  426. }
  427. bool getBattStats(uint8_t &chargeState, int8_t &percent, uint16_t &milliVolts) {
  428. sendAT(GF("+CBC?"));
  429. if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
  430. return false;
  431. }
  432. chargeState = stream.readStringUntil(',').toInt();
  433. percent = stream.readStringUntil('\n').toInt();
  434. milliVolts = 0;
  435. // Wait for final OK
  436. waitResponse();
  437. return true;
  438. }
  439. float getTemperature() TINY_GSM_ATTR_NOT_AVAILABLE;
  440. /*
  441. * Client related functions
  442. */
  443. protected:
  444. bool modemConnect(const char* host, uint16_t port, uint8_t* mux, int timeout_s = 75) {
  445. unsigned long startMillis = millis();
  446. uint32_t timeout_ms = ((uint32_t)timeout_s)*1000;
  447. sendAT(GF("+CIPSTART="), GF("\"TCP"), GF("\",\""), host, GF("\","), port);
  448. if (waitResponse(timeout_ms, GF(GSM_NL "+CIPNUM:")) != 1) {
  449. return false;
  450. }
  451. int newMux = stream.readStringUntil('\n').toInt();
  452. int rsp = waitResponse((timeout_ms- (millis() - startMillis)),
  453. GF("CONNECT OK" GSM_NL),
  454. GF("CONNECT FAIL" GSM_NL),
  455. GF("ALREADY CONNECT" GSM_NL));
  456. if (waitResponse() != 1) {
  457. return false;
  458. }
  459. *mux = newMux;
  460. return (1 == rsp);
  461. }
  462. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  463. sendAT(GF("+CIPSEND="), mux, ',', (uint16_t)len);
  464. if (waitResponse(2000L, GF(GSM_NL ">")) != 1) {
  465. return 0;
  466. }
  467. stream.write((uint8_t*)buff, len);
  468. stream.flush();
  469. if (waitResponse(10000L, GFP(GSM_OK), GF(GSM_NL "FAIL")) != 1) {
  470. return 0;
  471. }
  472. return len;
  473. }
  474. bool modemGetConnected(uint8_t) {
  475. sendAT(GF("+CIPSTATUS")); //TODO mux?
  476. int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""), GF(",\"INITIAL\""));
  477. waitResponse();
  478. return 1 == res;
  479. }
  480. public:
  481. /*
  482. Utilities
  483. */
  484. TINY_GSM_MODEM_STREAM_UTILITIES()
  485. // TODO: Optimize this!
  486. uint8_t waitResponse(uint32_t timeout_ms, String& data,
  487. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  488. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  489. {
  490. /*String r1s(r1); r1s.trim();
  491. String r2s(r2); r2s.trim();
  492. String r3s(r3); r3s.trim();
  493. String r4s(r4); r4s.trim();
  494. String r5s(r5); r5s.trim();
  495. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  496. data.reserve(64);
  497. int index = 0;
  498. unsigned long startMillis = millis();
  499. do {
  500. TINY_GSM_YIELD();
  501. while (stream.available() > 0) {
  502. TINY_GSM_YIELD();
  503. int a = stream.read();
  504. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  505. data += (char)a;
  506. if (r1 && data.endsWith(r1)) {
  507. index = 1;
  508. goto finish;
  509. } else if (r2 && data.endsWith(r2)) {
  510. index = 2;
  511. goto finish;
  512. } else if (r3 && data.endsWith(r3)) {
  513. index = 3;
  514. goto finish;
  515. } else if (r4 && data.endsWith(r4)) {
  516. index = 4;
  517. goto finish;
  518. } else if (r5 && data.endsWith(r5)) {
  519. index = 5;
  520. goto finish;
  521. } else if (data.endsWith(GF("+CIPRCV:"))) {
  522. int mux = stream.readStringUntil(',').toInt();
  523. int len = stream.readStringUntil(',').toInt();
  524. int len_orig = len;
  525. if (len > sockets[mux]->rx.free()) {
  526. DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
  527. } else {
  528. DBG("### Got: ", len, "->", sockets[mux]->rx.free());
  529. }
  530. while (len--) {
  531. TINY_GSM_MODEM_STREAM_TO_MUX_FIFO_WITH_DOUBLE_TIMEOUT
  532. }
  533. if (len_orig > sockets[mux]->available()) { // TODO
  534. DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
  535. }
  536. data = "";
  537. } else if (data.endsWith(GF("+TCPCLOSED:"))) {
  538. int mux = stream.readStringUntil('\n').toInt();
  539. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT) {
  540. sockets[mux]->sock_connected = false;
  541. }
  542. data = "";
  543. DBG("### Closed: ", mux);
  544. }
  545. }
  546. } while (millis() - startMillis < timeout_ms);
  547. finish:
  548. if (!index) {
  549. data.trim();
  550. if (data.length()) {
  551. DBG("### Unhandled:", data);
  552. }
  553. data = "";
  554. }
  555. //data.replace(GSM_NL, "/");
  556. //DBG('<', index, '>', data);
  557. return index;
  558. }
  559. uint8_t waitResponse(uint32_t timeout_ms,
  560. GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  561. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  562. {
  563. String data;
  564. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  565. }
  566. uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
  567. GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
  568. {
  569. return waitResponse(1000, r1, r2, r3, r4, r5);
  570. }
  571. public:
  572. Stream& stream;
  573. protected:
  574. GsmClient* sockets[TINY_GSM_MUX_COUNT];
  575. };
  576. #endif