ESP32S3 WIFI 实现TCP服务器和静态IP
一、 TCP服务器代码
代码由station_example_main的官方例程修改
/* WiFi station Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include <stdlib.h>
#include "lwip/err.h"
#include "lwip/sys.h"
#include "lwip/sockets.h"
#include <lwip/netdb.h>
/* The examples use WiFi configuration that you can set via project configuration menu
If you'd rather not, just change the below entries to strings with
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY
#define EXAMPLE_MAX_STA_CONN 4 // 最大客户端连接数
#define PORT 8080
#define KEEPALIVE_IDLE 5
#define KEEPALIVE_INTERVAL 5
#define KEEPALIVE_COUNT 3
#if CONFIG_ESP_WPA3_SAE_PWE_HUNT_AND_PECK
#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HUNT_AND_PECK
#define EXAMPLE_H2E_IDENTIFIER ""
#elif CONFIG_ESP_WPA3_SAE_PWE_HASH_TO_ELEMENT
#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HASH_TO_ELEMENT
#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID
#elif CONFIG_ESP_WPA3_SAE_PWE_BOTH
#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_BOTH
#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID
#endif
#if CONFIG_ESP_WIFI_AUTH_OPEN
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN
#elif CONFIG_ESP_WIFI_AUTH_WEP
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP
#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK
#endif
/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
#define WIFI_CONNECTED_SUCCESS BIT2
// 客户端任务参数结构体
typedef struct {
int socket;
struct sockaddr_in addr;
} client_params_t;
static const char *TAG = "wifi station";
static int s_retry_num = 0;
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_SUCCESS |WIFI_CONNECTED_BIT);
}
}
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
/* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (password len => 8).
* If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value
* to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to
* WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards.
*/
.threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
.sae_pwe_h2e = ESP_WIFI_SAE_MODE,
.sae_h2e_identifier = EXAMPLE_H2E_IDENTIFIER,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}
// 客户端处理任务
void client_task(void *pvParameters)
{
client_params_t *params = (client_params_t *)pvParameters;
int sock = params->socket;
struct sockaddr_in addr = params->addr;
free(pvParameters); // 释放分配的内存
char addr_str[128];
inet_ntoa_r(addr.sin_addr, addr_str, sizeof(addr_str) - 1);
ESP_LOGI(TAG, "客户端任务启动: IP=%s, 端口=%d", addr_str, ntohs(addr.sin_port));
char rx_buffer[128];
char tx_buffer[128];
// 设置keepalive选项
int keepAlive = 1;
int keepIdle = KEEPALIVE_IDLE;
int keepInterval = KEEPALIVE_INTERVAL;
int keepCount = KEEPALIVE_COUNT;
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
// 设置接收超时
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
// 处理客户端数据
while (1) {
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
if (len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 超时,继续等待
continue;
}
ESP_LOGE(TAG, "接收数据错误: errno %d", errno);
break;
} else if (len == 0) {
ESP_LOGI(TAG, "客户端 %s 断开连接", addr_str);
break;
} else {
rx_buffer[len] = 0; // 添加null终止符
ESP_LOGI(TAG, "从 %s 收到 %d 字节: %s", addr_str, len, rx_buffer);
// 发送响应
int tx_len = snprintf(tx_buffer, sizeof(tx_buffer), " server has received %d bytes", len);
int sent = send(sock, tx_buffer, tx_len, 0);
if (sent < 0) {
ESP_LOGE(TAG, "发送失败: errno %d", errno);
break;
}
}
}
// 关闭socket
shutdown(sock, 0);
close(sock);
ESP_LOGI(TAG, "客户端 %s 处理结束", addr_str);
vTaskDelete(NULL);
}
// TCP服务器任务
void tcp_server_task(void *pvParameters)
{
char addr_str[128];
int addr_family = AF_INET;
int ip_protocol = IPPROTO_IP;
struct sockaddr_in server_addr;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
// 创建socket
int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (listen_sock < 0) {
ESP_LOGE(TAG, "无法创建socket: errno %d", errno);
vTaskDelete(NULL);
return;
}
// 设置socket选项 (允许地址重用)
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 绑定socket
int err = bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err != 0) {
ESP_LOGE(TAG, "socket绑定失败: errno %d", errno);
close(listen_sock);
vTaskDelete(NULL);
return;
}
// 开始监听
err = listen(listen_sock, EXAMPLE_MAX_STA_CONN);
if (err != 0) {
ESP_LOGE(TAG, "socket监听失败: errno %d", errno);
close(listen_sock);
vTaskDelete(NULL);
return;
}
ESP_LOGI(TAG, "TCP服务器已启动,监听端口: %d", PORT);
while (1) {
ESP_LOGI(TAG, "等待新的客户端连接...");
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &client_len);
if (client_sock < 0) {
ESP_LOGE(TAG, "接受连接失败: errno %d", errno);
continue;
}
// 将客户端IP转换为字符串
inet_ntoa_r(client_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
ESP_LOGI(TAG, "新的客户端连接: IP=%s, 端口=%d", addr_str, ntohs(client_addr.sin_port));
// 为客户端任务分配参数
client_params_t *params = malloc(sizeof(client_params_t));
if (params == NULL) {
ESP_LOGE(TAG, "内存分配失败");
close(client_sock);
continue;
}
params->socket = client_sock;
params->addr = client_addr;
// 创建客户端任务
if (xTaskCreate(client_task, "client_task", 4096, params, 5, NULL) != pdPASS) {
ESP_LOGE(TAG, "无法创建客户端任务");
free(params);
close(client_sock);
}
}
close(listen_sock);
vTaskDelete(NULL);
}
void app_main(void)
{
//Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
wifi_init_sta();
xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_SUCCESS, pdFALSE, pdFALSE, portMAX_DELAY);
vTaskDelay(1000 / portTICK_PERIOD_MS);
// 创建TCP服务器任务
xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 5, NULL);
}
运行测试
I (1616) esp_netif_handlers: sta ip: 192.168.137.54, mask: 255.255.255.0, gw: 192.168.137.1
I (1616) wifi station: got ip:192.168.137.54
I (1616) wifi station: connected to ap SSID:zhaozhong password:12345678
I (2616) wifi station: TCP服务器已启动,监听端口: 8080
I (2616) wifi station: 等待新的客户端连接...
I (2616) main_task: Returned from app_main()
I (18386) wifi station: 新的客户端连接: IP=192.168.137.1, 端口=59883
I (18386) wifi station: 等待新的客户端连接...
I (18386) wifi station: 客户端任务启动: IP=192.168.137.1, 端口=59883
I (19296) wifi:<ba-add>idx:0 (ifx:0, d6:54:8b:b2:90:f3), tid:0, ssn:7, winSize:64
I (23296) wifi station: 从 192.168.137.1 收到 20 字节: http://www.cmsoft.cn
I (28216) wifi station: 从 192.168.137.1 收到 20 字节: http://www.cmsoft.cn
I (29146) wifi station: 从 192.168.137.1 收到 20 字节: http://www.cmsoft.cn
I (58626) wifi station: 新的客户端连接: IP=192.168.137.230, 端口=49416
I (58626) wifi station: 等待新的客户端连接...
I (58626) wifi station: 客户端任务启动: IP=192.168.137.230, 端口=49416
I (58626) wifi station: 新的客户端连接: IP=192.168.137.230, 端口=49416
I (58626) wifi station: 等待新的客户端连接...
I (58626) wifi station: 客户端任务启动: IP=192.168.137.230, 端口=49416
I (78916) wifi station: 从 192.168.137.230 收到 10 字节: 5588888811
I (87206) wifi station: 从 192.168.137.230 收到 5 字节: hello
I (93646) wifi station: 从 192.168.137.230 收到 5 字节: hello
I (98886) wifi station: 从 192.168.137.1 收到 20 字节: http://www.cmsoft.cn
I (101326) wifi station: 从 192.168.137.1 收到 20 字节: http://www.cmsoft.cn
I (106256) wifi station: 客户端 192.168.137.1 断开连接
- ESP32连接电脑创建的热点,并分配到了192.168.137.54地址。手机分配到了230地址
- ESP32启动TCP服务器,然后用电脑网络助手和手机的网络助手连接。然后发送数据测试。
- 可以实现多连接等功能。
二、 ESP32设置为静态IP
增加部分代码设置静态IP,然后在WiFi启动之前调用即可
// 静态IP配置
#define EXAMPLE_ESP_STATIC_IP "192.168.137.100"
#define EXAMPLE_ESP_STATIC_GW "192.168.137.1"
#define EXAMPLE_ESP_STATIC_NM "255.255.255.0"
// 设置静态IP的函数
void set_static_ip()
{
esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
if (netif == NULL) {
ESP_LOGE(TAG, "获取网络接口失败");
return;
}
// 设置静态IP配置
esp_netif_ip_info_t ip_info;
memset(&ip_info, 0, sizeof(esp_netif_ip_info_t));
ip_info.ip.addr = ipaddr_addr(EXAMPLE_ESP_STATIC_IP);
ip_info.gw.addr = ipaddr_addr(EXAMPLE_ESP_STATIC_GW);
ip_info.netmask.addr = ipaddr_addr(EXAMPLE_ESP_STATIC_NM);
// 应用静态IP配置
esp_netif_dhcpc_stop(netif);
esp_netif_set_ip_info(netif, &ip_info);
ESP_LOGI(TAG, "静态IP设置成功 (使用esp_netif)");
}
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
/* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (password len => 8).
* If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value
* to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to
* WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards.
*/
.threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
.sae_pwe_h2e = ESP_WIFI_SAE_MODE,
.sae_h2e_identifier = EXAMPLE_H2E_IDENTIFIER,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
// 在启动WiFi前设置静态IP
set_static_ip();
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}
进行测试
I (567) wifi:dp: 1, bi: 102400, li: 3, scale listen interval from 307200 us to 307200 us
I (577) wifi:set rx beacon pti, rx_bcn_pti: 0, bcn_timeout: 25000, mt_pti: 0, mt_time: 10000
I (587) esp_netif_handlers: sta ip: 192.168.137.100, mask: 255.255.255.0, gw: 192.168.137.1
I (597) wifi station: got ip:192.168.137.100
I (597) wifi station: connected to ap SSID:zhaozhong password:12345678
I (647) wifi:AP's beacon interval = 102400 us, DTIM period = 3
I (1607) wifi station: TCP服务器已启动,监听端口: 8080
I (1607) wifi station: 等待新的客户端连接...
I (1607) main_task: Returned from app_main()
I (8847) wifi station: 新的客户端连接: IP=192.168.137.1, 端口=54251
I (8847) wifi station: 等待新的客户端连接...
I (8847) wifi station: 客户端任务启动: IP=192.168.137.1, 端口=54251
I (11307) wifi:<ba-add>idx:0 (ifx:0, d6:54:8b:b2:90:f3), tid:0, ssn:6, winSize:64
I (13447) wifi station: 从 192.168.137.1 收到 20 字节: http://www.cmsoft.cn
I (14987) wifi station: 从 192.168.137.1 收到 20 字节: http://www.cmsoft.cn
I (15907) wifi station: 从 192.168.137.1 收到 20 字节: http://www.cmsoft.cn
I (21127) wifi station: 从 192.168.137.1 收到 20 字节: http://www.cmsoft.cn
I (21437) wifi station: 从 192.168.137.1 收到 20 字节: http://www.cmsoft.cn
IP已经固定为192.168.137.100 静态IP设置正常。