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.

343 lines
10 KiB

  1. /**
  2. * @file TinyGsmTCP.tpp
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef SRC_TINYGSMTCP_H_
  9. #define SRC_TINYGSMTCP_H_
  10. #include "TinyGsmCommon.h"
  11. #define TINY_GSM_MODEM_HAS_TCP
  12. #include "TinyGsmFifo.h"
  13. #if !defined(TINY_GSM_RX_BUFFER)
  14. #define TINY_GSM_RX_BUFFER 64
  15. #endif
  16. // Because of the ordering of resolution of overrides in templates, these need
  17. // to be written out every time. This macro is to shorten that.
  18. #define TINY_GSM_CLIENT_CONNECT_OVERRIDES \
  19. int connect(IPAddress ip, uint16_t port, int timeout_s) { \
  20. return connect(TinyGsmStringFromIp(ip).c_str(), port, timeout_s); \
  21. } \
  22. int connect(const char* host, uint16_t port) override { \
  23. return connect(host, port, 75); \
  24. } \
  25. int connect(IPAddress ip, uint16_t port) override { \
  26. return connect(ip, port, 75); \
  27. }
  28. // // For modules that do not store incoming data in any sort of buffer
  29. // #define TINY_GSM_NO_MODEM_BUFFER
  30. // // Data is stored in a buffer, but we can only read from the buffer,
  31. // // not check how much data is stored in it
  32. // #define TINY_GSM_BUFFER_READ_NO_CHECK
  33. // // Data is stored in a buffer and we can both read and check the size
  34. // // of the buffer
  35. // #define TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
  36. template <class modemType, uint8_t muxCount>
  37. class TinyGsmTCP {
  38. public:
  39. /*
  40. * Basic functions
  41. */
  42. void maintain() {
  43. return thisModem().maintainImpl();
  44. }
  45. /*
  46. * CRTP Helper
  47. */
  48. protected:
  49. inline const modemType& thisModem() const {
  50. return static_cast<const modemType&>(*this);
  51. }
  52. inline modemType& thisModem() {
  53. return static_cast<modemType&>(*this);
  54. }
  55. /*
  56. * Inner Client
  57. */
  58. public:
  59. class GsmClient : public Client {
  60. // Make all classes created from the modem template friends
  61. friend class TinyGsmTCP<modemType, muxCount>;
  62. typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
  63. public:
  64. // bool init(modemType* modem, uint8_t);
  65. // int connect(const char* host, uint16_t port, int timeout_s);
  66. // Connect to a IP address given as an IPAddress object by
  67. // converting said IP address to text
  68. // virtual int connect(IPAddress ip,uint16_t port, int timeout_s) {
  69. // return connect(TinyGsmStringFromIp(ip).c_str(), port,
  70. // timeout_s);
  71. // }
  72. // int connect(const char* host, uint16_t port) override {
  73. // return connect(host, port, 75);
  74. // }
  75. // int connect(IPAddress ip,uint16_t port) override {
  76. // return connect(ip, port, 75);
  77. // }
  78. static inline String TinyGsmStringFromIp(IPAddress ip) {
  79. String host;
  80. host.reserve(16);
  81. host += ip[0];
  82. host += ".";
  83. host += ip[1];
  84. host += ".";
  85. host += ip[2];
  86. host += ".";
  87. host += ip[3];
  88. return host;
  89. }
  90. // void stop(uint32_t maxWaitMs);
  91. // void stop() override {
  92. // stop(15000L);
  93. // }
  94. // Writes data out on the client using the modem send functionality
  95. size_t write(const uint8_t* buf, size_t size) override {
  96. TINY_GSM_YIELD();
  97. at->maintain();
  98. return at->modemSend(buf, size, mux);
  99. }
  100. size_t write(uint8_t c) override {
  101. return write(&c, 1);
  102. }
  103. size_t write(const char* str) {
  104. if (str == NULL) return 0;
  105. return write((const uint8_t*)str, strlen(str));
  106. }
  107. int available() override {
  108. TINY_GSM_YIELD();
  109. #if defined TINY_GSM_NO_MODEM_BUFFER
  110. // Returns the number of characters available in the TinyGSM fifo
  111. if (!rx.size() && sock_connected) { at->maintain(); }
  112. return rx.size();
  113. #elif defined TINY_GSM_BUFFER_READ_NO_CHECK
  114. // Returns the combined number of characters available in the TinyGSM
  115. // fifo and the modem chips internal fifo.
  116. if (!rx.size()) { at->maintain(); }
  117. return rx.size() + sock_available;
  118. #elif defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
  119. // Returns the combined number of characters available in the TinyGSM
  120. // fifo and the modem chips internal fifo, doing an extra check-in
  121. // with the modem to see if anything has arrived without a UURC.
  122. if (!rx.size()) {
  123. if (millis() - prev_check > 500) {
  124. got_data = true;
  125. prev_check = millis();
  126. }
  127. at->maintain();
  128. }
  129. return rx.size() + sock_available;
  130. #else
  131. #error Modem client has been incorrectly created
  132. #endif
  133. }
  134. int read(uint8_t* buf, size_t size) override {
  135. TINY_GSM_YIELD();
  136. size_t cnt = 0;
  137. #if defined TINY_GSM_NO_MODEM_BUFFER
  138. // Reads characters out of the TinyGSM fifo, waiting for any URC's
  139. // from the modem for new data if there's nothing in the fifo.
  140. uint32_t _startMillis = millis();
  141. while (cnt < size && millis() - _startMillis < _timeout) {
  142. size_t chunk = TinyGsmMin(size - cnt, rx.size());
  143. if (chunk > 0) {
  144. rx.get(buf, chunk);
  145. buf += chunk;
  146. cnt += chunk;
  147. continue;
  148. } /* TODO: Read directly into user buffer? */
  149. if (!rx.size() && sock_connected) { at->maintain(); }
  150. }
  151. return cnt;
  152. #elif defined TINY_GSM_BUFFER_READ_NO_CHECK
  153. // Reads characters out of the TinyGSM fifo, and from the modem chip's
  154. // internal fifo if avaiable.
  155. at->maintain();
  156. while (cnt < size) {
  157. size_t chunk = TinyGsmMin(size - cnt, rx.size());
  158. if (chunk > 0) {
  159. rx.get(buf, chunk);
  160. buf += chunk;
  161. cnt += chunk;
  162. continue;
  163. } /* TODO: Read directly into user buffer? */
  164. at->maintain();
  165. if (sock_available > 0) {
  166. int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available),
  167. mux);
  168. if (n == 0) break;
  169. } else {
  170. break;
  171. }
  172. }
  173. return cnt;
  174. #elif defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
  175. // Reads characters out of the TinyGSM fifo, and from the modem chips
  176. // internal fifo if avaiable, also double checking with the modem if
  177. // data has arrived without issuing a UURC.
  178. at->maintain();
  179. while (cnt < size) {
  180. size_t chunk = TinyGsmMin(size - cnt, rx.size());
  181. if (chunk > 0) {
  182. rx.get(buf, chunk);
  183. buf += chunk;
  184. cnt += chunk;
  185. continue;
  186. }
  187. // Workaround: Some modules "forget" to notify about data arrival
  188. if (millis() - prev_check > 500) {
  189. got_data = true;
  190. prev_check = millis();
  191. }
  192. // TODO(vshymanskyy): Read directly into user buffer?
  193. at->maintain();
  194. if (sock_available > 0) {
  195. int n = at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available),
  196. mux);
  197. if (n == 0) break;
  198. } else {
  199. break;
  200. }
  201. }
  202. return cnt;
  203. #else
  204. #error Modem client has been incorrectly created
  205. #endif
  206. }
  207. int read() override {
  208. uint8_t c;
  209. if (read(&c, 1) == 1) { return c; }
  210. return -1;
  211. }
  212. // TODO(SRGDamia1): Implement peek
  213. int peek() override {
  214. return -1;
  215. }
  216. void flush() override {
  217. at->stream.flush();
  218. }
  219. uint8_t connected() override {
  220. if (available()) { return true; }
  221. return sock_connected;
  222. }
  223. operator bool() override {
  224. return connected();
  225. }
  226. /*
  227. * Extended API
  228. */
  229. String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
  230. protected:
  231. // Read and dump anything remaining in the modem's internal buffer.
  232. // Using this in the client stop() function.
  233. // The socket will appear open in response to connected() even after it
  234. // closes until all data is read from the buffer.
  235. // Doing it this way allows the external mcu to find and get all of the
  236. // data that it wants from the socket even if it was closed externally.
  237. inline void dumpModemBuffer(uint32_t maxWaitMs) {
  238. #if defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE || \
  239. defined TINY_GSM_BUFFER_READ_NO_CHECK
  240. TINY_GSM_YIELD();
  241. uint32_t startMillis = millis();
  242. while (sock_available > 0 && (millis() - startMillis < maxWaitMs)) {
  243. rx.clear();
  244. DBG(TinyGsmMin((uint16_t)rx.free(), sock_available));
  245. at->modemRead(TinyGsmMin((uint16_t)rx.free(), sock_available), mux);
  246. }
  247. #elif defined TINY_GSM_NO_MODEM_BUFFER
  248. // Do nothing
  249. #else
  250. #error Modem client has been incorrectly created
  251. #endif
  252. }
  253. modemType* at;
  254. uint8_t mux;
  255. uint16_t sock_available;
  256. uint32_t prev_check;
  257. bool sock_connected;
  258. bool got_data;
  259. RxFifo rx;
  260. };
  261. /*
  262. * Basic functions
  263. */
  264. protected:
  265. void maintainImpl() {
  266. #if defined TINY_GSM_BUFFER_READ_AND_CHECK_SIZE
  267. // Keep listening for modem URC's and proactively iterate through
  268. // sockets asking if any data is avaiable
  269. for (int mux = 0; mux < muxCount; mux++) {
  270. GsmClient* sock = thisModem().sockets[mux];
  271. if (sock && sock->got_data) {
  272. sock->got_data = false;
  273. sock->sock_available = thisModem().modemGetAvailable(mux);
  274. }
  275. }
  276. while (thisModem().stream.available()) {
  277. thisModem().waitResponse(15, NULL, NULL);
  278. }
  279. #elif defined TINY_GSM_NO_MODEM_BUFFER || defined TINY_GSM_BUFFER_READ_NO_CHECK
  280. // Just listen for any URC's
  281. thisModem().waitResponse(100, NULL, NULL);
  282. #else
  283. #error Modem client has been incorrectly created
  284. #endif
  285. }
  286. // Yields up to a time-out period and then reads a character from the stream
  287. // into the mux FIFO
  288. // TODO(SRGDamia1): Do we need to wait two _timeout periods for no
  289. // character return? Will wait once in the first "while
  290. // !stream.available()" and then will wait again in the stream.read()
  291. // function.
  292. inline void moveCharFromStreamToFifo(uint8_t mux) {
  293. uint32_t startMillis = millis();
  294. while (!thisModem().stream.available() &&
  295. (millis() - startMillis < thisModem().sockets[mux]->_timeout)) {
  296. TINY_GSM_YIELD();
  297. }
  298. char c = thisModem().stream.read();
  299. thisModem().sockets[mux]->rx.put(c);
  300. }
  301. };
  302. #endif // SRC_TINYGSMTCP_H_