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.

456 lines
12 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
7 years ago
8 years ago
8 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. /**
  2. * @file TinyGsmClientM590.h
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef SRC_TINYGSMCLIENTM590_H_
  9. #define SRC_TINYGSMCLIENTM590_H_
  10. // #pragma message("TinyGSM: TinyGsmClientM590")
  11. // #define TINY_GSM_DEBUG Serial
  12. #define TINY_GSM_MUX_COUNT 2
  13. #define TINY_GSM_NO_MODEM_BUFFER
  14. #include "TinyGsmGPRS.tpp"
  15. #include "TinyGsmModem.tpp"
  16. #include "TinyGsmSMS.tpp"
  17. #include "TinyGsmTCP.tpp"
  18. #include "TinyGsmTime.tpp"
  19. #define GSM_NL "\r\n"
  20. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  21. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  22. #if defined TINY_GSM_DEBUG
  23. static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:";
  24. #endif
  25. enum RegStatus {
  26. REG_NO_RESULT = -1,
  27. REG_UNREGISTERED = 0,
  28. REG_SEARCHING = 3,
  29. REG_DENIED = 2,
  30. REG_OK_HOME = 1,
  31. REG_OK_ROAMING = 5,
  32. REG_UNKNOWN = 4,
  33. };
  34. class TinyGsmM590 : public TinyGsmModem<TinyGsmM590>,
  35. public TinyGsmGPRS<TinyGsmM590>,
  36. public TinyGsmTCP<TinyGsmM590, TINY_GSM_MUX_COUNT>,
  37. public TinyGsmSMS<TinyGsmM590>,
  38. public TinyGsmTime<TinyGsmM590> {
  39. friend class TinyGsmModem<TinyGsmM590>;
  40. friend class TinyGsmGPRS<TinyGsmM590>;
  41. friend class TinyGsmTCP<TinyGsmM590, TINY_GSM_MUX_COUNT>;
  42. friend class TinyGsmSMS<TinyGsmM590>;
  43. friend class TinyGsmTime<TinyGsmM590>;
  44. /*
  45. * Inner Client
  46. */
  47. public:
  48. class GsmClientM590 : public GsmClient {
  49. friend class TinyGsmM590;
  50. public:
  51. GsmClientM590() {}
  52. explicit GsmClientM590(TinyGsmM590& modem, uint8_t mux = 1) {
  53. init(&modem, mux);
  54. }
  55. bool init(TinyGsmM590* modem, uint8_t mux = 1) {
  56. this->at = modem;
  57. this->mux = mux;
  58. sock_connected = false;
  59. at->sockets[mux] = this;
  60. return true;
  61. }
  62. public:
  63. int connect(const char* host, uint16_t port, int timeout_s) {
  64. stop();
  65. TINY_GSM_YIELD();
  66. rx.clear();
  67. sock_connected = at->modemConnect(host, port, mux, false, timeout_s);
  68. return sock_connected;
  69. }
  70. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  71. virtual void stop(uint32_t maxWaitMs) {
  72. TINY_GSM_YIELD();
  73. at->sendAT(GF("+TCPCLOSE="), mux);
  74. sock_connected = false;
  75. at->waitResponse(maxWaitMs);
  76. rx.clear();
  77. }
  78. void stop() override {
  79. stop(1000L);
  80. }
  81. /*
  82. * Extended API
  83. */
  84. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  85. };
  86. /*
  87. * Constructor
  88. */
  89. public:
  90. explicit TinyGsmM590(Stream& stream) : stream(stream) {
  91. memset(sockets, 0, sizeof(sockets));
  92. }
  93. /*
  94. * Basic functions
  95. */
  96. protected:
  97. bool initImpl(const char* pin = NULL) {
  98. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  99. if (!testAT()) { return false; }
  100. sendAT(GF("E0")); // Echo Off
  101. if (waitResponse() != 1) { return false; }
  102. #ifdef TINY_GSM_DEBUG
  103. sendAT(GF("+CMEE=2")); // turn on verbose error codes
  104. #else
  105. sendAT(GF("+CMEE=0")); // turn off error codes
  106. #endif
  107. waitResponse();
  108. DBG(GF("### Modem:"), getModemName());
  109. SimStatus ret = getSimStatus();
  110. // if the sim isn't ready and a pin has been provided, try to unlock the sim
  111. if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
  112. simUnlock(pin);
  113. return (getSimStatus() == SIM_READY);
  114. } else {
  115. // if the sim is ready, or it's locked but no pin has been provided,
  116. // return true
  117. return (ret == SIM_READY || ret == SIM_LOCKED);
  118. }
  119. }
  120. // Doesn't support CGMI
  121. String getModemNameImpl() {
  122. return "Neoway M590";
  123. }
  124. // Extra stuff here - pwr save, internal stack
  125. bool factoryDefaultImpl() {
  126. sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
  127. waitResponse();
  128. sendAT(GF("+ICF=3,1")); // 8 data 0 parity 1 stop
  129. waitResponse();
  130. sendAT(GF("+ENPWRSAVE=0")); // Disable PWR save
  131. waitResponse();
  132. sendAT(GF("+XISP=0")); // Use internal stack
  133. waitResponse();
  134. sendAT(GF("&W")); // Write configuration
  135. return waitResponse() == 1;
  136. }
  137. /*
  138. * Power functions
  139. */
  140. protected:
  141. bool restartImpl() {
  142. if (!testAT()) { return false; }
  143. sendAT(GF("+CFUN=15"));
  144. if (waitResponse(10000L) != 1) { return false; }
  145. // MODEM:STARTUP
  146. waitResponse(60000L, GF(GSM_NL "+PBREADY" GSM_NL));
  147. return init();
  148. }
  149. bool powerOffImpl() {
  150. sendAT(GF("+CPWROFF"));
  151. return waitResponse(3000L) == 1;
  152. }
  153. bool sleepEnableImpl(bool enable = true) {
  154. sendAT(GF("+ENPWRSAVE="), enable);
  155. return waitResponse() == 1;
  156. }
  157. /*
  158. * Generic network functions
  159. */
  160. public:
  161. RegStatus getRegistrationStatus() {
  162. return (RegStatus)getRegistrationStatusXREG("CREG");
  163. }
  164. protected:
  165. bool isNetworkConnectedImpl() {
  166. RegStatus s = getRegistrationStatus();
  167. return (s == REG_OK_HOME || s == REG_OK_ROAMING);
  168. }
  169. String getLocalIPImpl() {
  170. sendAT(GF("+XIIC?"));
  171. if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { return ""; }
  172. streamSkipUntil(',');
  173. String res = stream.readStringUntil('\n');
  174. waitResponse();
  175. res.trim();
  176. return res;
  177. }
  178. /*
  179. * GPRS functions
  180. */
  181. protected:
  182. bool gprsConnectImpl(const char* apn, const char* user = NULL,
  183. const char* pwd = NULL) {
  184. gprsDisconnect();
  185. sendAT(GF("+XISP=0"));
  186. waitResponse();
  187. sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
  188. waitResponse();
  189. if (!user) user = "";
  190. if (!pwd) pwd = "";
  191. sendAT(GF("+XGAUTH=1,1,\""), user, GF("\",\""), pwd, GF("\""));
  192. waitResponse();
  193. sendAT(GF("+XIIC=1"));
  194. waitResponse();
  195. const uint32_t timeout_ms = 60000L;
  196. for (uint32_t start = millis(); millis() - start < timeout_ms;) {
  197. if (isGprsConnected()) {
  198. // goto set_dns; // TODO
  199. return true;
  200. }
  201. delay(500);
  202. }
  203. return false;
  204. // set_dns: // TODO
  205. // sendAT(GF("+DNSSERVER=1,8.8.8.8"));
  206. // waitResponse();
  207. //
  208. // sendAT(GF("+DNSSERVER=2,8.8.4.4"));
  209. // waitResponse();
  210. return true;
  211. }
  212. bool gprsDisconnectImpl() {
  213. // TODO(?): There is no command in AT command set
  214. // XIIC=0 does not work
  215. return true;
  216. }
  217. bool isGprsConnectedImpl() {
  218. sendAT(GF("+XIIC?"));
  219. if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) { return false; }
  220. int8_t res = streamGetIntBefore(',');
  221. waitResponse();
  222. return res == 1;
  223. }
  224. /*
  225. * SIM card functions
  226. */
  227. protected:
  228. // Able to follow all SIM card functions as inherited from the template
  229. /*
  230. * Messaging functions
  231. */
  232. protected:
  233. bool sendSMS_UTF16Impl(const String& number, const void* text,
  234. size_t len) TINY_GSM_ATTR_NOT_AVAILABLE;
  235. /*
  236. * Time functions
  237. */
  238. protected:
  239. // Can follow the standard CCLK function in the template
  240. /*
  241. * Client related functions
  242. */
  243. protected:
  244. bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool,
  245. int timeout_s = 75) {
  246. uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
  247. for (int i = 0; i < 3; i++) { // TODO(?): no need for loop?
  248. String ip = dnsIpQuery(host);
  249. sendAT(GF("+TCPSETUP="), mux, GF(","), ip, GF(","), port);
  250. int8_t rsp = waitResponse(timeout_ms, GF(",OK" GSM_NL),
  251. GF(",FAIL" GSM_NL),
  252. GF("+TCPSETUP:Error" GSM_NL));
  253. if (1 == rsp) {
  254. return true;
  255. } else if (3 == rsp) {
  256. sendAT(GF("+TCPCLOSE="), mux);
  257. waitResponse();
  258. }
  259. delay(1000);
  260. }
  261. return false;
  262. }
  263. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  264. sendAT(GF("+TCPSEND="), mux, ',', (uint16_t)len);
  265. if (waitResponse(GF(">")) != 1) { return 0; }
  266. stream.write(reinterpret_cast<const uint8_t*>(buff), len);
  267. stream.write(static_cast<char>(0x0D));
  268. stream.flush();
  269. if (waitResponse(30000L, GF(GSM_NL "+TCPSEND:")) != 1) { return 0; }
  270. streamSkipUntil('\n');
  271. return len;
  272. }
  273. bool modemGetConnected(uint8_t mux) {
  274. sendAT(GF("+CIPSTATUS="), mux);
  275. int8_t res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""),
  276. GF(",\"CLOSING\""), GF(",\"INITIAL\""));
  277. waitResponse();
  278. return 1 == res;
  279. }
  280. String dnsIpQuery(const char* host) {
  281. sendAT(GF("+DNS=\""), host, GF("\""));
  282. if (waitResponse(10000L, GF(GSM_NL "+DNS:")) != 1) { return ""; }
  283. String res = stream.readStringUntil('\n');
  284. waitResponse(GF("+DNS:OK" GSM_NL));
  285. res.trim();
  286. return res;
  287. }
  288. /*
  289. * Utilities
  290. */
  291. public:
  292. // TODO(vshymanskyy): Optimize this!
  293. int8_t waitResponse(uint32_t timeout_ms, String& data,
  294. GsmConstStr r1 = GFP(GSM_OK),
  295. GsmConstStr r2 = GFP(GSM_ERROR),
  296. #if defined TINY_GSM_DEBUG
  297. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  298. #else
  299. GsmConstStr r3 = NULL,
  300. #endif
  301. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  302. /*String r1s(r1); r1s.trim();
  303. String r2s(r2); r2s.trim();
  304. String r3s(r3); r3s.trim();
  305. String r4s(r4); r4s.trim();
  306. String r5s(r5); r5s.trim();
  307. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  308. data.reserve(64);
  309. uint8_t index = 0;
  310. uint32_t startMillis = millis();
  311. do {
  312. TINY_GSM_YIELD();
  313. while (stream.available() > 0) {
  314. TINY_GSM_YIELD();
  315. int8_t a = stream.read();
  316. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  317. data += static_cast<char>(a);
  318. if (r1 && data.endsWith(r1)) {
  319. index = 1;
  320. goto finish;
  321. } else if (r2 && data.endsWith(r2)) {
  322. index = 2;
  323. goto finish;
  324. } else if (r3 && data.endsWith(r3)) {
  325. #if defined TINY_GSM_DEBUG
  326. if (r3 == GFP(GSM_CME_ERROR)) {
  327. streamSkipUntil('\n'); // Read out the error
  328. }
  329. #endif
  330. index = 3;
  331. goto finish;
  332. } else if (r4 && data.endsWith(r4)) {
  333. index = 4;
  334. goto finish;
  335. } else if (r5 && data.endsWith(r5)) {
  336. index = 5;
  337. goto finish;
  338. } else if (data.endsWith(GF("+TCPRECV:"))) {
  339. int8_t mux = streamGetIntBefore(',');
  340. int16_t len = streamGetIntBefore(',');
  341. int16_t len_orig = len;
  342. if (len > sockets[mux]->rx.free()) {
  343. DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
  344. } else {
  345. DBG("### Got: ", len, "->", sockets[mux]->rx.free());
  346. }
  347. while (len--) { moveCharFromStreamToFifo(mux); }
  348. // TODO(?): Handle lost characters
  349. if (len_orig > sockets[mux]->available()) {
  350. DBG("### Fewer characters received than expected: ",
  351. sockets[mux]->available(), " vs ", len_orig);
  352. }
  353. data = "";
  354. } else if (data.endsWith(GF("+TCPCLOSE:"))) {
  355. int8_t mux = streamGetIntBefore(',');
  356. streamSkipUntil('\n');
  357. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT) {
  358. sockets[mux]->sock_connected = false;
  359. }
  360. data = "";
  361. DBG("### Closed: ", mux);
  362. }
  363. }
  364. } while (millis() - startMillis < timeout_ms);
  365. finish:
  366. if (!index) {
  367. data.trim();
  368. if (data.length()) { DBG("### Unhandled:", data); }
  369. data = "";
  370. }
  371. // data.replace(GSM_NL, "/");
  372. // DBG('<', index, '>', data);
  373. return index;
  374. }
  375. int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
  376. GsmConstStr r2 = GFP(GSM_ERROR),
  377. #if defined TINY_GSM_DEBUG
  378. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  379. #else
  380. GsmConstStr r3 = NULL,
  381. #endif
  382. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  383. String data;
  384. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  385. }
  386. int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
  387. GsmConstStr r2 = GFP(GSM_ERROR),
  388. #if defined TINY_GSM_DEBUG
  389. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  390. #else
  391. GsmConstStr r3 = NULL,
  392. #endif
  393. GsmConstStr r4 = NULL, GsmConstStr r5 = NULL) {
  394. return waitResponse(1000, r1, r2, r3, r4, r5);
  395. }
  396. protected:
  397. Stream& stream;
  398. GsmClientM590* sockets[TINY_GSM_MUX_COUNT];
  399. const char* gsmNL = GSM_NL;
  400. };
  401. #endif // SRC_TINYGSMCLIENTM590_H_