Implementation of Server-Sent Events (SSE) in ESP-IDF

mustafa
Posts: 1
Joined: Tue Nov 07, 2023 7:32 pm

Implementation of Server-Sent Events (SSE) in ESP-IDF

Postby mustafa » Fri Jan 19, 2024 8:43 am

Hello,

I am in the process of transitioning a project from the Arduino framework to ESP-IDF. Everything is going smoothly, but I couldn't find any examples or documentation related to the implementation of Server-Sent Events (SSE).

Am I missing something? Or should I consider abandoning SSE for ESP-IDF?

Thank you.

Maximilian22
Posts: 4
Joined: Mon Jan 29, 2024 11:58 am

Re: Implementation of Server-Sent Events (SSE) in ESP-IDF

Postby Maximilian22 » Mon Jan 29, 2024 12:08 pm

I'd like to second this question!

What I've found so far is only this:
https://esp32.com/viewtopic.php?p=115818
https://esp32.com/viewtopic.php?t=24445#p87263

And maybe also this could help:
https://esp32.com/viewtopic.php?t=24445#p87180

But for me that has been not sufficient to achieve anything further than getting a rough understanding.

I - and probably many others - would appreciate, if somebody would be so kind and provide a working SSE example.
So far I'm staying with Arduino IDE, with AsyncWebSrv and AsyncEventSource. However, I'd like to port may program to ESP-IDF.

Thanks, cheers,
Max

MicroController
Posts: 1385
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Implementation of Server-Sent Events (SSE) in ESP-IDF

Postby MicroController » Tue Jan 30, 2024 12:48 am

Yeah, SSE support in the IDF would be neat.
It's actually not hard to implement using the http_server component. In a request handler, you retrieve the connection's underlying socket, send the 'raw' HTTP header to the client, and store the socket fd somewhere so that another task can send events over it whenever needed.

Maximilian22
Posts: 4
Joined: Mon Jan 29, 2024 11:58 am

Re: Implementation of Server-Sent Events (SSE) in ESP-IDF

Postby Maximilian22 » Sat Feb 03, 2024 12:22 pm

I've tried that now. My html code is this, and I have to stick with this (EventSource).

Code: Select all

<!DOCTYPE HTML>
<html>
<header>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Eventserver</title>
</header>
<body>
<hr>
<h1> Test Async Event Source</h1>
<br>
<p>Complete State: <span id="target"></span></p>
<br>
<hr>
<script>
if (!!window.EventSource) {
 var source = new EventSource('/events');
 source.addEventListener('complete', function(e) {
  document.getElementById("target").innerHTML = e.data; 
 }, false);
}
</script>
</body>
</html>)
I'm sending the response to the events request like this (the following codes are based on the IDF server example):

Code: Select all

static esp_err_t events_handler(httpd_req_t *req)
{
    char*  buf;
    size_t buf_len;

    mySocketHD = req->handle;
    mySocketFD = httpd_req_to_sockfd(req);

    buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1;
    if (buf_len > 1) {
        buf = malloc(buf_len);
        if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) {
            ESP_LOGI(TAG, "Found header => Host: %s", buf);
        }
        free(buf);
    }

    httpd_resp_set_status(req, HTTPD_200);
    httpd_resp_set_type(req, "text/event-stream");
    httpd_resp_set_hdr(req, "Cache-Control", "no-cache");
    httpd_resp_set_hdr(req, "Connection", "keep-alive");

    const char* resp_str = (const char*) req->user_ctx;
    httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);

    if (httpd_req_get_hdr_value_len(req, "Host") == 0) {
        ESP_LOGI(TAG, "Request headers lost");
    }
    return ESP_OK;
}

static const httpd_uri_t events = {
    .uri       = "/events",
    .method    = HTTP_GET,
    .handler   = events_handler,
    .user_ctx  = "Events Connected!\0"
};
So far this works, and the client then tries to reconnect every 3 seconds or so by calling ip-adress/events.
However, the client never receives an answer. Here's my events sending code (within main()):

Code: Select all

    /* Start the server for the first time */
    server = start_webserver();

    while (server) {

	ESP_LOGI(TAG, "mySocketFD: '%d'", mySocketFD);
	if (mySocketFD > 0) {
   	    char data[] = "retry: 10000\nid: 12345\nevent: complete\ndata: Test\n\n\n\n\0";
	    httpd_ws_frame_t ws_pkt = {
	    .payload = (uint8_t *)data,
            .type = HTTPD_WS_TYPE_TEXT,
            .len = strlen(data),
            .final = true,
        	}; 

  	    ESP_LOGI(TAG, "Sending, length %d", ws_pkt.len);
            ESP_ERROR_CHECK(httpd_ws_send_frame_async(mySocketHD, mySocketFD, &ws_pkt));
	}

        sleep(5);
    }
I've tried a huge number of versions of this, but with no success - the client keeps reconnecting every 3 seconds. One thing I'm not sure about is the first parameter of httpd_ws_send_frame_async: Should I use the server (i.e. the pointer to the server here)?
I know that I'll have to manage multiple clients etc. later, but before that I have to get the event stream text to one client somehow.

Could somebody please help?`

Thanks a lot, cheers,
Max

MicroController
Posts: 1385
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Implementation of Server-Sent Events (SSE) in ESP-IDF

Postby MicroController » Sun Feb 04, 2024 3:04 am

You can't use httpd_resp_send for this. You have to send everything via httpd_socket_send(), including the HTTP response headers, like

Code: Select all

static constexpr std::string_view RESPONSE {
	"HTTP/1.1 200 OK\r\n"
	"Cache-Control: no-store\r\n"
	"Content-Type: text/event-stream\r\n"
	"\r\n"
	"retry: 20000\r\n"
	"\r\n"};

Maximilian22
Posts: 4
Joined: Mon Jan 29, 2024 11:58 am

Re: Implementation of Server-Sent Events (SSE) in ESP-IDF

Postby Maximilian22 » Sun Feb 04, 2024 7:48 pm

@Microcontroller Many thanks a lot for your help!

However, I don't get it working (with Arduino IDE I had no problems with SSE).
The html site is transferred and executed, the /events URI gets called, and the browser receives an OK 200. But then nothings else works - the ESP32-S3 keeps sending 127 chars, but the html code keeps reconnecting every 3-5 seconds. I've tried all kinds of event handlers in jscript (onmessage etc.), but with no effect.

Here's my last version of the code, if anybody would like (or be so kind) to check it. I'm running it on an ESP32-S3 DevkitM:

htmlCode.h

Code: Select all

const char index_html[] = R"rawliteral(
<!DOCTYPE HTML>
<html>
<header>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Eventserver</title>
</header>
<body>
<hr>
<h1> Test Async Event Source</h1>
<br>
<p>Complete State: <span id="target"></span></p>
<br>
<hr>
<script>
if (!!window.EventSource) {
 console.log("New events source!");
 var source = new EventSource('/events');

 source.addEventListener('complete', function(e) {
  document.getElementById("target").innerHTML = e.data; 
  console.log("Event message received!");
 }, false);

}
</script>
</body>
</html>)rawliteral";
and the main.c code:

Code: Select all

#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <sys/param.h>
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "protocol_examples_utils.h"
#include "esp_tls_crypto.h"
#include <esp_http_server.h>
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_tls.h"

#include <esp_wifi.h>
#include <esp_system.h>
#include "nvs_flash.h"
#include "esp_eth.h"

#include "htmlCode.h"

#define EXAMPLE_HTTP_QUERY_KEY_MAX_LEN  (64)

static const char *TAG = "example";
static int mySocketFD = 0;		// socket descriptor, to send SSE messages
static httpd_handle_t mySocketHD;

/* An HTTP GET handler */
static esp_err_t hello_get_handler(httpd_req_t *req)
{
    httpd_resp_send(req, index_html, HTTPD_RESP_USE_STRLEN);
    return ESP_OK;
}

static const httpd_uri_t hello = {
    .uri       = "/hello",
    .method    = HTTP_GET,
    .handler   = hello_get_handler,
};

//-------------------------------------------------------------------------------------------------------------
static esp_err_t events_handler(httpd_req_t *req)
{
    mySocketHD = req->handle;
    ESP_LOGI(TAG, "Inside events handler");
    mySocketFD = httpd_req_to_sockfd(req);
    ESP_LOGI(TAG, "Socket FD: %d", mySocketFD);

    httpd_resp_set_status(req, HTTPD_200);
    httpd_resp_set_type(req, "text/event-stream");
    httpd_resp_set_hdr(req, "Cache-Control", "no-cache");
    httpd_resp_set_hdr(req, "Connection", "keep-alive");

    const char myResp[] = "events request response";
    httpd_resp_send(req, myResp, HTTPD_RESP_USE_STRLEN);

    return ESP_OK;
}

static const httpd_uri_t events = {
    .uri       = "/events",
    .method    = HTTP_GET,
    .handler   = events_handler,
    .user_ctx  = "Events Connected!"
};
//------------------------------------------------------------------------------------------------------------------------------------
esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err)
{
    httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Some 404 error message");
    return ESP_FAIL;
}

static httpd_handle_t start_webserver(void)
{
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.lru_purge_enable = true;

    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK) {
        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &hello);
	httpd_register_uri_handler(server, &events);
        return server;
    }

    ESP_LOGI(TAG, "Error starting server!");
    return NULL;
}

static esp_err_t stop_webserver(httpd_handle_t server)
{
    return httpd_stop(server);
}

static void disconnect_handler(void* arg, esp_event_base_t event_base,
                               int32_t event_id, void* event_data)
{
    httpd_handle_t* server = (httpd_handle_t*) arg;
    if (*server) {
        ESP_LOGI(TAG, "Stopping webserver");
        if (stop_webserver(*server) == ESP_OK) {
            *server = NULL;
        } else {
            ESP_LOGE(TAG, "Failed to stop http server");
        }
    }
}

static void connect_handler(void* arg, esp_event_base_t event_base,
                            int32_t event_id, void* event_data)
{
    httpd_handle_t* server = (httpd_handle_t*) arg;
    if (*server == NULL) {
        ESP_LOGI(TAG, "Starting webserver");
        *server = start_webserver();
    }
}

void app_main(void)
{
    static httpd_handle_t server = NULL;

    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    ESP_ERROR_CHECK(example_connect());

    /* Register event handlers to stop the server when Wi-Fi or Ethernet is disconnected,
     * and re-start it upon connection.
     */
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server));
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server));

    /* Start the server for the first time */
    server = start_webserver();
	
    
    #define HTTPD_HDR_STR "HTTP/1.1 200 OK\r\n" "Cache-Control: no-store\r\n" "Content-Type: text/event-stream\r\n" \
	"\r\n" "retry: 20000\r\n" "event: complete\r\n" "data: sentEvent\r\n" "\r\n"

    while (server) {
	char buf[250];
	ESP_LOGI(TAG, "Inside loop, mySocketFD: '%d'", mySocketFD);

	if (mySocketFD > 0) {
  	snprintf(buf, sizeof(buf), HTTPD_HDR_STR);
	 ESP_LOGI(TAG, "Sending!");
	 ESP_LOGI(TAG,  "%d", (unsigned int) httpd_socket_send(mySocketHD, mySocketFD, buf, strlen(buf), 0));
	}

        sleep(5);
    }
}
Cheers,
Max

393877719
Posts: 10
Joined: Wed Jun 13, 2018 5:52 am

Re: Implementation of Server-Sent Events (SSE) in ESP-IDF

Postby 393877719 » Mon Feb 05, 2024 8:27 am

Is there any progress on this project? I also want to know how to transitioning a SSE project from the Arduino framework to ESP-IDF.
I tried using httpd_socket_send() and also failed, I don't know what I missed.

Code: Select all

httpd_handle_t mySocketHD;
int mySocketFD;
char sse_buf[128] = {'d', 'a', 't', 'a', ':'};
/* This handler gets the present value of the accumulator */
static esp_err_t adder_get_handler(httpd_req_t *req)
{
    sse_buf[126]='\n';
    sse_buf[127]='\n';

    mySocketHD = req->handle;
    mySocketFD = httpd_req_to_sockfd(req);

    char *buf="HTTP/1.1 200 OK\\r\\nCache-Control: no-store\\r\\nContent-Type: text/event-stream\\r\\n\\r\\nretry: 20000\\r\\n\\r\\n";
    httpd_socket_send(mySocketHD,mySocketFD,buf,sizeof(buf),0);
    return ESP_OK;
}
and I use httpd_socket_send() in the app_main() for periodic sending.

Code: Select all

if(mySocketFD>0)
{
      httpd_socket_send(mySocketHD, mySocketFD, sse_buf, sizeof(sse_buf), 0);
}

MicroController
Posts: 1385
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Implementation of Server-Sent Events (SSE) in ESP-IDF

Postby MicroController » Mon Feb 05, 2024 9:35 am

Code: Select all

"...200 OK\\r\\nCache-..."
Too many "\". Use "\r\n", not "\\r\\n".

Code: Select all

httpd_socket_send(mySocketHD,mySocketFD,buf,sizeof(buf),0);
sizeof(buf) is the size of the pointer buf, i.e. 4. Not what you want. Use strlen(buf) instead.

Code: Select all

char sse_buf[128] = {'d', 'a', 't', 'a', ':'};
...
    sse_buf[126]='\n';
    sse_buf[127]='\n';
1. you need to end each line with "\r\n", and an additional "\r\n" between events.
2. your sse_buf is 128 chars long, sse_buf[5]...sse_buf[125] are 0, then comes "\n\n". Use something like char sse_buf[128] = "data: something\r\n\r\n", and send strlen(sse_buf) bytes, not sizeof(sse_buf).

You may also want to use the 'developer tools' feature of your browser to see what requests are sent, what the responses look like, and if the browser can process them as valid HTTP responses.

393877719
Posts: 10
Joined: Wed Jun 13, 2018 5:52 am

Re: Implementation of Server-Sent Events (SSE) in ESP-IDF

Postby 393877719 » Mon Feb 05, 2024 1:38 pm

Yes, according to your guidance, it has been successful, thank you very much!@MicroController
Here is my test code(you need set ssid and password in line 256 and 257):

Code: Select all

/* 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 "lwip/err.h"
#include "lwip/sys.h"
#include <esp_http_server.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_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY

#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

static const char *TAG = "wifi station";

static int s_retry_num = 0;

const static char html_str[] = "<!DOCTYPE html>"
                               "<html lang='en'>"
                               "<head>"
                               "<meta charset='UTF-8'>"
                               "<meta name='viewport' content='width=device-width, initial-scale=1.0'>"
                               "<title>ESP-IDF SSE Demo</title>"
                               "</head>"
                               "<body>"
                               "<div id='target'>SSE Test</div> "
                               "</body>"
                               "<script>"
                               "var source = new EventSource('/sse');"
                               "source.addEventListener('message', function(e) {"
                               "document.getElementById('target').innerHTML = e.data;"
                               "}, false);"
                               "</script>"
                               "</html>";
const static char sse_resp[] = "HTTP/1.1 200 OK\r\n"
                               "Cache-Control: no-store\r\n"
                               "Content-Type: text/event-stream\r\n"
                               "\r\n"
                               "retry: 20000\r\n"
                               "\r\n";

httpd_handle_t mySocketHD;
int mySocketFD;
/* An HTTP GET handler */
static esp_err_t hello_get_handler(httpd_req_t *req)
{
    char *buf;
    size_t buf_len;

    /* Get header value string length and allocate memory for length + 1,
     * extra byte for null termination */
    buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1;
    if (buf_len > 1)
    {
        buf = malloc(buf_len);
        /* Copy null terminated value string into buffer */
        if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK)
        {
            ESP_LOGI(TAG, "Found header => Host: %s", buf);
        }
        free(buf);
    }
    if (0 == strcmp(req->uri, "/"))
    {
        ESP_LOGW(TAG, "/");
        /* Set some custom headers */
        httpd_resp_set_hdr(req, "Connection", "keep-alive");
        httpd_resp_set_type(req, "text/html; charset=UTF-8");
        /* Send response with custom headers and body set as the
         * string passed in user context*/
        httpd_resp_send(req, html_str, strlen(html_str));
    }
    else if (0 == strcmp(req->uri, "/favicon.ico"))
    {
        ESP_LOGW(TAG, "favicon");
        httpd_resp_send(req, html_str, 0);
    }
    else if (0 == strcmp(req->uri, "/sse"))
    {
        ESP_LOGW(TAG, "sse");
        mySocketHD = req->handle;
        mySocketFD = httpd_req_to_sockfd(req);
        // char buf[50];
        // snprintf(buf, sizeof(buf), "data:%d\r\n", cnt++);
        httpd_socket_send(mySocketHD, mySocketFD, sse_resp, sizeof(sse_resp), 0);
    }

    /* After sending the HTTP response the old HTTP request
     * headers are lost. Check if HTTP request headers can be read now. */
    if (httpd_req_get_hdr_value_len(req, "Host") == 0)
    {
        ESP_LOGI(TAG, "Request headers lost");
    }
    return ESP_OK;
}

static const httpd_uri_t main = {
    .uri = "/",
    .method = HTTP_GET,
    .handler = hello_get_handler,
};

static const httpd_uri_t ico = {
    .uri = "/favicon.ico",
    .method = HTTP_GET,
    .handler = hello_get_handler,
};
static const httpd_uri_t sse = {
    .uri = "/sse",
    .method = HTTP_GET,
    .handler = hello_get_handler,
};

static httpd_handle_t start_webserver(void)
{
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
#if CONFIG_IDF_TARGET_LINUX
    // Setting port as 8001 when building for Linux. Port 80 can be used only by a priviliged user in linux.
    // So when a unpriviliged user tries to run the application, it throws bind error and the server is not started.
    // Port 8001 can be used by an unpriviliged user as well. So the application will not throw bind error and the
    // server will be started.
    config.server_port = 8001;
#endif // !CONFIG_IDF_TARGET_LINUX
    config.lru_purge_enable = true;

    // Start the httpd server
    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK)
    {
        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &main);
        httpd_register_uri_handler(server, &ico);
        httpd_register_uri_handler(server, &sse);
#if CONFIG_EXAMPLE_BASIC_AUTH
        httpd_register_basic_auth(server);
#endif
        return server;
    }

    ESP_LOGI(TAG, "Error starting server!");
    return NULL;
}

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_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 = "",
            .password = "",
            /* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (pasword 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");
        start_webserver();
    }
    else if (bits & WIFI_FAIL_BIT)
    {
        ESP_LOGI(TAG, "Failed to connect to ap");
    }
    else
    {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }
}
const static char *sse_format = "data:%d\r\n\r\n";
uint8_t cnt = 0;
void app_main(void)
{
    char sse_buf[50];
    // 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();

    while (1)
    {
        if (mySocketFD > 0)
        {
            snprintf(sse_buf, sizeof(sse_buf), sse_format, cnt++);
            httpd_socket_send(mySocketHD, mySocketFD, sse_buf, strlen(sse_buf), 0);
        }
        vTaskDelay(1000);
    }
}


Maximilian22
Posts: 4
Joined: Mon Jan 29, 2024 11:58 am

Re: Implementation of Server-Sent Events (SSE) in ESP-IDF

Postby Maximilian22 » Mon Feb 05, 2024 9:13 pm

@Microcontroller: Thank you very much for the instructions, my code is now working as well!
Perfect, cheers,
Max

Who is online

Users browsing this forum: Baidu [Spider], Bing [Bot] and 120 guests