#ifndef TCP_H #define TCP_H #include "../Platforms.h" class TCP; typedef void (*TCPDataCallback)(TCP* tcp, const uint8_t* data, uint16_t len); typedef void (*TCPConnectCallback)(TCP* tcp); typedef void (*TCPDisconnectCallback)(TCP* tcp); #include "../Debug.h" #include "IP.h" #if ESP8266 extern "C" { #include "mem.h" #include "espconn.h" } class TCP { private: static constexpr const char* NAME = "TCP"; espconn* con; esp_tcp tcp; bool connected = false; TCPDataCallback onData = nullptr; TCPConnectCallback onConnect = nullptr; TCPDisconnectCallback onDisconnect = nullptr; void* userData = nullptr; public: TCP() { init(); } /** dtor */ ~TCP() { cleanup(); } /** send data to the other side */ bool send(const uint8_t* data, const size_t dataLen) { const int res = espconn_sent(con, (unsigned char*)data, dataLen); return (res == 0); } /** connect to the given IP and port */ void connect(const IP ip, const Port port) { disconnect(); debugMod2(NAME, "connect(%s, %d)", ip.toString(), port); con->proto.tcp->remote_port = port; con->proto.tcp->local_port = espconn_port(); os_memcpy(con->proto.tcp->remote_ip, ip.getPtr(), 4); espconn_connect(con); connected = true; } /** terminate connection */ void disconnect() { if (!connected) {return;} espconn_disconnect(con); connected = false; } void hold() { if (connected) { espconn_recv_hold(con); } } void unhold() { if (connected) { espconn_recv_unhold(con); } } /** set the callback to call whenever data is received */ void setDataCallback(TCPDataCallback callback) { this->onData = callback; } /** set the callback to call when connection is established */ void setConnectCallback(TCPConnectCallback callback) { this->onConnect = callback; } /** set the callback to call when connection is lost */ void setDisconnectCallback(TCPDisconnectCallback callback) { this->onDisconnect = callback; } /** attach user-data to this object. can bed used within callbacks */ void setUserData(void* ptr) { this->userData = ptr; } /** get previously attached user data */ void* getUserData() const { return this->userData; } private: /** called for incoming data */ static void _onData(void* ptr, char* data, unsigned short len) { TCP* tcp = (TCP*) ((espconn*)ptr)->reverse; if (tcp->onData) {tcp->onData(tcp, (const uint8_t*)data, len);} } /** called when connection is established */ static void _onConnect(void* ptr) { TCP* tcp = (TCP*) ((espconn*)ptr)->reverse; if (tcp->onConnect) {tcp->onConnect(tcp);} tcp->connected = true; } /** called when connection is lost */ static void _onDisconnect(void* ptr) { TCP* tcp = (TCP*) ((espconn*)ptr)->reverse; if (tcp->onDisconnect) {tcp->onDisconnect(tcp);} tcp->connected = false; } /** initialize the UDP "connection" */ void init() { debugMod(NAME, "init()"); // allocate connection-objects con = (espconn*) os_zalloc(sizeof(espconn)); ets_memset( con, 0, sizeof( espconn ) ); // configure con->type = ESPCONN_TCP; con->state = ESPCONN_NONE; //con->proto.tcp = (esp_tcp*) os_zalloc(sizeof(esp_tcp)); con->proto.tcp = &tcp; // attach ourselves for the callback con->reverse = (void*) this; // user-data to refer to this class espconn_regist_recvcb(con, _onData); espconn_regist_connectcb(con, _onConnect); espconn_regist_disconcb(con, _onDisconnect); // espconn_regist_reconcb(con, _onDisconnect); } /** cleanup everything */ void cleanup() { debugMod(NAME, "cleanup()"); espconn_delete(con); //os_free(con->proto.tcp); os_free(con); con = nullptr; } }; #elif ESP32 #include "lwip/err.h" #include "lwip/tcp.h" #include "lwip/ip_addr.h" class TCP { private: static constexpr const char* NAME = "TCP"; struct tcp_pcb* pcb; bool connected = false; TCPDataCallback onData = nullptr; TCPConnectCallback onConnect = nullptr; TCPDisconnectCallback onDisconnect = nullptr; void* userData = nullptr; public: TCP() { init(); } /** dtor */ ~TCP() { cleanup(); } /** send data to the other side */ bool send(const uint8_t* data, const size_t dataLen) { err_t res = tcp_write(pcb, data, dataLen, TCP_WRITE_FLAG_COPY); return (res == ERR_OK); } /** connect to the given IP and port */ void connect(const IP4 ip, const Port port) { debugMod2(NAME, "connect(%s, %d)", ip.toString(), port); disconnect(); tcp_connect(pcb, ip.getPtr(), port, _onConnectCallback); } /** terminate connection */ void disconnect() { if (!connected) {return;} tcp_close(pcb); connected = false; } void hold() { // if (connected) { // espconn_recv_hold(con); // } } void unhold() { // if (connected) { // espconn_recv_unhold(con); // } } /** set the callback to call whenever data is received */ void setDataCallback(TCPDataCallback callback) { this->onData = callback; } /** set the callback to call when connection is established */ void setConnectCallback(TCPConnectCallback callback) { this->onConnect = callback; } /** set the callback to call when connection is lost */ void setDisconnectCallback(TCPDisconnectCallback callback) { this->onDisconnect = callback; } /** attach user-data to this object. can bed used within callbacks */ void setUserData(void* ptr) { this->userData = ptr; } /** get previously attached user data */ void* getUserData() const { return this->userData; } /** inform backend we consumed some data */ void consumed(uint16_t len) { if (len) { tcp_recved(pcb, len); } } private: /** called for incoming data */ static err_t _onData(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err) { TCP* tcp = (TCP*) arg; // if p == nullptr -> disconnect -> inform listener if (!p) { if (tcp->onDisconnect) {tcp->onDisconnect(tcp);} return ERR_OK; } // process pBuf (might be a chain of buffers) do { // pass data to listener if (tcp->onData) {tcp->onData(tcp, (const uint8_t*)p->payload, p->len);} // next pBuf in chain (if any) pbuf* next = p->next; // free current buffer (we consumed it completely) pbuf_free(p); // proceed? p = next; } while (p); // everything fine return ERR_OK; } /** called when connection failed */ static err_t _onConnectCallback(void* arg, struct tcp_pcb* tpcb, err_t err) { TCP* tcp = (TCP*) arg; if (err == ERR_OK) { if (tcp->onConnect) {tcp->onConnect(tcp);} tcp->connected = true; } else { if (tcp->onDisconnect) {tcp->onDisconnect(tcp);} tcp->connected = false; } return ERR_OK; } /** initialize the UDP "connection" */ void init() { debugMod(NAME, "init()"); pcb = tcp_new(); if (!pcb) {debugMod(NAME, "error creating TCP socket");} // user-pointer pcb->callback_arg = (void*) this; tcp_recv(pcb, _onData); } /** cleanup everything */ void cleanup() { debugMod(NAME, "cleanup()"); // TODO } }; #endif #endif // TCP_H