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.

854 lines
28 KiB

5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
4 years ago
5 years ago
5 years ago
4 years ago
  1. /**
  2. * @file TinyGsmClientUBLOX.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_TINYGSMCLIENTUBLOX_H_
  9. #define SRC_TINYGSMCLIENTUBLOX_H_
  10. // #pragma message("TinyGSM: TinyGsmClientUBLOX")
  11. // #define TINY_GSM_DEBUG Serial
  12. #define TINY_GSM_MUX_COUNT 7
  13. #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
  14. #include "TinyGsmBattery.tpp"
  15. #include "TinyGsmCalling.tpp"
  16. #include "TinyGsmGPRS.tpp"
  17. #include "TinyGsmGPS.tpp"
  18. #include "TinyGsmGSMLocation.tpp"
  19. #include "TinyGsmModem.tpp"
  20. #include "TinyGsmSMS.tpp"
  21. #include "TinyGsmSSL.tpp"
  22. #include "TinyGsmTCP.tpp"
  23. #include "TinyGsmTime.tpp"
  24. #define GSM_NL "\r\n"
  25. static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
  26. static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
  27. #if defined TINY_GSM_DEBUG
  28. static const char GSM_CME_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CME ERROR:";
  29. static const char GSM_CMS_ERROR[] TINY_GSM_PROGMEM = GSM_NL "+CMS ERROR:";
  30. #endif
  31. enum RegStatus {
  32. REG_NO_RESULT = -1,
  33. REG_UNREGISTERED = 0,
  34. REG_SEARCHING = 2,
  35. REG_DENIED = 3,
  36. REG_OK_HOME = 1,
  37. REG_OK_ROAMING = 5,
  38. REG_UNKNOWN = 4,
  39. };
  40. class TinyGsmUBLOX : public TinyGsmModem<TinyGsmUBLOX>,
  41. public TinyGsmGPRS<TinyGsmUBLOX>,
  42. public TinyGsmTCP<TinyGsmUBLOX, TINY_GSM_MUX_COUNT>,
  43. public TinyGsmSSL<TinyGsmUBLOX>,
  44. public TinyGsmCalling<TinyGsmUBLOX>,
  45. public TinyGsmSMS<TinyGsmUBLOX>,
  46. public TinyGsmGSMLocation<TinyGsmUBLOX>,
  47. public TinyGsmGPS<TinyGsmUBLOX>,
  48. public TinyGsmTime<TinyGsmUBLOX>,
  49. public TinyGsmBattery<TinyGsmUBLOX> {
  50. friend class TinyGsmModem<TinyGsmUBLOX>;
  51. friend class TinyGsmGPRS<TinyGsmUBLOX>;
  52. friend class TinyGsmTCP<TinyGsmUBLOX, TINY_GSM_MUX_COUNT>;
  53. friend class TinyGsmSSL<TinyGsmUBLOX>;
  54. friend class TinyGsmCalling<TinyGsmUBLOX>;
  55. friend class TinyGsmSMS<TinyGsmUBLOX>;
  56. friend class TinyGsmGSMLocation<TinyGsmUBLOX>;
  57. friend class TinyGsmGPS<TinyGsmUBLOX>;
  58. friend class TinyGsmTime<TinyGsmUBLOX>;
  59. friend class TinyGsmBattery<TinyGsmUBLOX>;
  60. /*
  61. * Inner Client
  62. */
  63. public:
  64. class GsmClientUBLOX : public GsmClient {
  65. friend class TinyGsmUBLOX;
  66. public:
  67. GsmClientUBLOX() {}
  68. explicit GsmClientUBLOX(TinyGsmUBLOX& modem, uint8_t mux = 0) {
  69. init(&modem, mux);
  70. }
  71. bool init(TinyGsmUBLOX* modem, uint8_t mux = 0) {
  72. this->at = modem;
  73. sock_available = 0;
  74. prev_check = 0;
  75. sock_connected = false;
  76. got_data = false;
  77. if (mux < TINY_GSM_MUX_COUNT) {
  78. this->mux = mux;
  79. } else {
  80. this->mux = (mux % TINY_GSM_MUX_COUNT);
  81. }
  82. at->sockets[this->mux] = this;
  83. return true;
  84. }
  85. public:
  86. virtual int connect(const char* host, uint16_t port, int timeout_s) {
  87. // stop(); // DON'T stop!
  88. TINY_GSM_YIELD();
  89. rx.clear();
  90. uint8_t oldMux = mux;
  91. sock_connected = at->modemConnect(host, port, &mux, false, timeout_s);
  92. if (mux != oldMux) {
  93. DBG("WARNING: Mux number changed from", oldMux, "to", mux);
  94. at->sockets[oldMux] = NULL;
  95. }
  96. at->sockets[mux] = this;
  97. at->maintain();
  98. return sock_connected;
  99. }
  100. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  101. void stop(uint32_t maxWaitMs) {
  102. dumpModemBuffer(maxWaitMs);
  103. at->sendAT(GF("+USOCL="), mux);
  104. at->waitResponse(); // should return within 1s
  105. sock_connected = false;
  106. }
  107. void stop() override {
  108. stop(15000L);
  109. }
  110. /*
  111. * Extended API
  112. */
  113. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  114. };
  115. /*
  116. * Inner Secure Client
  117. */
  118. public:
  119. class GsmClientSecureUBLOX : public GsmClientUBLOX {
  120. public:
  121. GsmClientSecureUBLOX() {}
  122. explicit GsmClientSecureUBLOX(TinyGsmUBLOX& modem, uint8_t mux = 0)
  123. : GsmClientUBLOX(modem, mux) {}
  124. public:
  125. int connect(const char* host, uint16_t port, int timeout_s) override {
  126. // stop(); // DON'T stop!
  127. TINY_GSM_YIELD();
  128. rx.clear();
  129. uint8_t oldMux = mux;
  130. sock_connected = at->modemConnect(host, port, &mux, true, timeout_s);
  131. if (mux != oldMux) {
  132. DBG("WARNING: Mux number changed from", oldMux, "to", mux);
  133. at->sockets[oldMux] = NULL;
  134. }
  135. at->sockets[mux] = this;
  136. at->maintain();
  137. return sock_connected;
  138. }
  139. TINY_GSM_CLIENT_CONNECT_OVERRIDES
  140. };
  141. /*
  142. * Constructor
  143. */
  144. public:
  145. explicit TinyGsmUBLOX(Stream& stream) : stream(stream) {
  146. memset(sockets, 0, sizeof(sockets));
  147. }
  148. /*
  149. * Basic functions
  150. */
  151. protected:
  152. bool initImpl(const char* pin = NULL) {
  153. DBG(GF("### TinyGSM Version:"), TINYGSM_VERSION);
  154. DBG(GF("### TinyGSM Compiled Module: TinyGsmClientUBLOX"));
  155. if (!testAT()) { return false; }
  156. sendAT(GF("E0")); // Echo Off
  157. if (waitResponse() != 1) { return false; }
  158. #ifdef TINY_GSM_DEBUG
  159. sendAT(GF("+CMEE=2")); // turn on verbose error codes
  160. #else
  161. sendAT(GF("+CMEE=0")); // turn off error codes
  162. #endif
  163. waitResponse();
  164. DBG(GF("### Modem:"), getModemName());
  165. // Enable automatic time zome update
  166. sendAT(GF("+CTZU=1"));
  167. waitResponse(10000L);
  168. // Ignore the response, in case the network doesn't support it.
  169. // if (waitResponse(10000L) != 1) { return false; }
  170. SimStatus ret = getSimStatus();
  171. // if the sim isn't ready and a pin has been provided, try to unlock the sim
  172. if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
  173. simUnlock(pin);
  174. return (getSimStatus() == SIM_READY);
  175. } else {
  176. // if the sim is ready, or it's locked but no pin has been provided,
  177. // return true
  178. return (ret == SIM_READY || ret == SIM_LOCKED);
  179. }
  180. }
  181. // only difference in implementation is the warning on the wrong type
  182. String getModemNameImpl() {
  183. sendAT(GF("+CGMI"));
  184. String res1;
  185. if (waitResponse(1000L, res1) != 1) { return "u-blox Cellular Modem"; }
  186. res1.replace(GSM_NL "OK" GSM_NL, "");
  187. res1.trim();
  188. sendAT(GF("+GMM"));
  189. String res2;
  190. if (waitResponse(1000L, res2) != 1) { return "u-blox Cellular Modem"; }
  191. res2.replace(GSM_NL "OK" GSM_NL, "");
  192. res2.trim();
  193. String name = res1 + String(' ') + res2;
  194. if (name.startsWith("u-blox SARA-R4") ||
  195. name.startsWith("u-blox SARA-N4")) {
  196. DBG("### WARNING: You are using the wrong TinyGSM modem!");
  197. } else if (name.startsWith("u-blox SARA-N2")) {
  198. DBG("### SARA N2 NB-IoT modems not supported!");
  199. }
  200. return name;
  201. }
  202. bool factoryDefaultImpl() {
  203. sendAT(GF("+UFACTORY=0,1")); // No factory restore, erase NVM
  204. waitResponse();
  205. return setPhoneFunctionality(16); // Reset
  206. }
  207. /*
  208. * Power functions
  209. */
  210. protected:
  211. bool restartImpl(const char* pin = NULL) {
  212. if (!testAT()) { return false; }
  213. if (!setPhoneFunctionality(16)) { return false; }
  214. delay(3000); // TODO(?): Verify delay timing here
  215. return init(pin);
  216. }
  217. bool powerOffImpl() {
  218. sendAT(GF("+CPWROFF"));
  219. return waitResponse(40000L) == 1;
  220. }
  221. bool sleepEnableImpl(bool enable = true) TINY_GSM_ATTR_NOT_AVAILABLE;
  222. bool setPhoneFunctionalityImpl(uint8_t fun, bool reset = false) {
  223. sendAT(GF("+CFUN="), fun, reset ? ",1" : "");
  224. return waitResponse(10000L) == 1;
  225. }
  226. /*
  227. * Generic network functions
  228. */
  229. public:
  230. RegStatus getRegistrationStatus() {
  231. return (RegStatus)getRegistrationStatusXREG("CGREG");
  232. }
  233. bool setRadioAccessTecnology(int selected, int preferred) {
  234. // selected:
  235. // 0: GSM / GPRS / eGPRS (single mode)
  236. // 1: GSM / UMTS (dual mode)
  237. // 2: UMTS (single mode)
  238. // 3: LTE (single mode)
  239. // 4: GSM / UMTS / LTE (tri mode)
  240. // 5: GSM / LTE (dual mode)
  241. // 6: UMTS / LTE (dual mode)
  242. // preferred:
  243. // 0: GSM / GPRS / eGPRS
  244. // 2: UTRAN
  245. // 3: LTE
  246. sendAT(GF("+URAT="), selected, GF(","), preferred);
  247. if (waitResponse() != 1) { return false; }
  248. return true;
  249. }
  250. bool getCurrentRadioAccessTecnology(int&) {
  251. // @TODO
  252. return false;
  253. }
  254. protected:
  255. bool isNetworkConnectedImpl() {
  256. RegStatus s = getRegistrationStatus();
  257. if (s == REG_OK_HOME || s == REG_OK_ROAMING)
  258. return true;
  259. else if (s == REG_UNKNOWN) // for some reason, it can hang at unknown..
  260. return isGprsConnected();
  261. else
  262. return false;
  263. }
  264. String getLocalIPImpl() {
  265. sendAT(GF("+UPSND=0,0"));
  266. if (waitResponse(GF(GSM_NL "+UPSND:")) != 1) { return ""; }
  267. streamSkipUntil(','); // Skip PSD profile
  268. streamSkipUntil('\"'); // Skip request type
  269. String res = stream.readStringUntil('\"');
  270. if (waitResponse() != 1) { return ""; }
  271. return res;
  272. }
  273. /*
  274. * GPRS functions
  275. */
  276. protected:
  277. bool gprsConnectImpl(const char* apn, const char* user = NULL,
  278. const char* pwd = NULL) {
  279. // gprsDisconnect();
  280. sendAT(GF("+CGATT=1")); // attach to GPRS
  281. if (waitResponse(360000L) != 1) { return false; }
  282. // Setting up the PSD profile/PDP context with the UPSD commands sets up an
  283. // "internal" PDP context, i.e. a data connection using the internal IP
  284. // stack and related AT commands for sockets.
  285. // Packet switched data configuration
  286. // AT+UPSD=<profile_id>,<param_tag>,<param_val>
  287. // profile_id = 0 - PSD profile identifier, in range 0-6 (NOT PDP context)
  288. // param_tag = 1: APN
  289. // param_tag = 2: username
  290. // param_tag = 3: password
  291. // param_tag = 7: IP address Note: IP address set as "0.0.0.0" means
  292. // dynamic IP address assigned during PDP context activation
  293. sendAT(GF("+UPSD=0,1,\""), apn, '"'); // Set APN for PSD profile 0
  294. waitResponse();
  295. if (user && strlen(user) > 0) {
  296. sendAT(GF("+UPSD=0,2,\""), user, '"'); // Set user for PSD profile 0
  297. waitResponse();
  298. }
  299. if (pwd && strlen(pwd) > 0) {
  300. sendAT(GF("+UPSD=0,3,\""), pwd, '"'); // Set password for PSD profile 0
  301. waitResponse();
  302. }
  303. sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP on PSD profile 0
  304. waitResponse();
  305. // Packet switched data action
  306. // AT+UPSDA=<profile_id>,<action>
  307. // profile_id = 0: PSD profile identifier, in range 0-6 (NOT PDP context)
  308. // action = 3: activate; it activates a PDP context with the specified
  309. // profile, using the current parameters
  310. sendAT(GF(
  311. "+UPSDA=0,3")); // Activate the PDP context associated with profile 0
  312. if (waitResponse(360000L) != 1) { // Should return ok
  313. return false;
  314. }
  315. // Packet switched network-assigned data - Returns the current (dynamic)
  316. // network-assigned or network-negotiated value of the specified parameter
  317. // for the active PDP context associated with the specified PSD profile.
  318. // AT+UPSND=<profile_id>,<param_tag>
  319. // profile_id = 0: PSD profile identifier, in range 0-6 (NOT PDP context)
  320. // param_tag = 8: PSD profile status: if the profile is active the return
  321. // value is 1, 0 otherwise
  322. sendAT(GF("+UPSND=0,8")); // Check if PSD profile 0 is now active
  323. int8_t res = waitResponse(GF(",8,1"), GF(",8,0"));
  324. waitResponse(); // Should return another OK
  325. if (res == 1) {
  326. return true; // It's now active
  327. } else if (res == 2) { // If it's not active yet, wait for the +UUPSDA URC
  328. if (waitResponse(180000L, GF("+UUPSDA: 0")) != 1) { // 0=successful
  329. return false;
  330. }
  331. streamSkipUntil('\n'); // Ignore the IP address, if returned
  332. } else {
  333. return false;
  334. }
  335. return true;
  336. }
  337. bool gprsDisconnectImpl() {
  338. sendAT(GF(
  339. "+UPSDA=0,4")); // Deactivate the PDP context associated with profile 0
  340. if (waitResponse(360000L) != 1) { return false; }
  341. sendAT(GF("+CGATT=0")); // detach from GPRS
  342. if (waitResponse(360000L) != 1) { return false; }
  343. return true;
  344. }
  345. /*
  346. * SIM card functions
  347. */
  348. protected:
  349. // This uses "CGSN" instead of "GSN"
  350. String getIMEIImpl() {
  351. sendAT(GF("+CGSN"));
  352. if (waitResponse(GF(GSM_NL)) != 1) { return ""; }
  353. String res = stream.readStringUntil('\n');
  354. waitResponse();
  355. res.trim();
  356. return res;
  357. }
  358. /*
  359. * Phone Call functions
  360. */
  361. protected:
  362. // Can follow all of the phone call functions from the template
  363. /*
  364. * Messaging functions
  365. */
  366. protected:
  367. // Can follow all template functions
  368. /*
  369. * GSM/GPS/GNSS/GLONASS Location functions
  370. * NOTE: u-blox modules use the same function to get location data from both
  371. * GSM tower triangulation and from dedicated GPS/GNSS/GLONASS receivers. The
  372. * only difference in which sensor the data is requested from. If a GNSS
  373. * location is requested from a modem without a GNSS receiver installed on the
  374. * I2C port, the GSM-based "Cell Locate" location will be returned instead.
  375. */
  376. protected:
  377. bool enableGPSImpl() {
  378. // AT+UGPS=<mode>[,<aid_mode>[,<GNSS_systems>]]
  379. // <mode> - 0: GNSS receiver powered off, 1: on
  380. // <aid_mode> - 0: no aiding (default)
  381. // <GNSS_systems> - 3: GPS + SBAS (default)
  382. sendAT(GF("+UGPS=1,0,3"));
  383. if (waitResponse(10000L, GF(GSM_NL "+UGPS:")) != 1) { return false; }
  384. return waitResponse(10000L) == 1;
  385. }
  386. bool disableGPSImpl() {
  387. sendAT(GF("+UGPS=0"));
  388. if (waitResponse(10000L, GF(GSM_NL "+UGPS:")) != 1) { return false; }
  389. return waitResponse(10000L) == 1;
  390. }
  391. String inline getUbloxLocationRaw(int8_t sensor) {
  392. // AT+ULOC=<mode>,<sensor>,<response_type>,<timeout>,<accuracy>
  393. // <mode> - 2: single shot position
  394. // <sensor> - 0: use the last fix in the internal database and stop the GNSS
  395. // receiver
  396. // - 1: use the GNSS receiver for localization
  397. // - 2: use cellular CellLocate location information
  398. // - 3: ?? use the combined GNSS receiver and CellLocate service
  399. // information ?? - Docs show using sensor 3 and it's
  400. // documented for the +UTIME command but not for +ULOC
  401. // <response_type> - 0: standard (single-hypothesis) response
  402. // <timeout> - Timeout period in seconds
  403. // <accuracy> - Target accuracy in meters (1 - 999999)
  404. sendAT(GF("+ULOC=2,"), sensor, GF(",0,120,1"));
  405. // wait for first "OK"
  406. if (waitResponse(10000L) != 1) { return ""; }
  407. // wait for the final result - wait full timeout time
  408. if (waitResponse(120000L, GF(GSM_NL "+UULOC:")) != 1) { return ""; }
  409. String res = stream.readStringUntil('\n');
  410. waitResponse();
  411. res.trim();
  412. return res;
  413. }
  414. String getGsmLocationRawImpl() {
  415. return getUbloxLocationRaw(2);
  416. }
  417. String getGPSrawImpl() {
  418. return getUbloxLocationRaw(1);
  419. }
  420. inline bool getUbloxLocation(int8_t sensor, float* lat, float* lon,
  421. float* speed = 0, float* alt = 0, int* vsat = 0,
  422. int* usat = 0, float* accuracy = 0,
  423. int* year = 0, int* month = 0, int* day = 0,
  424. int* hour = 0, int* minute = 0,
  425. int* second = 0) {
  426. // AT+ULOC=<mode>,<sensor>,<response_type>,<timeout>,<accuracy>
  427. // <mode> - 2: single shot position
  428. // <sensor> - 2: use cellular CellLocate location information
  429. // - 0: use the last fix in the internal database and stop the GNSS
  430. // receiver
  431. // - 1: use the GNSS receiver for localization
  432. // - 3: ?? use the combined GNSS receiver and CellLocate service
  433. // information ?? - Docs show using sensor 3 and it's documented
  434. // for the +UTIME command but not for +ULOC
  435. // <response_type> - 0: standard (single-hypothesis) response
  436. // <timeout> - Timeout period in seconds
  437. // <accuracy> - Target accuracy in meters (1 - 999999)
  438. sendAT(GF("+ULOC=2,"), sensor, GF(",0,120,1"));
  439. // wait for first "OK"
  440. if (waitResponse(10000L) != 1) { return false; }
  441. // wait for the final result - wait full timeout time
  442. if (waitResponse(120000L, GF(GSM_NL "+UULOC: ")) != 1) { return false; }
  443. // +UULOC: <date>, <time>, <lat>, <long>, <alt>, <uncertainty>, <speed>,
  444. // <direction>, <vertical_acc>, <sensor_used>, <SV_used>, <antenna_status>,
  445. // <jamming_status>
  446. // init variables
  447. float ilat = 0;
  448. float ilon = 0;
  449. float ispeed = 0;
  450. float ialt = 0;
  451. int iusat = 0;
  452. float iaccuracy = 0;
  453. int iyear = 0;
  454. int imonth = 0;
  455. int iday = 0;
  456. int ihour = 0;
  457. int imin = 0;
  458. float secondWithSS = 0;
  459. // Date & Time
  460. iday = streamGetIntBefore('/'); // Two digit day
  461. imonth = streamGetIntBefore('/'); // Two digit month
  462. iyear = streamGetIntBefore(','); // Four digit year
  463. ihour = streamGetIntBefore(':'); // Two digit hour
  464. imin = streamGetIntBefore(':'); // Two digit minute
  465. secondWithSS = streamGetFloatBefore(','); // 6 digit second with subseconds
  466. ilat = streamGetFloatBefore(','); // Estimated latitude, in degrees
  467. ilon = streamGetFloatBefore(','); // Estimated longitude, in degrees
  468. ialt = streamGetFloatBefore(
  469. ','); // Estimated altitude, in meters - only forGNSS
  470. // positioning, 0 in case of CellLocate
  471. if (ialt != 0) { // values not returned for CellLocate
  472. iaccuracy =
  473. streamGetFloatBefore(','); // Maximum possible error, in meters
  474. ispeed = streamGetFloatBefore(','); // Speed over ground m/s3
  475. streamSkipUntil(','); // Course over ground in degree (0 deg - 360 deg)
  476. streamSkipUntil(','); // Vertical accuracy, in meters
  477. streamSkipUntil(','); // Sensor used for the position calculation
  478. iusat = streamGetIntBefore(','); // Number of satellite used
  479. streamSkipUntil(','); // Antenna status
  480. streamSkipUntil('\n'); // Jamming status
  481. } else {
  482. iaccuracy =
  483. streamGetFloatBefore('\n'); // Maximum possible error, in meters
  484. }
  485. // Set pointers
  486. if (lat != NULL) *lat = ilat;
  487. if (lon != NULL) *lon = ilon;
  488. if (speed != NULL) *speed = ispeed;
  489. if (alt != NULL) *alt = ialt;
  490. if (vsat != NULL) *vsat = 0; // Number of satellites viewed not reported;
  491. if (usat != NULL) *usat = iusat;
  492. if (accuracy != NULL) *accuracy = iaccuracy;
  493. if (iyear < 2000) iyear += 2000;
  494. if (year != NULL) *year = iyear;
  495. if (month != NULL) *month = imonth;
  496. if (day != NULL) *day = iday;
  497. if (hour != NULL) *hour = ihour;
  498. if (minute != NULL) *minute = imin;
  499. if (second != NULL) *second = static_cast<int>(secondWithSS);
  500. // final ok
  501. waitResponse();
  502. return true;
  503. }
  504. bool getGsmLocationImpl(float* lat, float* lon, float* accuracy = 0,
  505. int* year = 0, int* month = 0, int* day = 0,
  506. int* hour = 0, int* minute = 0, int* second = 0) {
  507. return getUbloxLocation(2, lat, lon, 0, 0, 0, 0, accuracy, year, month, day,
  508. hour, minute, second);
  509. }
  510. bool getGPSImpl(float* lat, float* lon, float* speed = 0, float* alt = 0,
  511. int* vsat = 0, int* usat = 0, float* accuracy = 0,
  512. int* year = 0, int* month = 0, int* day = 0, int* hour = 0,
  513. int* minute = 0, int* second = 0) {
  514. return getUbloxLocation(1, lat, lon, speed, alt, vsat, usat, accuracy, year,
  515. month, day, hour, minute, second);
  516. }
  517. /*
  518. * Time functions
  519. */
  520. protected:
  521. // Can follow the standard CCLK function in the template
  522. /*
  523. * Battery functions
  524. */
  525. protected:
  526. uint16_t getBattVoltageImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
  527. int8_t getBattPercentImpl() {
  528. sendAT(GF("+CIND?"));
  529. if (waitResponse(GF(GSM_NL "+CIND:")) != 1) { return 0; }
  530. int8_t res = streamGetIntBefore(',');
  531. int8_t percent = res * 20; // return is 0-5
  532. // Wait for final OK
  533. waitResponse();
  534. return percent;
  535. }
  536. uint8_t getBattChargeStateImpl() TINY_GSM_ATTR_NOT_AVAILABLE;
  537. bool getBattStatsImpl(uint8_t& chargeState, int8_t& percent,
  538. uint16_t& milliVolts) {
  539. chargeState = 0;
  540. percent = getBattPercent();
  541. milliVolts = 0;
  542. return true;
  543. }
  544. /*
  545. * Temperature functions
  546. */
  547. // This would only available for a small number of modules in this group
  548. // (TOBY-L)
  549. float getTemperatureImpl() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  550. /*
  551. * Client related functions
  552. */
  553. protected:
  554. bool modemConnect(const char* host, uint16_t port, uint8_t* mux,
  555. bool ssl = false, int timeout_s = 120) {
  556. uint32_t timeout_ms = ((uint32_t)timeout_s) * 1000;
  557. uint32_t startMillis = millis();
  558. // create a socket
  559. sendAT(GF("+USOCR=6"));
  560. // reply is +USOCR: ## of socket created
  561. if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) { return false; }
  562. *mux = streamGetIntBefore('\n');
  563. waitResponse();
  564. if (ssl) {
  565. sendAT(GF("+USOSEC="), *mux, ",1");
  566. waitResponse();
  567. }
  568. // Enable NODELAY
  569. // AT+USOSO=<socket>,<level>,<opt_name>,<opt_val>[,<opt_val2>]
  570. // <level> - 0 for IP, 6 for TCP, 65535 for socket level options
  571. // <opt_name> TCP/1 = no delay (do not delay send to coalesce packets)
  572. // NOTE: Enabling this may increase data plan usage
  573. // sendAT(GF("+USOSO="), *mux, GF(",6,1,1"));
  574. // waitResponse();
  575. // Enable KEEPALIVE, 30 sec
  576. // sendAT(GF("+USOSO="), *mux, GF(",6,2,30000"));
  577. // waitResponse();
  578. // connect on the allocated socket
  579. sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port);
  580. int8_t rsp = waitResponse(timeout_ms - (millis() - startMillis));
  581. return (1 == rsp);
  582. }
  583. int16_t modemSend(const void* buff, size_t len, uint8_t mux) {
  584. sendAT(GF("+USOWR="), mux, ',', (uint16_t)len);
  585. if (waitResponse(GF("@")) != 1) { return 0; }
  586. // 50ms delay, see AT manual section 25.10.4
  587. delay(50);
  588. stream.write(reinterpret_cast<const uint8_t*>(buff), len);
  589. stream.flush();
  590. if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) { return 0; }
  591. streamSkipUntil(','); // Skip mux
  592. int16_t sent = streamGetIntBefore('\n');
  593. waitResponse(); // sends back OK after the confirmation of number sent
  594. return sent;
  595. }
  596. size_t modemRead(size_t size, uint8_t mux) {
  597. if (!sockets[mux]) return 0;
  598. sendAT(GF("+USORD="), mux, ',', (uint16_t)size);
  599. if (waitResponse(GF(GSM_NL "+USORD:")) != 1) { return 0; }
  600. streamSkipUntil(','); // Skip mux
  601. int16_t len = streamGetIntBefore(',');
  602. streamSkipUntil('\"');
  603. for (int i = 0; i < len; i++) { moveCharFromStreamToFifo(mux); }
  604. streamSkipUntil('\"');
  605. waitResponse();
  606. // DBG("### READ:", len, "from", mux);
  607. sockets[mux]->sock_available = modemGetAvailable(mux);
  608. return len;
  609. }
  610. size_t modemGetAvailable(uint8_t mux) {
  611. if (!sockets[mux]) return 0;
  612. // NOTE: Querying a closed socket gives an error "operation not allowed"
  613. sendAT(GF("+USORD="), mux, ",0");
  614. size_t result = 0;
  615. uint8_t res = waitResponse(GF(GSM_NL "+USORD:"));
  616. // Will give error "operation not allowed" when attempting to read a socket
  617. // that you have already told to close
  618. if (res == 1) {
  619. streamSkipUntil(','); // Skip mux
  620. result = streamGetIntBefore('\n');
  621. // if (result) DBG("### DATA AVAILABLE:", result, "on", mux);
  622. waitResponse();
  623. }
  624. if (!result) { sockets[mux]->sock_connected = modemGetConnected(mux); }
  625. // DBG("### AvailablE:", result, "on", mux);
  626. return result;
  627. }
  628. bool modemGetConnected(uint8_t mux) {
  629. // NOTE: Querying a closed socket gives an error "operation not allowed"
  630. sendAT(GF("+USOCTL="), mux, ",10");
  631. uint8_t res = waitResponse(GF(GSM_NL "+USOCTL:"));
  632. if (res != 1) { return false; }
  633. streamSkipUntil(','); // Skip mux
  634. streamSkipUntil(','); // Skip type
  635. int8_t result = streamGetIntBefore('\n');
  636. // 0: the socket is in INACTIVE status (it corresponds to CLOSED status
  637. // defined in RFC793 "TCP Protocol Specification" [112])
  638. // 1: the socket is in LISTEN status
  639. // 2: the socket is in SYN_SENT status
  640. // 3: the socket is in SYN_RCVD status
  641. // 4: the socket is in ESTABILISHED status
  642. // 5: the socket is in FIN_WAIT_1 status
  643. // 6: the socket is in FIN_WAIT_2 status
  644. // 7: the sokcet is in CLOSE_WAIT status
  645. // 8: the socket is in CLOSING status
  646. // 9: the socket is in LAST_ACK status
  647. // 10: the socket is in TIME_WAIT status
  648. waitResponse();
  649. return (result != 0);
  650. }
  651. /*
  652. * Utilities
  653. */
  654. public:
  655. // TODO(vshymanskyy): Optimize this!
  656. int8_t waitResponse(uint32_t timeout_ms, String& data,
  657. GsmConstStr r1 = GFP(GSM_OK),
  658. GsmConstStr r2 = GFP(GSM_ERROR),
  659. #if defined TINY_GSM_DEBUG
  660. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  661. GsmConstStr r4 = GFP(GSM_CMS_ERROR),
  662. #else
  663. GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
  664. #endif
  665. GsmConstStr r5 = NULL) {
  666. /*String r1s(r1); r1s.trim();
  667. String r2s(r2); r2s.trim();
  668. String r3s(r3); r3s.trim();
  669. String r4s(r4); r4s.trim();
  670. String r5s(r5); r5s.trim();
  671. DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
  672. data.reserve(64);
  673. uint8_t index = 0;
  674. uint32_t startMillis = millis();
  675. do {
  676. TINY_GSM_YIELD();
  677. while (stream.available() > 0) {
  678. TINY_GSM_YIELD();
  679. int8_t a = stream.read();
  680. if (a <= 0) continue; // Skip 0x00 bytes, just in case
  681. data += static_cast<char>(a);
  682. if (r1 && data.endsWith(r1)) {
  683. index = 1;
  684. goto finish;
  685. } else if (r2 && data.endsWith(r2)) {
  686. index = 2;
  687. goto finish;
  688. } else if (r3 && data.endsWith(r3)) {
  689. #if defined TINY_GSM_DEBUG
  690. if (r3 == GFP(GSM_CME_ERROR)) {
  691. streamSkipUntil('\n'); // Read out the error
  692. }
  693. #endif
  694. index = 3;
  695. goto finish;
  696. } else if (r4 && data.endsWith(r4)) {
  697. index = 4;
  698. goto finish;
  699. } else if (r5 && data.endsWith(r5)) {
  700. index = 5;
  701. goto finish;
  702. } else if (data.endsWith(GF("+UUSORD:"))) {
  703. int8_t mux = streamGetIntBefore(',');
  704. int16_t len = streamGetIntBefore('\n');
  705. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  706. sockets[mux]->got_data = true;
  707. // max size is 1024
  708. if (len >= 0 && len <= 1024) { sockets[mux]->sock_available = len; }
  709. }
  710. data = "";
  711. // DBG("### URC Data Received:", len, "on", mux);
  712. } else if (data.endsWith(GF("+UUSOCL:"))) {
  713. int8_t mux = streamGetIntBefore('\n');
  714. if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
  715. sockets[mux]->sock_connected = false;
  716. }
  717. data = "";
  718. DBG("### URC Sock Closed: ", mux);
  719. }
  720. }
  721. } while (millis() - startMillis < timeout_ms);
  722. finish:
  723. if (!index) {
  724. data.trim();
  725. if (data.length()) { DBG("### Unhandled:", data); }
  726. data = "";
  727. }
  728. // data.replace(GSM_NL, "/");
  729. // DBG('<', index, '>', data);
  730. return index;
  731. }
  732. int8_t waitResponse(uint32_t timeout_ms, GsmConstStr r1 = GFP(GSM_OK),
  733. GsmConstStr r2 = GFP(GSM_ERROR),
  734. #if defined TINY_GSM_DEBUG
  735. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  736. GsmConstStr r4 = GFP(GSM_CMS_ERROR),
  737. #else
  738. GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
  739. #endif
  740. GsmConstStr r5 = NULL) {
  741. String data;
  742. return waitResponse(timeout_ms, data, r1, r2, r3, r4, r5);
  743. }
  744. int8_t waitResponse(GsmConstStr r1 = GFP(GSM_OK),
  745. GsmConstStr r2 = GFP(GSM_ERROR),
  746. #if defined TINY_GSM_DEBUG
  747. GsmConstStr r3 = GFP(GSM_CME_ERROR),
  748. GsmConstStr r4 = GFP(GSM_CMS_ERROR),
  749. #else
  750. GsmConstStr r3 = NULL, GsmConstStr r4 = NULL,
  751. #endif
  752. GsmConstStr r5 = NULL) {
  753. return waitResponse(1000, r1, r2, r3, r4, r5);
  754. }
  755. public:
  756. Stream& stream;
  757. protected:
  758. GsmClientUBLOX* sockets[TINY_GSM_MUX_COUNT];
  759. const char* gsmNL = GSM_NL;
  760. };
  761. #endif // SRC_TINYGSMCLIENTUBLOX_H_