博客 / 詳情

返回

《ESP32-S3使用指南—IDF版 V1.6》第五十三章 TCPClient實驗

第五十三章 TCPClient實驗

本章作者重點講解lwIP的Socket接口如何配置TCP客户端,並在此基礎上實現收發功能。
本章分為如下幾個部分:
53.1 Socket編程TCPClient連接流程
53.2 硬件設計
53.3 軟件設計
53.4 下載驗證

53.1 Socket編程TCPClient連接流程

在實現TCP協議之前,用户需要按照以下步驟配置結構體sockaddr_in的成員變量,以便建立TCPClient連接:
①:配置ESP32-S3設備連接網絡(必須的,因為WiFi是無線通信,所以需搭建通信橋樑)。
②:將sin_family設置為AF_INET,表示使用IPv4網絡協議。
③:設置sin_port為所需的端口號,例如8080。
④:設置sin_addr.s_addr為遠程IP地址。
⑤:調用函數Socket創建Socket連接。請注意,該函數的第二個參數指定連接類型。SOCK_STREAM表示TCP連接,而SOCK_DGRAM表示UDP連接。
⑥:調用函數connect連接遠程IP地址。
⑦:調用適當的收發函數來接收或發送數據。
通過遵循這些步驟,用户可成功地配置並建立TCPClient連接,以實現數據的發送和接收。

53.2 硬件設計

1.例程功能
本章實驗功能簡介:
本實驗主要通過Socket編程接口實現了一個TCPClient客户端。這個客户端具有以下功能:
①:可以通過按鍵發送TCPClient數據發送至服務器。
②:能夠接收服務器發送的數據。
③:實時將接收到的數據顯示在LCD屏幕上。
通過這個實驗,用户可深入瞭解TCP協議的工作原理,並掌握如何使用Socket編程接口來實現TCP通信。這對於開發基於TCP的網絡應用程序非常有用,例如實時傳輸、文件傳輸等。

2.硬件資源
1)LED燈
LED-IO1
2)XL9555
IIC_INT-IO0(需在P5連接IO0)
IIC_SDA-IO41
IIC_SCL-IO42
3)SPILCD
CS-IO21
SCK-IO12
SDA-IO11
DC-IO40(在P5端口,使用跳線帽將IO_SET和LCD_DC相連)
PWR- IO1_3(XL9555)
RST- IO1_2(XL9555)
4)ESP32-S3內部WiFi

3.原理圖
本章實驗使用的WiFi為ESP32-S3的片上資源,因此並沒有相應的連接原理圖。

53.3 軟件設計

53.3.1 程序流程圖

程序流程圖能幫助我們更好的理解一個工程的功能和實現的過程,對學習和設計工程有很好的主導作用。下面看看本實驗的程序流程圖:

圖53.3.1.1 程序流程圖

53.3.2 程序解析

在本章節中,我們主要關注兩個文件:lwip_demo.c和lwip_demo.h。lwip_demo.h文件主要定義了發送標誌位並聲明瞭lwip_demo函數,這部分相對簡單,所以我們暫不詳細解釋。主要關注點是lwip_demo.c文件中的函數。在lwip_demo函數中,我們配置了相關的TCPClient參數,並創建了一個名為lwip_send_thread的發送數據線程。這個線程通過調用scokec函數來發送數據到服務器。接下來,我們將分別詳細解釋lwip_demo函數和lwip_send_thread任務。

/* 需要自己設置遠程IP地址 */
#define IP_ADDR   "192.168.101.33"

#define LWIP_DEMO_RX_BUFSIZE        100                       /* 最大接收數據長度 */
#define LWIP_DEMO_PORT               8080                     /* 連接的本地端口號 */
#define LWIP_SEND_THREAD_PRIO          ( tskIDLE_PRIORITY + 3 )/* 發送數據線程優先級 */
/* 接收數據緩衝區 */
uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE]; 

/* 發送數據內容 */
uint8_t g_lwip_demo_sendbuf[] = "ALIENTEK DATA \r\n";
/* 數據發送標誌位 */
uint8_t g_lwip_send_flag;
int g_sock = -1;
int g_lwip_connect_state = 0;
static void lwip_send_thread(void *arg);


/**
 * @brief       發送數據線程
 * @param       無
 * @retval      無
 */
void lwip_data_send(void)
{
xTaskCreate(lwip_send_thread, "lwip_send_thread", 4096, 
NULL, LWIP_SEND_THREAD_PRIO, NULL);
}

/**
 * @brief       lwip_demo實驗入口
 * @param       無
 * @retval      無
 */
void lwip_demo(void)
{
    struct sockaddr_in atk_client_addr;
    err_t err;
    int recv_data_len;
    char *tbuf;
    
    lwip_data_send();                                /* 創建發送數據線程 */
    
    while (1)
    {
sock_start:
        g_lwip_connect_state = 0;
        atk_client_addr.sin_family = AF_INET;         /* 表示IPv4網絡協議 */
        atk_client_addr.sin_port = htons(LWIP_DEMO_PORT);       /* 端口號 */
        atk_client_addr.sin_addr.s_addr = inet_addr(IP_ADDR);   /* 遠程IP地址 */
        g_sock = socket(AF_INET, SOCK_STREAM, 0);/* 可靠數據流交付服務既是TCP協議 */
        memset(&(atk_client_addr.sin_zero), 0,
               sizeof(atk_client_addr.sin_zero));
        
        tbuf = malloc(200);                                     /* 申請內存 */
        sprintf((char *)tbuf, "Port:%d", LWIP_DEMO_PORT);       /* 客户端端口號 */
        lcd_show_string(5, 170, 200, 16, 16, tbuf, MAGENTA);
        
        /* 連接遠程IP地址 */
        err = connect(g_sock, (struct sockaddr *)&atk_client_addr, 
sizeof(struct sockaddr));

        if (err == -1)
        {
            lcd_show_string(5, 190, 200, 16, 16, "State:Disconnect", MAGENTA);
            g_sock = -1;
            closesocket(g_sock);
            free(tbuf);
            vTaskDelay(10);
            goto sock_start;
        }

        lcd_show_string(5,190,200,16,16,"State:Connection Successful", MAGENTA);
        g_lwip_connect_state = 1;
        
        while (1)
        {
            recv_data_len = recv(g_sock,g_lwip_demo_recvbuf,
                                 LWIP_DEMO_RX_BUFSIZE,0);
            if (recv_data_len <= 0 )
            {
                closesocket(g_sock);
                g_sock = -1;
                lcd_fill(5, 190, lcd_self.width,320, WHITE);
                lcd_show_string(5, 190, 200,16,16,"State:Disconnect", MAGENTA);
                free(tbuf);
                goto sock_start;
            }
            
            printf("%s\r\n",g_lwip_demo_recvbuf);
            vTaskDelay(10);
        }
    }
}

/**
 * @brief       發送數據線程函數
 * @param       pvParameters : 傳入參數(未用到)
 * @retval      無
 */
void lwip_send_thread(void *pvParameters)
{
    pvParameters = pvParameters;
    
    err_t err;
    
    while (1)
    {
        while (1)
        {
            if(((g_lwip_send_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) 
&& (g_lwip_connect_state == 1)) /* 有數據要發送 */
            {
                err = write(g_sock, g_lwip_demo_sendbuf,
                            sizeof(g_lwip_demo_sendbuf));
                
                if (err < 0)
                {
                    break;
                }
                
                g_lwip_send_flag &= ~LWIP_SEND_DATA;
            }
            
            vTaskDelay(10);
        }
        
        closesocket(g_sock);
    }
}

在上述源碼中,首先創建了一個用於發送ESP32-S3設備數據的任務。然後,對TCPClient進行網絡參數配置,並調用connect函數來建立與遠程服務器的連接。當連接成功時,系統將進入接收輪詢任務。如果出現斷開連接的情況,系統將嘗試重新連接服務器。在發送線程中,發送數據前會檢查標誌位。如果標誌位有效,則通過write函數發送數據並重置標誌位。

53.4 下載驗證

在程序中,首先需要設置好能夠連接的網絡賬號和密碼。然後,使用筆記本電腦作為終端,確保它與ESP32-S3設備處於同一網絡段內。當ESP32-S3設備成功連接到網絡時,它的LCD顯示屏上會顯示相應的內容:

圖53.4.1 設備連接到網絡時,LCD顯示的信息
打開網絡調試助手,然後配置網絡參數,如TCPServer協議、端口號等,設置內容如下圖所示。

在確保網絡連接正常後,可以通過按下開發板上的KEY0按鍵來發送數據至網絡調試助手。當網絡調試助手接收到“ALIENTEK DATA”字符串時,它會在顯示區域展示這個信息。此外,用户還可以在調試助手的發送區域自行輸入要發送的數據,然後點擊發送鍵,將數據發送至ESP32-S3設備。此時,ESP32-S3的串口將打印接收到的數據,具體操作和輸出如下圖所示。

圖53.4.3 接收網絡調試助手的數據

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.