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.

484 lines
13 KiB

6 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 SRC_TINYGSMCLIENTBG96_H_
  9. #define SRC_TINYGSMCLIENTBG96_H_
  10. // #pragma message("TinyGSM: TinyGsmClientBG96")
  11. // #define TINY_GSM_DEBUG Serial
  12. #define TINY_GSM_MUX_COUNT 12
  13. #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
  14. #include "TinyGsmBattery.tpp"
  15. #include "TinyGsmCalling.tpp"
  16. #include "TinyGsmGPRS.tpp"
  17. #include "TinyGsmModem.tpp"
  18. #include "TinyGsmSMS.tpp"
  19. #include "TinyGsmTCP.tpp"
  20. #include "TinyGsmTime.tpp"
  21. #define GSM_NL "\r\n"
  22. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  23. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  24. #if defined TINY_GSM_DEBUG
  25. static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:";
  26. #endif
  27. enum RegStatus {
  28. REG_NO_RESULT = -1,
  29. REG_UNREGISTERED = 0,
  30. REG_SEARCHING = 2,
  31. REG_DENIED = 3,
  32. REG_OK_HOME = 1,
  33. REG_OK_ROAMING = 5,
  34. REG_UNKNOWN = 4,
  35. };
  36. class TinyGsmBG96 : public TinyGsmModem<TinyGsmBG96>,
  37. public TinyGsmGPRS<TinyGsmBG96>,
  38. public TinyGsmTCP<TinyGsmBG96, TINY_GSM_MUX_COUNT>,
  39. public TinyGsmCalling<TinyGsmBG96>,
  40. public TinyGsmSMS<TinyGsmBG96>,
  41. public TinyGsmTime<TinyGsmBG96>,
  42. public TinyGsmBattery<TinyGsmBG96> {
  43. friend class TinyGsmModem<TinyGsmBG96>;
  44. friend class TinyGsmGPRS<TinyGsmBG96>;
  45. friend class TinyGsmTCP<TinyGsmBG96, TINY_GSM_MUX_COUNT>;
  46. friend class TinyGsmCalling<TinyGsmBG96>;
  47. friend class TinyGsmSMS<TinyGsmBG96>;
  48. friend class TinyGsmTime<TinyGsmBG96>;
  49. friend class TinyGsmBattery<TinyGsmBG96>;
  50. /*
  51. * Inner Client
  52. */
  53. public:
  54. class GsmClientBG96 : public GsmClient {
  55. friend class TinyGsmBG96;
  56. public:
  57. GsmClientBG96() {}
  58. explicit GsmClientBG96(TinyGsmBG96& modem, uint8_t mux = 1) {
  59. init(&modem, mux);
  60. }
  61. bool init(TinyGsmBG96* modem, uint8_t mux = 1) {
  62. this->at = modem;
  63. this->mux = mux;
  64. sock_available = 0;
  65. prev_check = 0;
  66. sock_connected = false;
  67. got_data = false;
  68. at->sockets[mux] = this;
  69. return true;
  70. }
  71. public:
  72. virtual int connect(const char* host, uint16_t port, int timeout_s) {
  73. stop();
  74. TINY_GSM_YIELD();
  75. rx.clear();
  76. sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
  77. return sock_connected;
  78. }
  79. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  80. virtual void stop(uint32_t maxWaitMs) {
  81. dumpModemBuffer(maxWaitMs);
  82. at->sendAT(GF("+QICLOSE="), mux);
  83. sock_connected = false;
  84. at->waitResponse();
  85. }
  86. void stop() override {
  87. stop(15000L);
  88. }
  89. /*
  90. * Extended API
  91. */
  92. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  93. };
  94. /*
  95. * Inner Secure Client
  96. */
  97. /*
  98. class GsmClientSecureBG96 : public GsmClientBG96
  99. {
  100. public:
  101. GsmClientSecure() {}
  102. GsmClientSecure(TinyGsmBG96& modem, uint8_t mux = 1)
  103. : public GsmClient(modem, mux)
  104. {}
  105. public:
  106. int connect(const char* host, uint16_t port, int timeout_s) override {
  107. stop();
  108. TINY_GSM_YIELD();
  109. rx.clear();
  110. sock_connected = at->modemConnect(host, port, mux, true, timeout_s);
  111. return sock_connected;
  112. }
  113. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  114. };
  115. */
  116. /*
  117. * Constructor
  118. */
  119. public:
  120. explicit TinyGsmBG96(Stream& stream) : stream(stream) {
  121. memset(sockets, 0, sizeof(sockets));
  122. }
  123. /*
  124. * Basic functions
  125. */
  126. protected:
  127. bool initImpl(const char* pin = NULL) {
  128. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  129. if (!testAT()) { return false; }
  130. sendAT(GF("E0")); // Echo Off
  131. if (waitResponse() != 1) { return false; }
  132. #ifdef TINY_GSM_DEBUG
  133. sendAT(GF("+CMEE=2")); // turn on verbose error codes
  134. #else
  135. sendAT(GF("+CMEE=0")); // turn off error codes
  136. #endif
  137. waitResponse();
  138. DBG(GF("### Modem:"), getModemName());
  139. // Enable automatic time zone update
  140. sendAT(GF("+CTZU=1"));
  141. if (waitResponse(10000L) != 1) { return false; }
  142. SimStatus ret = getSimStatus();
  143. // if the sim isn't ready and a pin has been provided, try to unlock the sim
  144. if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
  145. simUnlock(pin);
  146. return (getSimStatus() == SIM_READY);
  147. } else {
  148. // if the sim is ready, or it's locked but no pin has been provided,
  149. // return true
  150. return (ret == SIM_READY || ret == SIM_LOCKED);
  151. }
  152. }
  153. /*
  154. * Power functions
  155. */
  156. protected:
  157. bool restartImpl() {
  158. if (!testAT()) { return false; }
  159. sendAT(GF("+CFUN=1,1"));
  160. if (waitResponse(60000L, GF("POWERED DOWN")) != 1) { return false; }
  161. waitResponse(5000L, GF("RDY"));
  162. return init();
  163. }
  164. bool powerOffImpl() {
  165. sendAT(GF("+QPOWD=1"));
  166. waitResponse(300); // returns OK first
  167. return waitResponse(300, GF("POWERED DOWN")) == 1;
  168. }
  169. // When entering into sleep mode is enabled, DTR is pulled up, and WAKEUP_IN
  170. // is pulled up, the module can directly enter into sleep mode.If entering
  171. // into sleep mode is enabled, DTR is pulled down, and WAKEUP_IN is pulled
  172. // down, there is a need to pull the DTR pin and the WAKEUP_IN pin up first,
  173. // and then the module can enter into sleep mode.
  174. bool sleepEnableImpl(bool enable = true) {
  175. sendAT(GF("+QSCLK="), enable);
  176. return waitResponse() == 1;
  177. }
  178. /*
  179. * Generic network functions
  180. */
  181. public:
  182. RegStatus getRegistrationStatus() {
  183. return (RegStatus)getRegistrationStatusXREG("CREG");
  184. }
  185. protected:
  186. bool isNetworkConnectedImpl() {
  187. RegStatus s = getRegistrationStatus();
  188. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  189. }
  190. /*
  191. * GPRS functions
  192. */
  193. protected:
  194. bool gprsConnectImpl(const char* apn, const char* user = NULL,
  195. const char* pwd = NULL) {
  196. gprsDisconnect();
  197. // Configure the TCPIP Context
  198. sendAT(GF("+QICSGP=1,1,\""), apn, GF("\",\""), user, GF("\",\""), pwd,
  199. GF("\""));
  200. if (waitResponse() != 1) { return false; }
  201. // Activate GPRS/CSD Context
  202. sendAT(GF("+QIACT=1"));
  203. if (waitResponse(150000L) != 1) { return false; }
  204. // Attach to Packet Domain service - is this necessary?
  205. sendAT(GF("+CGATT=1"));
  206. if (waitResponse(60000L) != 1) { return false; }
  207. return true;
  208. }
  209. bool gprsDisconnectImpl() {
  210. sendAT(GF("+QIDEACT=1")); // Deactivate the bearer context
  211. if (waitResponse(40000L) != 1) { return false; }
  212. return true;
  213. }
  214. /*
  215. * SIM card functions
  216. */
  217. protected:
  218. String getSimCCIDImpl() {
  219. sendAT(GF("+QCCID"));
  220. if (waitResponse(GF(GSM_NL "+QCCID:")) != 1) { return ""; }
  221. String res = stream.readStringUntil('\n');
  222. waitResponse();
  223. res.trim();
  224. return res;
  225. }
  226. /*
  227. * Phone Call functions
  228. */
  229. protected:
  230. // Can follow all of the phone call functions from the template
  231. /*
  232. * Messaging functions
  233. */
  234. protected:
  235. // Follows all messaging functions per template
  236. /*
  237. * Time functions
  238. */
  239. protected:
  240. // Can follow the standard CCLK function in the template
  241. /*
  242. * Battery functions
  243. */
  244. /*
  245. * Client related functions
  246. */
  247. protected:
  248. bool modemConnect(const char* host, uint16_t port, uint8_t mux,
  249. bool ssl = false, int timeout_s = 20) {
  250. if (ssl) { DBG("SSL not yet supported on this module!"); }
  251. uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
  252. // <PDPcontextID>(1-16), <connectID>(0-11),
  253. // "TCP/UDP/TCP LISTENER/UDPSERVICE", "<IP_address>/<domain_name>",
  254. // <remote_port>,<local_port>,<access_mode>(0-2; 0=buffer)
  255. sendAT(GF("+QIOPEN=1,"), mux, GF(",\""), GF("TCP"), GF("\",\""), host,
  256. GF("\","), port, GF(",0,0"));
  257. waitResponse();
  258. if (waitResponse(timeout_ms, GF(GSM_NL "+QIOPEN:")) != 1) { return false; }
  259. if (streamGetIntBefore(',') != mux) { return false; }
  260. // Read status
  261. return (0 == streamGetIntBefore('\n'));
  262. }
  263. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  264. sendAT(GF("+QISEND="), mux, ',', (uint16_t)len);
  265. if (waitResponse(GF(">")) != 1) { return 0; }
  266. stream.write(reinterpret_cast<const uint8_t*>(buff), len);
  267. stream.flush();
  268. if (waitResponse(GF(GSM_NL "SEND OK")) != 1) { return 0; }
  269. // TODO(?): Wait for ACK? AT+QISEND=id,0
  270. return len;
  271. }
  272. size_t modemRead(size_t size, uint8_t mux) {
  273. sendAT(GF("+QIRD="), mux, ',', (uint16_t)size);
  274. if (waitResponse(GF("+QIRD:")) != 1) { return 0; }
  275. int16_t len = streamGetIntBefore('\n');
  276. for (int i = 0; i < len; i++) { moveCharFromStreamToFifo(mux); }
  277. waitResponse();
  278. // DBG("### READ:", len, "from", mux);
  279. sockets[mux]->sock_available = modemGetAvailable(mux);
  280. return len;
  281. }
  282. size_t modemGetAvailable(uint8_t mux) {
  283. sendAT(GF("+QIRD="), mux, GF(",0"));
  284. size_t result = 0;
  285. if (waitResponse(GF("+QIRD:")) == 1) {
  286. streamSkipUntil(','); // Skip total received
  287. streamSkipUntil(','); // Skip have read
  288. result = streamGetIntBefore('\n');
  289. if (result) { DBG("### DATA AVAILABLE:", result, "on", mux); }
  290. waitResponse();
  291. }
  292. if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
  293. return result;
  294. }
  295. bool modemGetConnected(uint8_t mux) {
  296. sendAT(GF("+QISTATE=1,"), mux);
  297. // +QISTATE: 0,"TCP","151.139.237.11",80,5087,4,1,0,0,"uart1"
  298. if (waitResponse(GF("+QISTATE:")) != 1) { return false; }
  299. streamSkipUntil(','); // Skip mux
  300. streamSkipUntil(','); // Skip socket type
  301. streamSkipUntil(','); // Skip remote ip
  302. streamSkipUntil(','); // Skip remote port
  303. streamSkipUntil(','); // Skip local port
  304. int8_t res = streamGetIntBefore(','); // socket state
  305. waitResponse();
  306. // 0 Initial, 1 Opening, 2 Connected, 3 Listening, 4 Closing
  307. return 2 == res;
  308. }
  309. /*
  310. * Utilities
  311. */
  312. public:
  313. // TODO(vshymanskyy): Optimize this!
  314. int8_t waitResponse(uint32_t timeout_ms, String& data,
  315. GsmConstStr r1 = GFP(GSM_OK),
  316. GsmConstStr r2 = GFP(GSM_ERROR),
  317. #if defined TINY_GSM_DEBUG
  318. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  319. #else
  320. GsmConstStr r3 = NULL,
  321. #endif
  322. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  323. /*String r1s(r1); r1s.trim();
  324. String r2s(r2); r2s.trim();
  325. String r3s(r3); r3s.trim();
  326. String r4s(r4); r4s.trim();
  327. String r5s(r5); r5s.trim();
  328. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  329. data.reserve(64);
  330. uint8_t index = 0;
  331. uint32_t startMillis = millis();
  332. do {
  333. TINY_GSM_YIELD();
  334. while (stream.available() > 0) {
  335. TINY_GSM_YIELD();
  336. int8_t a = stream.read();
  337. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  338. data += static_cast<char>(a);
  339. if (r1 && data.endsWith(r1)) {
  340. index = 1;
  341. goto finish;
  342. } else if (r2 && data.endsWith(r2)) {
  343. index = 2;
  344. goto finish;
  345. } else if (r3 && data.endsWith(r3)) {
  346. #if defined TINY_GSM_DEBUG
  347. if (r3 == GFP(GSM_CME_ERROR)) {
  348. streamSkipUntil('\n'); // Read out the error
  349. }
  350. #endif
  351. index = 3;
  352. goto finish;
  353. } else if (r4 && data.endsWith(r4)) {
  354. index = 4;
  355. goto finish;
  356. } else if (r5 && data.endsWith(r5)) {
  357. index = 5;
  358. goto finish;
  359. } else if (data.endsWith(GF(GSM_NL "+QIURC:"))) {
  360. streamSkipUntil('\"');
  361. String urc = stream.readStringUntil('\"');
  362. streamSkipUntil(',');
  363. if (urc == "recv") {
  364. int8_t mux = streamGetIntBefore('\n');
  365. DBG("### URC RECV:", mux);
  366. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  367. sockets[mux]->got_data = true;
  368. }
  369. } else if (urc == "closed") {
  370. int8_t mux = streamGetIntBefore('\n');
  371. DBG("### URC CLOSE:", mux);
  372. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  373. sockets[mux]->sock_connected = false;
  374. }
  375. } else {
  376. streamSkipUntil('\n');
  377. }
  378. data = "";
  379. }
  380. }
  381. } while (millis() - startMillis < timeout_ms);
  382. finish:
  383. if (!index) {
  384. data.trim();
  385. if (data.length()) { DBG("### Unhandled:", data); }
  386. data = "";
  387. }
  388. // data.replace(GSM_NL, "/");
  389. // DBG('<', index, '>', data);
  390. return index;
  391. }
  392. int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
  393. GsmConstStr r2 = GFP(GSM_ERROR),
  394. #if defined TINY_GSM_DEBUG
  395. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  396. #else
  397. GsmConstStr r3 = NULL,
  398. #endif
  399. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  400. String data;
  401. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  402. }
  403. int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
  404. GsmConstStr r2 = GFP(GSM_ERROR),
  405. #if defined TINY_GSM_DEBUG
  406. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  407. #else
  408. GsmConstStr r3 = NULL,
  409. #endif
  410. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  411. return waitResponse(1000, r1, r2, r3, r4, r5);
  412. }
  413. protected:
  414. Stream& stream;
  415. GsmClientBG96* sockets[TINY_GSM_MUX_COUNT];
  416. const char* gsmNL = GSM_NL;
  417. };
  418. #endif // SRC_TINYGSMCLIENTBG96_H_