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.

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