/** * @file tcp_client.c * @brief TCP Client module implementation for transparent transmission with UART3 */ #include "tcp_client.h" #include "config.h" #include "lwip/opt.h" #include "lwip/tcp.h" #include "lwip/sys.h" #include "lwip/sockets.h" #include "FreeRTOS.h" #include "task.h" #include "stream_buffer.h" #include /*--------------------------------------------------------------------------- * Private Variables *---------------------------------------------------------------------------*/ /* Client configuration */ static tcp_client_config_t client_config = { .server_ip = {192, 168, 1, 100}, .server_port = TCP_CLIENT_DEFAULT_PORT, .auto_reconnect = true, .reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS }; /* Client status */ static tcp_client_status_t client_status = { .state = TCP_CLIENT_STATE_IDLE, .rx_bytes = 0, .tx_bytes = 0, .reconnect_count = 0, .errors = 0 }; /* Socket descriptor */ static int client_socket = -1; /* Stream buffers for UART integration */ static StreamBufferHandle_t rx_stream = NULL; /* TCP RX -> UART TX */ static StreamBufferHandle_t tx_stream = NULL; /* UART RX -> TCP TX */ /*--------------------------------------------------------------------------- * Private Functions *---------------------------------------------------------------------------*/ static int tcp_client_send_all(int sock, const uint8_t *data, uint16_t len) { uint16_t total = 0; while (total < len) { int sent = send(sock, data + total, len - total, 0); if (sent > 0) { total += (uint16_t)sent; } else { return -1; } } return (int)total; } /** * @brief Internal connect function */ static int tcp_client_do_connect(void) { struct sockaddr_in server_addr; int ret; if (client_socket >= 0) { /* Already connected */ return 0; } client_status.state = TCP_CLIENT_STATE_CONNECTING; /* Create socket */ client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket < 0) { client_status.state = TCP_CLIENT_STATE_ERROR; client_status.errors++; return -1; } /* Prepare server address */ memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(client_config.server_port); server_addr.sin_addr.s_addr = ((uint32_t)client_config.server_ip[0]) | ((uint32_t)client_config.server_ip[1] << 8) | ((uint32_t)client_config.server_ip[2] << 16) | ((uint32_t)client_config.server_ip[3] << 24); /* Connect to server */ ret = connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (ret < 0) { close(client_socket); client_socket = -1; client_status.state = TCP_CLIENT_STATE_DISCONNECTED; client_status.errors++; return -1; } client_status.state = TCP_CLIENT_STATE_CONNECTED; return 0; } /*--------------------------------------------------------------------------- * Public Functions *---------------------------------------------------------------------------*/ /** * @brief Initialize TCP Client module */ int tcp_client_init(const tcp_client_config_t *config) { if (config != NULL) { memcpy(&client_config, config, sizeof(tcp_client_config_t)); } /* Create stream buffers */ if (rx_stream == NULL) { rx_stream = xStreamBufferCreate(TCP_CLIENT_RX_BUFFER_SIZE, 1); if (rx_stream == NULL) { return -1; } } if (tx_stream == NULL) { tx_stream = xStreamBufferCreate(TCP_CLIENT_TX_BUFFER_SIZE, 1); if (tx_stream == NULL) { return -1; } } client_status.state = TCP_CLIENT_STATE_IDLE; return 0; } /** * @brief Connect to remote server */ int tcp_client_connect(void) { return tcp_client_do_connect(); } /** * @brief Disconnect from server */ int tcp_client_disconnect(void) { if (client_socket >= 0) { close(client_socket); client_socket = -1; } client_status.state = TCP_CLIENT_STATE_DISCONNECTED; return 0; } /** * @brief Send data to server */ int tcp_client_send(const uint8_t *data, uint16_t len) { int sent; if (client_socket < 0) { return -1; } sent = tcp_client_send_all(client_socket, data, len); if (sent > 0) { client_status.tx_bytes += sent; } else if (sent < 0) { /* Connection error */ close(client_socket); client_socket = -1; client_status.state = TCP_CLIENT_STATE_DISCONNECTED; client_status.errors++; } return sent; } /** * @brief Receive data from server */ int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms) { int received; struct timeval tv; if (client_socket < 0) { return -1; } /* Set receive timeout */ tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms % 1000) * 1000; setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); received = recv(client_socket, data, max_len, 0); if (received > 0) { client_status.rx_bytes += received; } else if (received == 0) { /* Connection closed by server */ close(client_socket); client_socket = -1; client_status.state = TCP_CLIENT_STATE_DISCONNECTED; } return received; } /** * @brief Check if connected to server */ bool tcp_client_is_connected(void) { return (client_socket >= 0); } /** * @brief Update server configuration */ int tcp_client_set_server(const uint8_t *ip, uint16_t port) { if (ip == NULL) { return -1; } /* Disconnect if connected */ if (client_socket >= 0) { tcp_client_disconnect(); } memcpy(client_config.server_ip, ip, 4); client_config.server_port = port; return 0; } /** * @brief Get TCP Client status */ void tcp_client_get_status(tcp_client_status_t *status) { if (status != NULL) { memcpy(status, &client_status, sizeof(tcp_client_status_t)); } } /** * @brief Get RX StreamBuffer handle */ void *tcp_client_get_rx_stream(void) { return rx_stream; } /** * @brief Get TX StreamBuffer handle */ void *tcp_client_get_tx_stream(void) { return tx_stream; } /** * @brief TCP Client task */ void tcp_client_task(void *argument) { const device_config_t *cfg; tcp_client_config_t task_cfg; uint8_t rx_buffer[256]; uint8_t tx_buffer[256]; int received; size_t tx_len; fd_set read_fds; struct timeval tv; uint32_t reconnect_timer = 0; (void)argument; /* Initialize client */ task_cfg.server_ip[0] = 192; task_cfg.server_ip[1] = 168; task_cfg.server_ip[2] = 1; task_cfg.server_ip[3] = 100; task_cfg.server_port = TCP_CLIENT_DEFAULT_PORT; task_cfg.auto_reconnect = true; task_cfg.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS; cfg = config_get(); if (cfg != NULL) { memcpy(task_cfg.server_ip, cfg->remote_ip, sizeof(task_cfg.server_ip)); if (cfg->remote_port > 0) { task_cfg.server_port = cfg->remote_port; } if (cfg->reconnect_interval > 0) { task_cfg.reconnect_interval_ms = cfg->reconnect_interval; } } tcp_client_init(&task_cfg); while (1) { /* Handle connection state */ if (client_socket < 0) { /* Not connected - try to reconnect */ if (client_config.auto_reconnect) { if (xTaskGetTickCount() - reconnect_timer >= pdMS_TO_TICKS(client_config.reconnect_interval_ms)) { if (tcp_client_do_connect() == 0) { /* Connected successfully */ client_status.reconnect_count++; } reconnect_timer = xTaskGetTickCount(); } } /* Wait before retry */ vTaskDelay(pdMS_TO_TICKS(100)); continue; } /* Handle data transfer if connected */ /* Check for data from TCP server */ FD_ZERO(&read_fds); FD_SET(client_socket, &read_fds); tv.tv_sec = 0; tv.tv_usec = 10000; /* 10ms timeout */ if (select(client_socket + 1, &read_fds, NULL, NULL, &tv) > 0) { if (FD_ISSET(client_socket, &read_fds)) { received = recv(client_socket, rx_buffer, sizeof(rx_buffer), 0); if (received > 0) { /* Forward to UART via stream buffer */ xStreamBufferSend(rx_stream, rx_buffer, received, pdMS_TO_TICKS(10)); client_status.rx_bytes += received; } else if (received == 0) { /* Connection closed */ close(client_socket); client_socket = -1; client_status.state = TCP_CLIENT_STATE_DISCONNECTED; reconnect_timer = xTaskGetTickCount(); } else { /* Error */ close(client_socket); client_socket = -1; client_status.state = TCP_CLIENT_STATE_DISCONNECTED; client_status.errors++; reconnect_timer = xTaskGetTickCount(); } } } /* Check for data from UART to send to TCP */ tx_len = xStreamBufferReceive(tx_stream, tx_buffer, sizeof(tx_buffer), 0); if (tx_len > 0) { int sent = tcp_client_send_all(client_socket, tx_buffer, (uint16_t)tx_len); if (sent > 0) { client_status.tx_bytes += sent; } else if (sent < 0) { /* Send error */ close(client_socket); client_socket = -1; client_status.state = TCP_CLIENT_STATE_DISCONNECTED; client_status.errors++; reconnect_timer = xTaskGetTickCount(); } } /* Small delay to prevent tight loop */ vTaskDelay(pdMS_TO_TICKS(1)); } }