参考
espidf的esp32版的webServer测试.csdn
platformio的esp32版的websocketServer.csdn
app_wifi.h
c
#ifndef _APP_WIFI_H_
#define _APP_WIFI_H_
#ifdef __cplusplus
extern "C" {
#endif
void app_wifi_main();
#ifdef __cplusplus
}
#endif
#endif
app_wifi.cpp
c
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "app_wifi.h"
// ============================================================
// WiFi 配置
// ============================================================
#define WIFI_SSID "ming"
#define WIFI_PASSWORD "panzer918190"
#define WIFI_MAX_RETRY 5
// ============================================================
// TAG
// ============================================================
static const char *TAG="app_wifi";
// ============================================================
// WiFi 状态
// ============================================================
static int s_retry_num=0;
static EventGroupHandle_t s_wifi_event_group=NULL;
// ============================================================
// Event Bits
// ============================================================
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
// ============================================================
// WiFi Event Handler
// ============================================================
static void wifi_event_handler(void *arg,esp_event_base_t event_base,int32_t event_id,void *event_data)
{
// STA 启动
if(event_base==WIFI_EVENT&&event_id==WIFI_EVENT_STA_START)
{
ESP_LOGI(TAG,"WiFi STA started");
esp_wifi_connect();
return;
}
// STA 断开
if(event_base==WIFI_EVENT&&event_id==WIFI_EVENT_STA_DISCONNECTED)
{
if(s_retry_num<WIFI_MAX_RETRY)
{
esp_wifi_connect();
s_retry_num++;
ESP_LOGW(TAG,"WiFi disconnected, retry %d/%d",s_retry_num,WIFI_MAX_RETRY);
}
else
{
ESP_LOGE(TAG,"WiFi connect failed");
if(s_wifi_event_group)
xEventGroupSetBits(s_wifi_event_group,WIFI_FAIL_BIT);
}
return;
}
// STA 获取 IP
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));
ESP_LOGI(TAG,"Netmask: " IPSTR,IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG,"Gateway: " IPSTR,IP2STR(&event->ip_info.gw));
s_retry_num=0;
if(s_wifi_event_group)
xEventGroupSetBits(s_wifi_event_group,WIFI_CONNECTED_BIT);
return;
}
}
// ============================================================
// 初始化 STA
// ============================================================
static void wifi_init_sta(void)
{
wifi_config_t wifi_config={0};
// SSID
strncpy((char*)wifi_config.sta.ssid,WIFI_SSID,sizeof(wifi_config.sta.ssid)-1);
// Password
strncpy((char*)wifi_config.sta.password,WIFI_PASSWORD,sizeof(wifi_config.sta.password)-1);
// WiFi STA 配置
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA,&wifi_config));
ESP_LOGI(TAG,"STA configured");
ESP_LOGI(TAG,"SSID: %s",WIFI_SSID);
}
// ============================================================
// app_wifi_main
// ============================================================
void app_wifi_main(void)
{
esp_err_t ret;
// 1. 初始化 NVS
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);
// 2. 初始化 TCP/IP 网络栈
ESP_ERROR_CHECK(esp_netif_init());
// 3. 创建默认 Event Loop
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 4. 创建 Event Group
s_wifi_event_group=xEventGroupCreate();
if(!s_wifi_event_group)
{
ESP_LOGE(TAG,"Failed to create event group");
return;
}
// 5. 创建 STA 网络接口
esp_netif_t *sta_netif=esp_netif_create_default_wifi_sta();
if(!sta_netif)
{
ESP_LOGE(TAG,"Failed to create STA netif");
return;
}
// 6. 初始化 WiFi Driver
wifi_init_config_t cfg=WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// 7. 注册 WiFi Event
ESP_ERROR_CHECK(
esp_event_handler_instance_register(
WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
// 8. 注册 IP Event
ESP_ERROR_CHECK(
esp_event_handler_instance_register(
IP_EVENT,
IP_EVENT_STA_GOT_IP,
&wifi_event_handler,
NULL,
NULL));
// 9. 配置 STA
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
wifi_init_sta();
// 10. 启动 WiFi
ESP_ERROR_CHECK(esp_wifi_start());
// 11. 关闭 WiFi 省电
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
ESP_LOGI(TAG,"WiFi started");
// 12. 等待连接结果
EventBits_t bits=xEventGroupWaitBits(
s_wifi_event_group,
WIFI_CONNECTED_BIT|WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
// 13. 判断连接结果
if(bits&WIFI_CONNECTED_BIT)
{
ESP_LOGI(TAG,"Connected to WiFi");
}
else if(bits&WIFI_FAIL_BIT)
{
ESP_LOGE(TAG,"Failed to connect to WiFi");
}
// 14. 删除 Event Group
vEventGroupDelete(s_wifi_event_group);
s_wifi_event_group=NULL;
ESP_LOGI(TAG,"app_wifi_main finished");
}
WebSocketServer.h
c
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <functional>
#include "esp_err.h"
#include "esp_http_server.h"
class WebSocketServer
{
public:
enum class EventType { CONNECT, DISCONNECT, TEXT, BINARY };
struct Event
{
EventType type;
int fd;
uint8_t *data;
size_t len;
};
using EventCallback=std::function<void(const Event&)>;
public:
WebSocketServer();
~WebSocketServer();
bool start();
void set_event_callback(EventCallback callback);
esp_err_t broadcast_text(const char *data);
esp_err_t broadcast_binary(const uint8_t *data,size_t len);
esp_err_t send_text(int fd,const char *data);
esp_err_t send_binary(int fd,const uint8_t *data,size_t len);
httpd_handle_t get_server(){return server;}
private:
httpd_handle_t server;
EventCallback callback;
static esp_err_t ws_handler(httpd_req_t *req);
static esp_err_t ws_open(httpd_handle_t hd,int sockfd);
static void ws_close(httpd_handle_t hd,int sockfd);
esp_err_t send_frame(int fd,httpd_ws_type_t type,const uint8_t *data,size_t len);
esp_err_t broadcast(httpd_ws_type_t type,const uint8_t *data,size_t len);
void notify_event(const Event& event);
private:
static WebSocketServer *instance;
static const char *TAG;
};
WebSocketServer.cpp
c
#include "WebSocketServer.h"
#include <string.h>
#include <vector>
#include "esp_log.h"
const char *WebSocketServer::TAG="WebSocketServer";
WebSocketServer *WebSocketServer::instance=nullptr;
WebSocketServer::WebSocketServer(){server=nullptr;instance=this;}
WebSocketServer::~WebSocketServer()
{
if(server){httpd_stop(server);server=nullptr;}
}
void WebSocketServer::set_event_callback(EventCallback cb){callback=cb;}
void WebSocketServer::notify_event(const Event &event)
{
if(callback) callback(event);
}
esp_err_t WebSocketServer::ws_open(httpd_handle_t hd,int sockfd)
{
ESP_LOGI(TAG,"Client online fd=%d",sockfd);
if(instance)
{
Event e{EventType::CONNECT,sockfd,nullptr,0};
instance->notify_event(e);
}
return ESP_OK;
}
void WebSocketServer::ws_close(httpd_handle_t hd,int sockfd)
{
ESP_LOGI(TAG,"Client offline fd=%d",sockfd);
if(instance)
{
Event e{EventType::DISCONNECT,sockfd,nullptr,0};
instance->notify_event(e);
}
}
esp_err_t WebSocketServer::ws_handler(httpd_req_t *req)
{
int fd=httpd_req_to_sockfd(req);
if(req->method==HTTP_GET) return ESP_OK;
httpd_ws_frame_t pkt{};
esp_err_t ret=httpd_ws_recv_frame(req,&pkt,0);
if(ret!=ESP_OK) return ret;
std::vector<uint8_t> buf;
if(pkt.len)
{
buf.resize(pkt.len);
pkt.payload=buf.data();
ret=httpd_ws_recv_frame(req,&pkt,pkt.len);
if(ret!=ESP_OK) return ret;
}
if(instance)
{
EventType type;
if(pkt.type==HTTPD_WS_TYPE_TEXT)
type=EventType::TEXT;
else if(pkt.type==HTTPD_WS_TYPE_BINARY)
type=EventType::BINARY;
else
return ESP_OK;
Event e{type,fd,pkt.payload,pkt.len};
instance->notify_event(e);
}
return ESP_OK;
}
bool WebSocketServer::start()
{
httpd_config_t config=HTTPD_DEFAULT_CONFIG();
config.open_fn=ws_open;
config.close_fn=ws_close;
if(httpd_start(&server,&config)!=ESP_OK)
{
ESP_LOGE(TAG,"httpd_start failed");
return false;
}
httpd_uri_t uri{};
uri.uri="/ws";
uri.method=HTTP_GET;
uri.handler=ws_handler;
uri.is_websocket=true;
if(httpd_register_uri_handler(server,&uri)!=ESP_OK)
{
ESP_LOGE(TAG,"register websocket failed");
return false;
}
ESP_LOGI(TAG,"WebSocket server started");
return true;
}
esp_err_t WebSocketServer::send_frame(int fd,httpd_ws_type_t type,const uint8_t *data,size_t len)
{
if(!server) return ESP_FAIL;
httpd_ws_frame_t pkt{};
pkt.type=type;
pkt.payload=const_cast<uint8_t*>(data);
pkt.len=len;
return httpd_ws_send_frame_async(server,fd,&pkt);
}
esp_err_t WebSocketServer::send_text(int fd,const char *data)
{
return send_frame(fd,HTTPD_WS_TYPE_TEXT,(uint8_t*)data,strlen(data));
}
esp_err_t WebSocketServer::send_binary(int fd,const uint8_t *data,size_t len)
{
return send_frame(fd,HTTPD_WS_TYPE_BINARY,data,len);
}
esp_err_t WebSocketServer::broadcast(httpd_ws_type_t type,const uint8_t *data,size_t len)
{
if(!server) return ESP_FAIL;
httpd_ws_frame_t pkt{};
pkt.type=type;
pkt.payload=const_cast<uint8_t*>(data);
pkt.len=len;
int fds[8];
size_t num=8;
esp_err_t ret=httpd_get_client_list(server,&num,fds);
if(ret!=ESP_OK) return ret;
for(size_t i=0;i<num;i++)
{
if(httpd_ws_get_fd_info(server,fds[i])==HTTPD_WS_CLIENT_WEBSOCKET)
httpd_ws_send_frame_async(server,fds[i],&pkt);
}
return ESP_OK;
}
esp_err_t WebSocketServer::broadcast_text(const char *data)
{
return broadcast(HTTPD_WS_TYPE_TEXT,(uint8_t*)data,strlen(data));
}
esp_err_t WebSocketServer::broadcast_binary(const uint8_t *data,size_t len)
{
return broadcast(HTTPD_WS_TYPE_BINARY,data,len);
}
main.cpp
c
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "app_wifi.h"
#include "WebSocketServer.h"
static const char *TAG="MAIN";
static WebSocketServer ws;
static void websocket_event(const WebSocketServer::Event &event)
{
switch(event.type)
{
case WebSocketServer::EventType::CONNECT:
ESP_LOGI(TAG,"Client connect fd=%d",event.fd);
break;
case WebSocketServer::EventType::DISCONNECT:
ESP_LOGI(TAG,"Client disconnect fd=%d",event.fd);
break;
case WebSocketServer::EventType::TEXT:
ESP_LOGI(TAG,"RX TEXT fd=%d len=%d",event.fd,event.len);
if(event.data&&event.len) ESP_LOGI(TAG,"data=%s",(char*)event.data);
break;
case WebSocketServer::EventType::BINARY:
ESP_LOGI(TAG,"RX BINARY fd=%d len=%d",event.fd,event.len);
break;
default:
break;
}
}
static void websocket_periodic_task(void *arg)
{
uint32_t count=0;
while(1)
{
vTaskDelay(pdMS_TO_TICKS(1000));
char msg[64];
snprintf(msg,sizeof(msg),"%lu hello word",count++);
ESP_LOGI(TAG,"TX:%s",msg);
ws.broadcast_text(msg);
}
}
extern "C" void app_main(void)
{
app_wifi_main();
ws.set_event_callback(websocket_event);
if(!ws.start())
{
ESP_LOGE(TAG,"WebSocket start failed");
return;
}
xTaskCreate(websocket_periodic_task,"ws_periodic",4096,NULL,5,NULL);
ESP_LOGI(TAG,"system running");
}
CMakeLists.txt
bash
idf_component_register(
SRCS
"main.cpp"
"WebSocketServer.cpp"
"app_wifi.cpp"
INCLUDE_DIRS
"."
REQUIRES
esp_http_server
esp_timer
esp_wifi
esp_event
esp_netif
nvs_flash
)
测试
连接 ws://192.168.1.102/ws
bash
I (10827) MAIN: TX:8 hello word
I (14117) MAIN: Client connect fd=57
I (14827) MAIN: TX:12 hello word
I (16827) MAIN: TX:14 hello word
I (20247) MAIN: Client disconnect fd=57
I (26827) MAIN: TX:24 hello word