diff --git a/components/constants/include/constants/tcp.h b/components/constants/include/constants/tcp.h new file mode 100644 index 0000000..2714557 --- /dev/null +++ b/components/constants/include/constants/tcp.h @@ -0,0 +1,16 @@ +// +// Created by Johnathon Slightham on 2025-06-01. +// + +#ifndef TCP_H +#define TCP_H + +#define TCP_DEFAULT_LISTEN_BACKLOG 1 + +#define KEEPALIVE_IDLE 7200 +#define KEEPALIVE_INTERVAL 75 +#define KEEPALIVE_COUNT 9 + +#define SLEEP_AFTER_FAIL_MS 5000 + +#endif //TCP_H diff --git a/components/constants/include/constants/wifi.h b/components/constants/include/constants/wifi.h index c710911..5267c4b 100644 --- a/components/constants/include/constants/wifi.h +++ b/components/constants/include/constants/wifi.h @@ -5,8 +5,9 @@ #ifndef WIFI_H #define WIFI_H -#define WIFI_SSID "botchain" -#define SOFTAP_CHANNEL 1 -#define SOFTAP_MAX_CONNECTIONS 10 +// SoftAP Configuration +#define WIFI_SSID "botchain" +#define SOFTAP_CHANNEL 1 +#define SOFTAP_MAX_CONNECTIONS 10 #endif //WIFI_H diff --git a/components/rpc/CMakeLists.txt b/components/rpc/CMakeLists.txt index fd1ad7e..213ab93 100644 --- a/components/rpc/CMakeLists.txt +++ b/components/rpc/CMakeLists.txt @@ -1,3 +1,3 @@ -idf_component_register(SRCS "rpc.cpp" "WifiManager.cpp" "mDNSDiscoveryService.cpp" +idf_component_register(SRCS "rpc.cpp" "WifiManager.cpp" "mDNSDiscoveryService.cpp" "TCPServer" PRIV_REQUIRES driver esp_event nvs_flash esp_netif esp_wifi espressif__mdns constants INCLUDE_DIRS "include") diff --git a/components/rpc/TCPServer.cpp b/components/rpc/TCPServer.cpp index e96ef99..7feb778 100644 --- a/components/rpc/TCPServer.cpp +++ b/components/rpc/TCPServer.cpp @@ -1,54 +1,201 @@ -#include - -#include "esp_system.h" -#include "esp_mac.h" +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_event.h" #include "esp_netif.h" -#include "esp_log.h" -#define HOST_IP_ADDR "192.168.0.196" -#define PORT 3001 -#define TAG "SOCKET" +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include -static const char *payload = "Message from board"; +#include "TCPServer.h" -int tcp_client() { - char rx_buffer[128]; - char host_ip[] = HOST_IP_ADDR; - int addr_family = AF_INET; - int ip_protocol = IPPROTO_IP; +#include - struct sockaddr_in dest_addr; - inet_pton(AF_INET, host_ip, &dest_addr.sin_addr); // Convert ipv4 address to binary - dest_addr.sin_family = AF_INET; - dest_addr.sin_port = htons(PORT); +#include "constants/tcp.h" - int sock = socket(addr_family, SOCK_STREAM, ip_protocol); - if (sock < 0) { - ESP_LOGE(TAG, "Failed to create socket: %d", errno); - return sock; - } +// todo: - add message routing to correct client +// - authenticate (don't just return true from the auth function) - if (0 != connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr))) { - ESP_LOGE(TAG, "Failed to connect: %d", errno); - } +TCPServer::TCPServer(const int port) { + this->m_port = port; + this->m_mutex = xSemaphoreCreateMutex(); + this->m_clients = std::unordered_set(); - ESP_LOGI(TAG, "Connected"); - while(1) { - int err = send(sock, payload, strlen(payload), 0); - if (err < 0) { - ESP_LOGE(TAG, "Error occurred during sending: %d", errno); + this->m_task = nullptr; + this->m_rx_task = nullptr; + this->m_tx_task = nullptr; + + xTaskCreate(tcp_server_task, "tcp_accept_server", 4096, this, 5, &this->m_task); + xTaskCreate(socket_monitor_thread, "tcp_rx", 4096, this, 5, &this->m_rx_task); +} + +TCPServer::~TCPServer() { + vTaskDelete(this->m_task); + vTaskDelete(this->m_rx_task); + vTaskDelete(this->m_tx_task); + vSemaphoreDelete(this->m_mutex); +} + +[[noreturn]] void TCPServer::tcp_server_task(void *args) { + constexpr int keepAlive = 1; + constexpr int keepIdle = KEEPALIVE_IDLE; + constexpr int keepInterval = KEEPALIVE_INTERVAL; + constexpr int keepCount = KEEPALIVE_COUNT; + + auto that = static_cast(args); + + while (true) { + printf("Attempting to start TCP Server on port %d", that->m_port); + + that->m_server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (that->m_server_sock < 0) { + printf("Unable to create TCP socket: errno %d\n", errno); + vTaskDelay(SLEEP_AFTER_FAIL_MS / portTICK_PERIOD_MS); + continue; } - int len = recv(sock, rx_buffer, sizeof(rx_buffer) -1, 0); // blocking call - if (len < 0) { - ESP_LOGE(TAG, "Failed to receive: %d", errno); - } else { - rx_buffer[len] = 0; // temp: Null terminate to treat as a string - ESP_LOGI(TAG, "Received %d bytes", len); - ESP_LOGI(TAG, "%s", rx_buffer); + constexpr int opt = 1; + setsockopt(that->m_server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + printf("Socket created\n"); + + sockaddr_in server_addr = { + .sin_family = AF_INET, + .sin_port = htons(that->m_port), + .sin_addr = { + .s_addr = htonl(INADDR_ANY), + }, + }; + + int err = bind(that->m_server_sock, reinterpret_cast(&server_addr), sizeof(server_addr)); + if (0 != err) { + printf("Socket unable to bind: errno %d\n", errno); + close(that->m_server_sock); + that->m_server_sock = -1; + vTaskDelay(SLEEP_AFTER_FAIL_MS / portTICK_PERIOD_MS); + continue; } - vTaskDelay(1000 / portTICK_PERIOD_MS); + printf("Socket bound to port %d\n", that->m_port); + + err = listen(that->m_server_sock, TCP_DEFAULT_LISTEN_BACKLOG); + if (0 != err) { + printf("Error occurred during TCP listen: errno %d\n", errno); + close(that->m_server_sock); + that->m_server_sock = -1; + vTaskDelay(SLEEP_AFTER_FAIL_MS / portTICK_PERIOD_MS); + continue; + } + + while (is_network_connected()) { + sockaddr_in client_addr{}; + socklen_t addr_len = sizeof(client_addr); + int client_sock = accept(that->m_server_sock, reinterpret_cast(&client_addr), &addr_len); + if (client_sock < 0) { + printf("Unable to accept TCP connection: errno %d\n", errno); + continue; + } + + err = that->authenticate_client(client_sock); + if (0 != err) { + printf("Client failed authentication\n"); + } + + setsockopt(client_sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int)); + setsockopt(client_sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int)); + setsockopt(client_sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int)); + setsockopt(client_sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int)); + + xSemaphoreTake(that->m_mutex, portMAX_DELAY); + that->m_clients.emplace(client_sock); + xSemaphoreGive(that->m_mutex); + } + + close(that->m_server_sock); + vTaskDelay(SLEEP_AFTER_FAIL_MS / portTICK_PERIOD_MS); } -} \ No newline at end of file +} + +[[noreturn]] void TCPServer::socket_monitor_thread(void *args) { + auto that = static_cast(args); + + while (true) { + printf("top of loop\n"); + fd_set readfds; + FD_ZERO(&readfds); + int max_fd = -1; + + xSemaphoreTake(that->m_mutex, portMAX_DELAY); + for (const auto sock : that->m_clients) { + FD_SET(sock, &readfds); + if (sock > max_fd) max_fd = sock; + } + xSemaphoreGive(that->m_mutex); + + timeval timeout = {1, 0}; // 1 second timeout + int ret = select(max_fd + 1, &readfds, nullptr, nullptr, &timeout); + + if (ret > 0) { + xSemaphoreTake(that->m_mutex, portMAX_DELAY); + std::vector to_remove; + for (int sock : that->m_clients) { + if (FD_ISSET(sock, &readfds)) { + // Handle socket + char buffer[512]; + int len = recv(sock, buffer, sizeof(buffer) - 1, 0); // temp: for the null terminator + + if (len < 0) { + printf("Error occurred during receiving: errno %d\n", errno); + } else if (0 == len) { + printf("Connection closed\n"); + close(sock); + to_remove.emplace_back(sock); + } else { + // todo: send to rx queue + buffer[len] = 0; // temp: Null-terminate whatever is received and treat it like a string + printf("TCP Server Received %d bytes: %s\n", len, buffer); + } + } + } + + for (const auto r : to_remove) { + that->m_clients.erase(r); + } + + xSemaphoreGive(that->m_mutex); + } + } +} + +bool TCPServer::is_network_connected() { + esp_netif_ip_info_t ip_info; + esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); + + if (netif != nullptr && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) { + return true; + } + + if (0 != ip_info.ip.addr) { + return true; + } + + netif = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"); + + if (netif != nullptr && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) { + return true; + } + + if (0 != ip_info.ip.addr) { + return true; + } + + return false; +} + +bool TCPServer::authenticate_client(int sock) { + // todo: authentication (wait for a passphrase from the client) + return 0; +} diff --git a/components/rpc/WifiManager.cpp b/components/rpc/WifiManager.cpp index f1ce63d..638943c 100644 --- a/components/rpc/WifiManager.cpp +++ b/components/rpc/WifiManager.cpp @@ -98,7 +98,7 @@ int WifiManager::init_connection() { wifi_config_t wifi_configuration; wifi_configuration = { .sta = { - .ssid = "dlink-C32D", + .ssid = "", .password = "", } }; diff --git a/components/rpc/include/IRPCServer.h b/components/rpc/include/IRPCServer.h index 6a9db7a..9de70a1 100644 --- a/components/rpc/include/IRPCServer.h +++ b/components/rpc/include/IRPCServer.h @@ -7,6 +7,6 @@ class IRPCServer { -} +}; #endif //IRPCSERVER_H diff --git a/components/rpc/include/TCPServer.h b/components/rpc/include/TCPServer.h index 631ff3a..2945f25 100644 --- a/components/rpc/include/TCPServer.h +++ b/components/rpc/include/TCPServer.h @@ -5,15 +5,34 @@ #ifndef TCPSERVER_H #define TCPSERVER_H +#include +#include + +#include "freertos/FreeRTOS.h" + #include "IRPCServer.h" class TCPServer : IRPCServer { public: -TCPServer(); -~TCPServer(); + explicit TCPServer(int port); + ~TCPServer(); private: + bool authenticate_client(int client_sock); -} + static bool is_network_connected(); + [[noreturn]] static void tcp_server_task(void *args); + [[noreturn]] static void socket_monitor_thread(void *args); + + int m_port; + int m_server_sock; + + TaskHandle_t m_task; + TaskHandle_t m_rx_task; + TaskHandle_t m_tx_task; + + SemaphoreHandle_t m_mutex; + std::unordered_set m_clients; +}; #endif //TCPSERVER_H diff --git a/main/main.cpp b/main/main.cpp index b06f086..c2dd00d 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -9,6 +9,7 @@ #include "WifiManager.h" #include "mDNSDiscoveryService.h" +#include "TCPServer.h" extern "C" [[noreturn]] void app_main(void) { nvs_flash_init(); @@ -18,6 +19,10 @@ extern "C" [[noreturn]] void app_main(void) { const auto discovery = std::make_unique(); + vTaskDelay(20000 / portTICK_PERIOD_MS); + + const auto tcp_server = std::make_unique(3001); + printf("Hello world!\n"); for (int i = 0; ; i++) {