Skip to content
Permalink
dd435bc7a1
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
694 lines (578 sloc) 24.3 KB
/******************************************************************************
* File Name: tcp_client.c
*
* Description: This file contains task and functions related to TCP client
* operation.
*
* Related Document: See README.md
*
*
*******************************************************************************
* $ Copyright 2021-2023 Cypress Semiconductor $
*******************************************************************************/
/* Header file includes. */
#include "cyhal.h"
#include "cybsp.h"
#include "cy_retarget_io.h"
/* FreeRTOS header file. */
#include <FreeRTOS.h>
#include <task.h>
#include <semphr.h>
/* Standard C header file. */
#include <string.h>
/* Cypress secure socket header file. */
#include "cy_secure_sockets.h"
/* Wi-Fi connection manager header files. */
#include "cy_wcm.h"
#include "cy_wcm_error.h"
/* TCP client task header file. */
#include "tcp_client.h"
/* IP address related header files (part of the lwIP TCP/IP stack). */
#include "ip_addr.h"
/* Standard C header files */
#include <inttypes.h>
/*******************************************************************************
* Macros
********************************************************************************/
/* To use the Wi-Fi device in AP interface mode, set this macro as '1' */
#define USE_AP_INTERFACE (0)
#define MAKE_IP_PARAMETERS(a, b, c, d) ((((uint32_t) d) << 24) | \
(((uint32_t) c) << 16) | \
(((uint32_t) b) << 8) |\
((uint32_t) a))
#if(USE_AP_INTERFACE)
#define WIFI_INTERFACE_TYPE CY_WCM_INTERFACE_TYPE_AP
/* SoftAP Credentials: Modify SOFTAP_SSID and SOFTAP_PASSWORD as required */
#define SOFTAP_SSID "MY_SOFT_AP"
#define SOFTAP_PASSWORD "cyw43907"
/* Security type of the SoftAP. See 'cy_wcm_security_t' structure
* in "cy_wcm.h" for more details.
*/
#define SOFTAP_SECURITY_TYPE CY_WCM_SECURITY_WPA2_AES_PSK
#define SOFTAP_IP_ADDRESS_COUNT (2u)
#define SOFTAP_IP_ADDRESS MAKE_IP_PARAMETERS(192, 168, 10, 1)
#define SOFTAP_NETMASK MAKE_IP_PARAMETERS(255, 255, 255, 0)
#define SOFTAP_GATEWAY MAKE_IP_PARAMETERS(192, 168, 10, 1)
#define SOFTAP_RADIO_CHANNEL (1u)
#else
#define WIFI_INTERFACE_TYPE CY_WCM_INTERFACE_TYPE_STA
/* Wi-Fi Credentials: Modify WIFI_SSID, WIFI_PASSWORD, and WIFI_SECURITY_TYPE
* to match your Wi-Fi network credentials.
* Note: Maximum length of the Wi-Fi SSID and password is set to
* CY_WCM_MAX_SSID_LEN and CY_WCM_MAX_PASSPHRASE_LEN as defined in cy_wcm.h file.
*/
#define WIFI_SSID "MY_WIFI_SSID"
#define WIFI_PASSWORD "MY_WIFI_PASSWORD"
/* Security type of the Wi-Fi access point. See 'cy_wcm_security_t' structure
* in "cy_wcm.h" for more details.
*/
#define WIFI_SECURITY_TYPE CY_WCM_SECURITY_WPA2_AES_PSK
/* Maximum number of connection retries to a Wi-Fi network. */
#define MAX_WIFI_CONN_RETRIES (10u)
/* Wi-Fi re-connection time interval in milliseconds */
#define WIFI_CONN_RETRY_INTERVAL_MSEC (1000u)
#endif /* USE_AP_INTERFACE */
/* Maximum number of connection retries to the TCP server. */
#define MAX_TCP_SERVER_CONN_RETRIES (5u)
/* Length of the TCP data packet. */
#define MAX_TCP_DATA_PACKET_LENGTH (20u)
/* TCP keep alive related macros. */
#define TCP_KEEP_ALIVE_IDLE_TIME_MS (10000u)
#define TCP_KEEP_ALIVE_INTERVAL_MS (1000u)
#define TCP_KEEP_ALIVE_RETRY_COUNT (2u)
/* Length of the LED ON/OFF command issued from the TCP server. */
#define TCP_LED_CMD_LEN (1u)
#define LED_ON_CMD '1'
#define LED_OFF_CMD '0'
#define ACK_LED_ON "LED ON ACK"
#define ACK_LED_OFF "LED OFF ACK"
#define MSG_INVALID_CMD "Invalid command"
#define TCP_SERVER_PORT (50007u)
#define ASCII_BACKSPACE (0x08)
#define RTOS_TICK_TO_WAIT (100u)
#define UART_INPUT_TIMEOUT_MS (1u)
#define UART_BUFFER_SIZE (50u)
/*******************************************************************************
* Function Prototypes
********************************************************************************/
cy_rslt_t create_tcp_client_socket();
cy_rslt_t tcp_client_recv_handler(cy_socket_t socket_handle, void *arg);
cy_rslt_t tcp_disconnection_handler(cy_socket_t socket_handle, void *arg);
cy_rslt_t connect_to_tcp_server(cy_socket_sockaddr_t address);
void read_uart_input(uint8_t* input_buffer_ptr);
#if(USE_AP_INTERFACE)
static cy_rslt_t softap_start(void);
#else
static cy_rslt_t connect_to_wifi_ap(void);
#endif /* USE_AP_INTERFACE */
/*******************************************************************************
* Global Variables
********************************************************************************/
/* TCP client socket handle */
cy_socket_t client_handle;
/* Binary semaphore handle to keep track of TCP server connection. */
SemaphoreHandle_t connect_to_server;
/* Holds the IP address obtained for SoftAP using Wi-Fi Connection Manager (WCM). */
cy_wcm_ip_address_t softap_ip_address;
/*******************************************************************************
* Function Name: tcp_client_task
*******************************************************************************
* Summary:
* Task used to establish a connection to a remote TCP server and
* control the LED state (ON/OFF) based on the command received from TCP server.
*
* Parameters:
* void *args : Task parameter defined during task creation (unused).
*
* Return:
* void
*
*******************************************************************************/
void tcp_client_task(void *arg)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
uint8_t uart_input[UART_BUFFER_SIZE] = {0};
cy_wcm_config_t wifi_config = { .interface = WIFI_INTERFACE_TYPE };
/* IP address and TCP port number of the TCP server to which the TCP client
* connects to.
*/
cy_socket_sockaddr_t tcp_server_address =
{
.ip_address.version = CY_SOCKET_IP_VER_V4,
.port = TCP_SERVER_PORT
};
/* Initialize Wi-Fi connection manager. */
result = cy_wcm_init(&wifi_config);
if (result != CY_RSLT_SUCCESS)
{
printf("Wi-Fi Connection Manager initialization failed! Error code: 0x%08"PRIx32"\n", (uint32_t)result);
CY_ASSERT(0);
}
printf("Wi-Fi Connection Manager initialized.\r\n");
#if(USE_AP_INTERFACE)
/* Start the Wi-Fi device as a Soft AP interface. */
result = softap_start();
if (result != CY_RSLT_SUCCESS)
{
printf("Failed to Start Soft AP! Error code: 0x%08"PRIx32"\n", (uint32_t)result);
CY_ASSERT(0);
}
#else
/* Connect to Wi-Fi AP */
result = connect_to_wifi_ap();
if(result!= CY_RSLT_SUCCESS )
{
printf("\n Failed to connect to Wi-Fi AP! Error code: 0x%08"PRIx32"\n", (uint32_t)result);
CY_ASSERT(0);
}
#endif /* USE_AP_INTERFACE */
/* Create a binary semaphore to keep track of TCP server connection. */
connect_to_server = xSemaphoreCreateBinary();
/* Give the semaphore so as to connect to TCP server. */
xSemaphoreGive(connect_to_server);
/* Initialize secure socket library. */
result = cy_socket_init();
if (result != CY_RSLT_SUCCESS)
{
printf("Secure Socket initialization failed!\n");
CY_ASSERT(0);
}
printf("Secure Socket initialized\n");
for(;;)
{
/* Wait till semaphore is acquired so as to connect to a TCP server. */
xSemaphoreTake(connect_to_server, portMAX_DELAY);
printf("Connect to TCP server\n");
printf("Enter the IPv4 address of the TCP Server:\n");
/* Prevent system from entering deep sleep mode
* when receiving data from UART.
*/
cyhal_syspm_lock_deepsleep();
/* Clear the UART input buffer. */
memset(uart_input, 0, UART_BUFFER_SIZE);
/* Read the TCP server's IPv4 address from the user via the
* UART terminal.
*/
read_uart_input(uart_input);
/* Allow system to enter deep sleep mode. */
cyhal_syspm_unlock_deepsleep();
ip4addr_aton((char *)uart_input,
(ip4_addr_t *)&tcp_server_address.ip_address.ip.v4);
/* Connect to the TCP server. If the connection fails, retry
* to connect to the server for MAX_TCP_SERVER_CONN_RETRIES times.
*/
printf("Connecting to TCP Server (IP Address: %s, Port: %d)\n\n",
ip4addr_ntoa((const ip4_addr_t *)&tcp_server_address.ip_address.ip.v4),
TCP_SERVER_PORT);
result = connect_to_tcp_server(tcp_server_address);
if(result != CY_RSLT_SUCCESS)
{
printf("Failed to connect to TCP server.\n");
/* Give the semaphore so as to connect to TCP server. */
xSemaphoreGive(connect_to_server);
}
}
}
#if(!USE_AP_INTERFACE)
/*******************************************************************************
* Function Name: connect_to_wifi_ap()
*******************************************************************************
* Summary:
* Connects to Wi-Fi AP using the user-configured credentials, retries up to a
* configured number of times until the connection succeeds.
*
*******************************************************************************/
cy_rslt_t connect_to_wifi_ap(void)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
uint8_t ssid_len = 0;
uint8_t pwd_len = 0;
/* Variables used by Wi-Fi connection manager.*/
cy_wcm_connect_params_t wifi_conn_param;
cy_wcm_ip_address_t ip_address;
/*Validate the length of SSID and password*/
ssid_len = (uint8_t)strlen(WIFI_SSID);
pwd_len = (uint8_t)strlen(WIFI_PASSWORD);
if(ssid_len == 0 || ssid_len > CY_WCM_MAX_SSID_LEN )
{
printf("SSID - invalid length error \n");
return CY_RSLT_WCM_BAD_SSID_LEN;
}
if(pwd_len == 0 || pwd_len > CY_WCM_MAX_PASSPHRASE_LEN )
{
printf("AP credentials passphrase length error\n");
return CY_RSLT_WCM_BAD_PASSPHRASE_LEN;
}
/* Set the Wi-Fi SSID, password and security type. */
memset(&wifi_conn_param, 0, sizeof(cy_wcm_connect_params_t));
memcpy(wifi_conn_param.ap_credentials.SSID, WIFI_SSID, sizeof(WIFI_SSID));
memcpy(wifi_conn_param.ap_credentials.password, WIFI_PASSWORD, sizeof(WIFI_PASSWORD));
wifi_conn_param.ap_credentials.security = WIFI_SECURITY_TYPE;
printf("Connecting to Wi-Fi Network: %s\n", WIFI_SSID);
/* Join the Wi-Fi AP. */
for(uint32_t conn_retries = 0; conn_retries < MAX_WIFI_CONN_RETRIES; conn_retries++ )
{
result = cy_wcm_connect_ap(&wifi_conn_param, &ip_address);
if(result == CY_RSLT_SUCCESS)
{
printf("Successfully connected to Wi-Fi network '%s'.\n",
wifi_conn_param.ap_credentials.SSID);
printf("IP Address Assigned: %s\n",
ip4addr_ntoa((const ip4_addr_t *)&ip_address.ip.v4));
return result;
}
printf("Connection to Wi-Fi network failed with error code %d."
"Retrying in %d ms...\n", (int)result, WIFI_CONN_RETRY_INTERVAL_MSEC);
vTaskDelay(pdMS_TO_TICKS(WIFI_CONN_RETRY_INTERVAL_MSEC));
}
/* Stop retrying after maximum retry attempts. */
printf("Exceeded maximum Wi-Fi connection attempts\n");
return result;
}
#endif /* USE_AP_INTERFACE */
#if(USE_AP_INTERFACE)
/********************************************************************************
* Function Name: softap_start
********************************************************************************
* Summary:
* This function configures device in AP mode and initializes
* a SoftAP with the given credentials (SOFTAP_SSID, SOFTAP_PASSWORD and
* SOFTAP_SECURITY_TYPE).
*
* Parameters:
* void
*
* Return:
* cy_rslt_t: Returns CY_RSLT_SUCCESS if the Soft AP is started successfully,
* a WCM error code otherwise.
*
*******************************************************************************/
static cy_rslt_t softap_start(void)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
/* Initialize the Wi-Fi device as a Soft AP. */
cy_wcm_ap_credentials_t softap_credentials = {SOFTAP_SSID, SOFTAP_PASSWORD,
SOFTAP_SECURITY_TYPE};
cy_wcm_ip_setting_t softap_ip_info = {
.ip_address = {.version = CY_WCM_IP_VER_V4, .ip.v4 = SOFTAP_IP_ADDRESS},
.gateway = {.version = CY_WCM_IP_VER_V4, .ip.v4 = SOFTAP_GATEWAY},
.netmask = {.version = CY_WCM_IP_VER_V4, .ip.v4 = SOFTAP_NETMASK}};
cy_wcm_ap_config_t softap_config = {softap_credentials, SOFTAP_RADIO_CHANNEL,
softap_ip_info,
NULL};
/* Start the the Wi-Fi device as a Soft AP. */
result = cy_wcm_start_ap(&softap_config);
if(result == CY_RSLT_SUCCESS)
{
printf("Wi-Fi Device configured as Soft AP\n");
printf("Connect TCP client device to the network: SSID: %s Password:%s\n",
SOFTAP_SSID, SOFTAP_PASSWORD);
printf("SofAP IP Address : %s\n\n",
ip4addr_ntoa((const ip4_addr_t *)&softap_ip_info.ip_address.ip.v4));
}
return result;
}
#endif /* USE_AP_INTERFACE */
/*******************************************************************************
* Function Name: create_tcp_client_socket
*******************************************************************************
* Summary:
* Function to create a socket and set the socket options
* to set call back function for handling incoming messages, call back
* function to handle disconnection.
*
*******************************************************************************/
cy_rslt_t create_tcp_client_socket()
{
cy_rslt_t result = CY_RSLT_SUCCESS;
/* TCP keep alive parameters. */
int keep_alive = 1;
uint32_t keep_alive_interval = TCP_KEEP_ALIVE_INTERVAL_MS;
uint32_t keep_alive_count = TCP_KEEP_ALIVE_RETRY_COUNT;
uint32_t keep_alive_idle_time = TCP_KEEP_ALIVE_IDLE_TIME_MS;
/* Variables used to set socket options. */
cy_socket_opt_callback_t tcp_recv_option;
cy_socket_opt_callback_t tcp_disconnect_option;
/* Create a new secure TCP socket. */
result = cy_socket_create(CY_SOCKET_DOMAIN_AF_INET, CY_SOCKET_TYPE_STREAM,
CY_SOCKET_IPPROTO_TCP, &client_handle);
if (result != CY_RSLT_SUCCESS)
{
printf("Failed to create socket!\n");
return result;
}
/* Register the callback function to handle messages received from TCP server. */
tcp_recv_option.callback = tcp_client_recv_handler;
tcp_recv_option.arg = NULL;
result = cy_socket_setsockopt(client_handle, CY_SOCKET_SOL_SOCKET,
CY_SOCKET_SO_RECEIVE_CALLBACK,
&tcp_recv_option, sizeof(cy_socket_opt_callback_t));
if (result != CY_RSLT_SUCCESS)
{
printf("Set socket option: CY_SOCKET_SO_RECEIVE_CALLBACK failed\n");
return result;
}
/* Register the callback function to handle disconnection. */
tcp_disconnect_option.callback = tcp_disconnection_handler;
tcp_disconnect_option.arg = NULL;
result = cy_socket_setsockopt(client_handle, CY_SOCKET_SOL_SOCKET,
CY_SOCKET_SO_DISCONNECT_CALLBACK,
&tcp_disconnect_option, sizeof(cy_socket_opt_callback_t));
if(result != CY_RSLT_SUCCESS)
{
printf("Set socket option: CY_SOCKET_SO_DISCONNECT_CALLBACK failed\n");
}
/* Set the TCP keep alive interval. */
result = cy_socket_setsockopt(client_handle, CY_SOCKET_SOL_TCP,
CY_SOCKET_SO_TCP_KEEPALIVE_INTERVAL,
&keep_alive_interval, sizeof(keep_alive_interval));
if(result != CY_RSLT_SUCCESS)
{
printf("Set socket option: CY_SOCKET_SO_TCP_KEEPALIVE_INTERVAL failed\n");
return result;
}
/* Set the retry count for TCP keep alive packet. */
result = cy_socket_setsockopt(client_handle, CY_SOCKET_SOL_TCP,
CY_SOCKET_SO_TCP_KEEPALIVE_COUNT,
&keep_alive_count, sizeof(keep_alive_count));
if(result != CY_RSLT_SUCCESS)
{
printf("Set socket option: CY_SOCKET_SO_TCP_KEEPALIVE_COUNT failed\n");
return result;
}
/* Set the network idle time before sending the TCP keep alive packet. */
result = cy_socket_setsockopt(client_handle, CY_SOCKET_SOL_TCP,
CY_SOCKET_SO_TCP_KEEPALIVE_IDLE_TIME,
&keep_alive_idle_time, sizeof(keep_alive_idle_time));
if(result != CY_RSLT_SUCCESS)
{
printf("Set socket option: CY_SOCKET_SO_TCP_KEEPALIVE_IDLE_TIME failed\n");
return result;
}
/* Enable TCP keep alive. */
result = cy_socket_setsockopt(client_handle, CY_SOCKET_SOL_SOCKET,
CY_SOCKET_SO_TCP_KEEPALIVE_ENABLE,
&keep_alive, sizeof(keep_alive));
if(result != CY_RSLT_SUCCESS)
{
printf("Set socket option: CY_SOCKET_SO_TCP_KEEPALIVE_ENABLE failed\n");
return result;
}
return result;
}
/*******************************************************************************
* Function Name: connect_to_tcp_server
*******************************************************************************
* Summary:
* Function to connect to TCP server.
*
* Parameters:
* cy_socket_sockaddr_t address: Address of TCP server socket
*
* Return:
* cy_result result: Result of the operation
*
*******************************************************************************/
cy_rslt_t connect_to_tcp_server(cy_socket_sockaddr_t address)
{
cy_rslt_t result = CY_RSLT_MODULE_SECURE_SOCKETS_TIMEOUT;
cy_rslt_t conn_result = CY_RSLT_SUCCESS;
for(uint32_t conn_retries = 0; conn_retries < MAX_TCP_SERVER_CONN_RETRIES; conn_retries++)
{
/* Create a TCP socket */
conn_result = create_tcp_client_socket();
if(conn_result != CY_RSLT_SUCCESS)
{
printf("Socket creation failed!\n");
CY_ASSERT(0);
}
conn_result = cy_socket_connect(client_handle, &address, sizeof(cy_socket_sockaddr_t));
if (conn_result == CY_RSLT_SUCCESS)
{
printf("============================================================\n");
printf("Connected to TCP server\n");
return conn_result;
}
printf("Could not connect to TCP server. Error code: 0x%08"PRIx32"\n", (uint32_t)result);
printf("Trying to reconnect to TCP server... Please check if the server is listening\n");
/* The resources allocated during the socket creation (cy_socket_create)
* should be deleted.
*/
cy_socket_delete(client_handle);
}
/* Stop retrying after maximum retry attempts. */
printf("Exceeded maximum connection attempts to the TCP server\n");
return result;
}
/*******************************************************************************
* Function Name: tcp_client_recv_handler
*******************************************************************************
* Summary:
* Callback function to handle incoming TCP server messages.
*
* Parameters:
* cy_socket_t socket_handle: Connection handle for the TCP client socket
* void *args : Parameter passed on to the function (unused)
*
* Return:
* cy_result result: Result of the operation
*
*******************************************************************************/
cy_rslt_t tcp_client_recv_handler(cy_socket_t socket_handle, void *arg)
{
/* Variable to store number of bytes send to the TCP server. */
uint32_t bytes_sent = 0;
/* Variable to store number of bytes received. */
uint32_t bytes_received = 0;
char message_buffer[MAX_TCP_DATA_PACKET_LENGTH] = {0};
cy_rslt_t result = CY_RSLT_SUCCESS;
printf("============================================================\n");
result = cy_socket_recv(socket_handle, message_buffer, TCP_LED_CMD_LEN,
CY_SOCKET_FLAGS_NONE, &bytes_received);
if(message_buffer[0] == LED_ON_CMD)
{
/* Turn the LED ON. */
cyhal_gpio_write(CYBSP_USER_LED2, CYBSP_LED_STATE_ON);
printf("LED turned ON\n");
sprintf(message_buffer, ACK_LED_ON);
}
else if(message_buffer[0] == LED_OFF_CMD)
{
/* Turn the LED OFF. */
cyhal_gpio_write(CYBSP_USER_LED2, CYBSP_LED_STATE_OFF);
printf("LED turned OFF\n");
sprintf(message_buffer, ACK_LED_OFF);
}
else
{
printf("Invalid command\n");
sprintf(message_buffer, MSG_INVALID_CMD);
}
/* Send acknowledgement to the TCP server in receipt of the message received. */
result = cy_socket_send(socket_handle, message_buffer, strlen(message_buffer),
CY_SOCKET_FLAGS_NONE, &bytes_sent);
if(result == CY_RSLT_SUCCESS)
{
printf("Acknowledgement sent to TCP server\n");
}
return result;
}
/*******************************************************************************
* Function Name: tcp_disconnection_handler
*******************************************************************************
* Summary:
* Callback function to handle TCP socket disconnection event.
*
* Parameters:
* cy_socket_t socket_handle: Connection handle for the TCP client socket
* void *args : Parameter passed on to the function (unused)
*
* Return:
* cy_result result: Result of the operation
*
*******************************************************************************/
cy_rslt_t tcp_disconnection_handler(cy_socket_t socket_handle, void *arg)
{
cy_rslt_t result;
/* Disconnect the TCP client. */
result = cy_socket_disconnect(socket_handle, 0);
/* Free the resources allocated to the socket. */
cy_socket_delete(socket_handle);
printf("Disconnected from the TCP server! \n");
/* Give the semaphore so as to connect to TCP server. */
xSemaphoreGive(connect_to_server);
return result;
}
/*******************************************************************************
* Function Name: read_uart_input
*******************************************************************************
* Summary:
* Function to read user input from UART terminal.
*
* Parameters:
* uint8_t* input_buffer_ptr: Pointer to input buffer
*
* Return:
* void
*
*******************************************************************************/
void read_uart_input(uint8_t* input_buffer_ptr)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
uint8_t *ptr = input_buffer_ptr;
uint32_t numBytes = 0;
do
{
/* Check for data in the UART buffer with zero timeout. */
numBytes = cyhal_uart_readable(&cy_retarget_io_uart_obj);
if(numBytes > 0)
{
result = cyhal_uart_getc(&cy_retarget_io_uart_obj, ptr,
UART_INPUT_TIMEOUT_MS);
if(result == CY_RSLT_SUCCESS)
{
if((*ptr == '\r') || (*ptr == '\n'))
{
printf("\n");
}
else
{
/* Echo the received character */
cyhal_uart_putc(&cy_retarget_io_uart_obj, *ptr);
if (*ptr != '\b')
{
ptr++;
}
else if(ptr != input_buffer_ptr)
{
ptr--;
}
}
}
}
vTaskDelay(RTOS_TICK_TO_WAIT);
} while((*ptr != '\r') && (*ptr != '\n'));
/* Terminate string with NULL character. */
*ptr = '\0';
}
/* [] END OF FILE */