此代碼為接收STM32的數據然后直接轉發到網絡調試助手,當有設備連接到esp32軟件熱點時會通過串口發送字符’a’給STM32,當有設備斷開連接時會通過串口發送字符’b’,
ESP32的TX:GPIO4, RX:GPIO5
ESP32作為TCP服務器地址為192.168.4.1 監聽端口為3333
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_mac.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h" //lwip錯誤代碼
#include "lwip/sys.h"
#include "sdkconfig.h"
#include <unistd.h>
#include <sys/socket.h>
#include <errno.h>
#include <arpa/inet.h>
#include "esp_netif.h"
#include <sys/param.h>
#include "esp_system.h"
#include "lwip/sockets.h"
#include <lwip/netdb.h>
#include "driver/uart.h"
#include "driver/gpio.h"#define EXAMPLE_ESP_WIFI_SSID "laotie666" // 熱點名
#define EXAMPLE_ESP_WIFI_PASS "12345678" // 熱點密碼
#define EXAMPLE_ESP_WIFI_CHANNEL CONFIG_ESP_WIFI_CHANNEL // 信道(來自menuconfig)
#define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN // 最大連接數(來自menuconfig)
#define CONFIG_EXAMPLE_IPV4 1 // 啟用IPv4
#define PORT 3333 // 服務器監聽的端口號
#define KEEPALIVE_IDLE 5 // TCP Keepalive空閑時間(秒)
#define KEEPALIVE_INTERVAL 5 // Keepalive探測間隔(秒)
#define KEEPALIVE_COUNT 3 // 超時前的探測次數
#define EX_UART_NUM UART_NUM_1 // 使用UART1端口
#define BUF_SIZE (1024) // UART緩沖區大小
#define RD_BUF_SIZE (BUF_SIZE) // 讀取緩沖區大小/*函數聲明*/uint8_t mac[6];
// 日志標簽
static const char *TAG = "wifi softAP";
static const char *TAG_ZH = "Zhanghui:";static uint8_t uart_buffer1[1024];
uint8_t uart_data;static QueueHandle_t uart0_queue;// 當建立tcp連接之后才會進行串口接收震動數據
uint8_t uart_re_en_flag = 0;
// TCP發送錯誤標志
int tcp_send_error_flag;// WiFi事件處理函數
static void wifi_event_handler(void *arg, esp_event_base_t event_base,int32_t event_id, void *event_data)
{if (event_id == WIFI_EVENT_AP_STACONNECTED){// 有設備連接到我們的軟件熱點時會觸發這個事件wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data;ESP_LOGI(TAG, "station " MACSTR " join, AID=%d",MAC2STR(event->mac), event->aid);// 當有設備連接到esp32軟件熱點時會通過串口發送字符’a’uart_data = 'a';uart_write_bytes(EX_UART_NUM, &uart_data, 1);}else if (event_id == WIFI_EVENT_AP_STADISCONNECTED){// 斷開連接時wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data;// 打印斷開設備的信息,如ipESP_LOGI(TAG, "station " MACSTR " leave, AID=%d, reason=%d",MAC2STR(event->mac), event->aid, event->reason);// 當有設備斷開連接時會通過串口發送字符’b’uart_data = 'b';uart_write_bytes(EX_UART_NUM, &uart_data, 1);}
}// 初始化SoftAP模式
void wifi_init_softap(void)
{// 通過調用esp_netif_init()來啟動LWIPtaskESP_ERROR_CHECK(esp_netif_init()); // 初始化底層TCP/IP棧// 通過esp_event_loop_create_default()來創建啟動事件task--eventtaskESP_ERROR_CHECK(esp_event_loop_create_default()); // 創建默認事件循環esp_netif_create_default_wifi_ap(); // 創建默認WIFI AP網絡接口// 初始化WIFI驅動配置為默認值wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();// esp_wifi_init() 初始化WIFI driverESP_ERROR_CHECK(esp_wifi_init(&cfg));// 4.對我們所需處理的一些事件注冊了一個回調函數wifi_event_handlerESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,ESP_EVENT_ANY_ID,&wifi_event_handler,NULL,NULL));// 配置driverwifi_config_t wifi_config = {.ap = {.ssid = EXAMPLE_ESP_WIFI_SSID, // 熱點的名字.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID), // SSID長度.channel = EXAMPLE_ESP_WIFI_CHANNEL, // 無線信道 WiFi信號工作在2.4GHz或5GHz頻段(取決于設備支持),這些頻段被劃分為多個子頻段,每個子頻段稱為一個信道。.password = EXAMPLE_ESP_WIFI_PASS, // 熱點的密碼.max_connection = EXAMPLE_MAX_STA_CONN, // 熱點的最大連接數目
#ifdef CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT // 用于根據系統配置決定使用哪種 Wi-Fi 安全協議.authmode = WIFI_AUTH_WPA3_PSK, // 授權的模式,登錄的加密模式 // WPA3加密模式.sae_pwe_h2e = WPA3_SAE_PWE_BOTH, // SAE密碼交換方法
#else /* CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT */.authmode = WIFI_AUTH_WPA2_PSK, // 默認WPA2加密
#endif.pmf_cfg = {// 受保護的管理幀配置.required = true, // 要求PMF},},};// 如果密碼為空則使用開放模式if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0){wifi_config.ap.authmode = WIFI_AUTH_OPEN;}// 使用esp_wifi_set_mode()對driver進行配置ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); // 設置WIFI為AP模式// 應用AP配置ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));// 啟動driverESP_ERROR_CHECK(esp_wifi_start()); // 啟動WIFI// 打印熱點配置信息ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}
/*wifi driver被啟動之后, 會發送WIFI_EVENT_AP_START這個事件到event Task中,如果有處理這個事件的回調函數,就會再這個回調函數中進行處理;如果有設備進行連接,就會發送WIFI_EVENT_AP_STACONNECTED這個事件到event Task,之后再wifi_event_handler中進行處理(實例代碼中只是打印了相應的信息)如果有斷開連接的事件WIFI_EVENT_AP_STADISCONNECTED,也會發送到event Task,event Task調用wifi_event_handler進行處理
*/// 數據轉發函數
static void do_retransmit(const int sock)
{int len;uart_event_t event; // UART事件結構體uint8_t *dtmp = (uint8_t *)malloc(RD_BUF_SIZE);while (1){//從名為`uart0_queue`的隊列中接收數據,并將接收到的數據存儲到`event`變量中,如果沒有數據可接收,則無限期等待(阻塞)直到有數據到來。if (xQueueReceive(uart0_queue, (void *)&event, (TickType_t)portMAX_DELAY)){//清零內存區域,即將這段內存區域中的所有字節都設置為0,與memset()函數類似但功能受限。由于bzero()已被廢棄,推薦使用memset()進行內存設置。bzero(dtmp, RD_BUF_SIZE);ESP_LOGI(TAG, "uart[%d] event:", EX_UART_NUM);switch (event.type){// 接收到的數據通常會在這里被處理 dtmp指向接收到的數據 event.size為接收到數據的大小case UART_DATA: {ESP_LOGI(TAG, "[UART DATA]: %d", event.size);uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY);int written = send(sock, dtmp, event.size, 0);if (written < 0){ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);// Failed to retransmit, giving upreturn;}}break;// 硬件FIFO溢出事件case UART_FIFO_OVF:ESP_LOGI(TAG, "hw fifo overflow");// If fifo overflow happened, you should consider adding flow control for your application.// The ISR has already reset the rx FIFO,// As an example, we directly flush the rx buffer here in order to read more data.uart_flush_input(EX_UART_NUM);xQueueReset(uart0_queue);break;// 環形緩沖區滿事件case UART_BUFFER_FULL:ESP_LOGI(TAG, "ring buffer full");// If buffer full happened, you should consider increasing your buffer size// As an example, we directly flush the rx buffer here in order to read more data.uart_flush_input(EX_UART_NUM);xQueueReset(uart0_queue);break;// 檢測到UART線路斷開case UART_BREAK:ESP_LOGI(TAG, "uart rx break");break;// 幀錯誤case UART_FRAME_ERR:ESP_LOGI(TAG, "uart frame error");break;default:ESP_LOGI(TAG, "uart event type: %d", event.type);break;}}}// do {// len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);// if (len < 0) {// ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);// } else if (len == 0) {// ESP_LOGW(TAG, "Connection closed");// } else {// rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string// ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);// // send() can return less bytes than supplied length.// // Walk-around for robust implementation.// int to_write = len;// while (to_write > 0) {// int written = send(sock, rx_buffer + (len - to_write), to_write, 0);// if (written < 0) {// ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);// // Failed to retransmit, giving up// return;// }// to_write -= written;// }// }// } while (len > 0);
}
/* UART事件處理任務 */// TCP服務器任務
static void tcp_server_task(void *pvParameters)
{char addr_str[128]; // 存儲客戶端IP字符串int addr_family = (int)pvParameters; // 獲取地址族參數int ip_protocol = 0; // IP協議類型int keepAlive = 1; // 啟用Keepaliveint keepIdle = 5; // Keepalive空閑時間int keepInterval = 5; // 探測間隔int keepCount = 3; // 探測次數struct sockaddr_storage dest_addr; // 服務器地址存儲結構// 配置服務器地址if (addr_family == AF_INET){struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY); // 監聽所有接口dest_addr_ip4->sin_family = AF_INET; // IPv4地址族dest_addr_ip4->sin_port = htons(PORT); // 設置端口ip_protocol = IPPROTO_IP; // IP協議}// 創建監聽套接字ESP_LOGE(TAG_ZH, "create socket");int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);if (listen_sock < 0){ESP_LOGE(TAG_ZH, "Unable to create socket: errno %d", errno);vTaskDelete(NULL);return;}// 設置套接字選項(地址重用)int opt = 1;setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)// Note that by default IPV6 binds to both protocols, it is must be disabled// if both protocols used at the same time (used in CI)setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
#endifESP_LOGI(TAG, "套接字已創建");int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));if (err != 0){ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); // 套接字綁定失敗ESP_LOGE(TAG, "IPPROTO: %d", addr_family);goto CLEAN_UP;}ESP_LOGI(TAG, "Socket bound, port %d", PORT);err = listen(listen_sock, 1); // 最大掛起連接數為1if (err != 0){ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);goto CLEAN_UP;}while (1){struct sockaddr_storage source_addr; // 客戶端地址存儲socklen_t addr_len = sizeof(source_addr);// 接受客戶端連接int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);if (sock < 0){ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);break;}// 設置TCP Keepalive選項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));// 將客戶端IP地址轉換為字符串
#ifdef CONFIG_EXAMPLE_IPV4if (source_addr.ss_family == PF_INET){inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);}
#endif
#ifdef CONFIG_EXAMPLE_IPV6if (source_addr.ss_family == PF_INET6){inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);}
#endifESP_LOGI(TAG_ZH, "Socket accepted ip address: %s", addr_str); // 接受的客戶端IP:uart_re_en_flag = 1; // 設置標志位,表示可以接收串口數據// do_retransmit(sock);do_retransmit(sock);shutdown(sock, 0);close(sock);}CLEAN_UP:close(listen_sock);vTaskDelete(NULL);
}void app_main(void)
{// 初始化存儲空間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);uart_config_t uart_config = {.baud_rate = 115200,.data_bits = UART_DATA_8_BITS,.parity = UART_PARITY_DISABLE,.stop_bits = UART_STOP_BITS_1,.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,.source_clk = UART_SCLK_DEFAULT,};// 安裝UART驅動程序(創建事件隊列)uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0);// 應用參數配置uart_param_config(EX_UART_NUM, &uart_config);// 設置UART引腳(TX:GPIO4, RX:GPIO5)uart_set_pin(EX_UART_NUM, GPIO_NUM_4, GPIO_NUM_5, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");ESP_ERROR_CHECK(esp_efuse_mac_get_default(mac));wifi_init_softap();xTaskCreate(tcp_server_task, "tcp_server", 4096, (void *)AF_INET, 5, NULL);
}