espidf的esp32版的websocketServer

参考

esp32-idf环境安装配置.csdn

espidf的esp32版的webServer测试.csdn

espidf的esp32版的webClient.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
相关推荐
j7~1 小时前
【C++】《C++二叉搜索树(BST)从入门到精通:概念、实现与Key/Value模型全解析》
开发语言·c++·学习·二叉搜索树
吃着火锅x唱着歌11 小时前
Effective C++ 学习笔记 条款43 学习处理模板化基类内的名称
c++·笔记·学习
weixin_4316004412 小时前
Agent Workflow 学习向:Code 节点,比模板更强的变量加工
后端·python·学习·ai·
Xuanzang Road13 小时前
第 40 届学习强企玄奘路戈壁挑战赛:在绝境中重塑自我
学习
tju新生代魔迷14 小时前
Python学习日记3
学习
hsjiasb15 小时前
FreeRTOS学习(三十九)——最终总结
学习·学习笔记·freertos
m4Rk_16 小时前
【论文阅读】Agent 记忆机制(47):Nemori——用“预测误差”判断什么经验值得被记住
论文阅读·人工智能·学习·开源·github
约克316 小时前
2024年空调除甲醛技术参数与性能评测解析
学习
kkkkkkkkkk_Z16 小时前
学嵌入式和C语言编程数据结构|学习日记Day21:内核链表与队列学习
c语言·数据结构·学习