summaryrefslogtreecommitdiff
path: root/libraries/ESP_Async_WebServer/examples/ServerSentEvents_PR156/ServerSentEvents_PR156.ino
blob: 928c9ad1c8e3fd8c3fba3a72e6d05b651d14a1b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov

//
// SSE example
//

#include <Arduino.h>
#ifdef ESP32
#include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
#include <RPAsyncTCP.h>
#include <WiFi.h>
#endif

#include <ESPAsyncWebServer.h>

static const char *htmlContent PROGMEM = R"(
<!DOCTYPE html>
<html>
<head>
  <title>Server-Sent Events</title>
  <script>
    if (!!window.EventSource) {
      var source = new EventSource('/events');
      source.onopen = function(e) {
        console.log("Events Connected");
      };
      source.onerror = function(e) {
        if (e.target.readyState != EventSource.OPEN) {
          console.log("Events Disconnected");
        }
        // Uncomment below to prevent the client from proactively establishing a new connection.
        // source.close();
      };
      source.onmessage = function(e) {
        console.log("Message: " + e.data);
      };
      source.addEventListener('heartbeat', function(e) {
        console.log("Heartbeat", e.data);
      }, false);
    }
  </script>
</head>
<body>
  <h1>Open your browser console!</h1>
</body>
</html>
)";

static const size_t htmlContentLength = strlen_P(htmlContent);

static AsyncWebServer server(80);
static AsyncEventSource events("/events");

static volatile size_t connectionCount = 0;
static volatile uint32_t timestampConnected = 0;
static constexpr uint32_t timeoutClose = 15000;

void setup() {
  Serial.begin(115200);

#ifndef CONFIG_IDF_TARGET_ESP32H2
  WiFi.mode(WIFI_AP);
  WiFi.softAP("esp-captive");
#endif

  // curl -v http://192.168.4.1/
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    // need to cast to uint8_t*
    // if you do not, the const char* will be copied in a temporary String buffer
    request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
  });

  events.onConnect([](AsyncEventSourceClient *client) {
    /**
     * @brief: Purpose for a test case: count() function
     * Task watchdog shall be triggered due to a self-deadlock by mutex handling of the AsyncEventSource.
     *
     * E (61642) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
     * E (61642) task_wdt:  - async_tcp (CPU 0/1)
     *
     * Resolve: using recursive_mutex insteads of mutex.
    */
    connectionCount = events.count();

    timestampConnected = millis();
    Serial.printf("SSE Client connected! ID: %" PRIu32 "\n", client->lastId());
    client->send("hello!", NULL, millis(), 1000);
    Serial.printf("Number of connected clients: %u\n", connectionCount);
  });

  events.onDisconnect([](AsyncEventSourceClient *client) {
    connectionCount = events.count();
    Serial.printf("SSE Client disconnected! ID: %" PRIu32 "\n", client->lastId());
    Serial.printf("Number of connected clients: %u\n", connectionCount);
  });

  server.addHandler(&events);

  server.begin();
}

static constexpr uint32_t deltaSSE = 3000;
static uint32_t lastSSE = 0;
static uint32_t lastHeap = 0;

void loop() {
  uint32_t now = millis();
  if (connectionCount > 0) {
    if (now - lastSSE >= deltaSSE) {
      events.send(String("ping-") + now, "heartbeat", now);
      lastSSE = millis();
    }

    /**
     * @brief: Purpose for a test case: close() function
     * Task watchdog shall be triggered due to a self-deadlock by mutex handling of the AsyncEventSource.
     *
     * E (61642) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
     * E (61642) task_wdt:  - async_tcp (CPU 0/1)
     *
     * Resolve: using recursive_mutex insteads of mutex.
    */
    if (now - timestampConnected >= timeoutClose) {
      Serial.printf("SSE Clients close\n");
      events.close();
    }
  }

#ifdef ESP32
  if (now - lastHeap >= 2000) {
    Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
    lastHeap = now;
  }
#endif
}