Subversion Repositories ESP8266_P1_Meter

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 raymond 1
// SPDX-License-Identifier: LGPL-3.0-or-later
2
// Copyright 2016-2026 Hristo Gochkov, Mathieu Carbou, Emil Muratov, Will Miles
3
 
4
//
5
// WebSocket example
6
//
7
 
8
#include <Arduino.h>
9
#if defined(ESP32) || defined(LIBRETINY)
10
#include <AsyncTCP.h>
11
#include <WiFi.h>
12
#elif defined(ESP8266)
13
#include <ESP8266WiFi.h>
14
#include <ESPAsyncTCP.h>
15
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
16
#include <RPAsyncTCP.h>
17
#include <WiFi.h>
18
#endif
19
 
20
#include <ESPAsyncWebServer.h>
21
 
22
static const char *htmlContent PROGMEM = R"(
23
<!DOCTYPE html>
24
<html>
25
<head>
26
  <title>WebSocket</title>
27
</head>
28
<body>
29
  <h1>WebSocket Example</h1>
30
  <p>Open your browser console!</p>
31
  <input type="text" id="message" placeholder="Type a message">
32
  <button onclick='sendMessage()'>Send</button>
33
  <script>
34
    var ws = new WebSocket('ws://192.168.4.1/ws');
35
    ws.onopen = function() {
36
      console.log("WebSocket connected");
37
    };
38
    ws.onmessage = function(event) {
39
      console.log("WebSocket message: " + event.data);
40
    };
41
    ws.onclose = function() {
42
      console.log("WebSocket closed");
43
    };
44
    ws.onerror = function(error) {
45
      console.log("WebSocket error: " + error);
46
    };
47
    function sendMessage() {
48
      var message = document.getElementById("message").value;
49
      ws.send(message);
50
      console.log("WebSocket sent: " + message);
51
    }
52
    setInterval(function() {
53
      if (ws.readyState === WebSocket.OPEN) {
54
        ws.send("msg from browser");
55
      }
56
    }, 1000);
57
  </script>
58
</body>
59
</html>
60
  )";
61
static const size_t htmlContentLength = strlen_P(htmlContent);
62
 
63
static AsyncWebServer server(80);
64
static AsyncWebSocket ws("/ws");
65
 
66
void setup() {
67
  Serial.begin(115200);
68
 
69
#if ASYNCWEBSERVER_WIFI_SUPPORTED
70
  WiFi.mode(WIFI_AP);
71
  WiFi.softAP("esp-captive");
72
#endif
73
 
74
  // serves root html page
75
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
76
    request->send(200, "text/html", (const uint8_t *)htmlContent, htmlContentLength);
77
  });
78
 
79
  //
80
  // Run in terminal 1: websocat ws://192.168.4.1/ws => should stream data
81
  // Run in terminal 2: websocat ws://192.168.4.1/ws => should stream data
82
  // Run in terminal 3: websocat ws://192.168.4.1/ws => should fail:
83
  //
84
  // To send a message to the WebSocket server (\n at the end):
85
  // > echo "Hello!" | websocat ws://192.168.4.1/ws
86
  //
87
  // Generates 2001 characters (\n at the end) to cause a fragmentation (over TCP MSS):
88
  // > openssl rand -hex 1000 | websocat ws://192.168.4.1/ws
89
  //
90
  ws.onEvent([](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
91
    (void)len;
92
 
93
    if (type == WS_EVT_CONNECT) {
94
      ws.textAll("new client connected");
95
      Serial.println("ws connect");
96
      client->ping();
97
 
98
    } else if (type == WS_EVT_DISCONNECT) {
99
      ws.textAll("client disconnected");
100
      Serial.println("ws disconnect");
101
 
102
    } else if (type == WS_EVT_ERROR) {
103
      Serial.println("ws error");
104
 
105
    } else if (type == WS_EVT_PONG) {
106
      Serial.println("ws pong");
107
 
108
    } else if (type == WS_EVT_DATA) {
109
      AwsFrameInfo *info = (AwsFrameInfo *)arg;
110
      Serial.printf(
111
        "index: %" PRIu64 ", len: %" PRIu64 ", final: %" PRIu8 ", opcode: %" PRIu8 ", framelen: %d\n", info->index, info->len, info->final,
112
        info->message_opcode, len
113
      );
114
 
115
      // complete frame
116
      if (info->final && info->index == 0 && info->len == len) {
117
        if (info->message_opcode == WS_TEXT) {
118
          Serial.printf("ws text: %s\n", (char *)data);
119
          client->ping();
120
          // Also send a message in the message queue when we get one
121
          ws.textAll("Message received: " + String((char *)data));
122
        }
123
 
124
      } else {
125
        // incomplete frame
126
        if (info->index == 0) {
127
          if (info->num == 0) {
128
            Serial.printf(
129
              "ws[%s][%" PRIu32 "] [%" PRIu32 "] MSG START %s\n", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary"
130
            );
131
          }
132
          Serial.printf("ws[%s][%" PRIu32 "] [%" PRIu32 "] FRAME START len=%" PRIu64 "\n", server->url(), client->id(), info->num, info->len);
133
        }
134
 
135
        Serial.printf(
136
          "ws[%s][%" PRIu32 "] [%" PRIu32 "] FRAME %s, index=%" PRIu64 ", len=%" PRIu32 "]: ", server->url(), client->id(), info->num,
137
          (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, (uint32_t)len
138
        );
139
 
140
        if (info->message_opcode == WS_TEXT) {
141
          Serial.printf("%s\n", (char *)data);
142
        } else {
143
          for (size_t i = 0; i < len; i++) {
144
            Serial.printf("%02x ", data[i]);
145
          }
146
          Serial.printf("\n");
147
        }
148
 
149
        if ((info->index + len) == info->len) {
150
          Serial.printf("ws[%s][%" PRIu32 "] [%" PRIu32 "] FRAME END\n", server->url(), client->id(), info->num);
151
 
152
          if (info->final) {
153
            Serial.printf("ws[%s][%" PRIu32 "] [%" PRIu32 "] MSG END\n", server->url(), client->id(), info->num);
154
          }
155
        }
156
      }
157
    }
158
  });
159
 
160
  // shows how to prevent a third WS client to connect
161
  server.addHandler(&ws).addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
162
    // ws.count() is the current count of WS clients: this one is trying to upgrade its HTTP connection
163
    if (ws.count() > 1) {
164
      // if we have 2 clients or more, prevent the next one to connect
165
      request->send(503, "text/plain", "Server is busy");
166
    } else {
167
      // process next middleware and at the end the handler
168
      next();
169
    }
170
  });
171
 
172
  server.addHandler(&ws);
173
 
174
  server.begin();
175
}
176
 
177
#ifdef ESP32
178
static const uint32_t deltaWS = 50;
179
#else
180
static const uint32_t deltaWS = 200;
181
#endif
182
 
183
static uint32_t lastWS = 0;
184
static uint32_t lastHeap = 0;
185
 
186
void loop() {
187
  uint32_t now = millis();
188
 
189
  if (now - lastWS >= deltaWS) {
190
    ws.printfAll("kp:%.4f", (10.0 / 3.0));
191
    lastWS = millis();
192
  }
193
 
194
  if (now - lastHeap >= 5000) {
195
    Serial.printf("Connected clients: %u / %u total\n", ws.count(), ws.getClients().size());
196
 
197
    // this can be called to also set a soft limit on the number of connected clients
198
    ws.cleanupClients(2);  // no more than 2 clients
199
 
200
    String random;
201
    random.reserve(8192);
202
    for (size_t i = 0; i < 8192; i++) {
203
      random += "x";
204
    }
205
    ws.textAll(random);
206
 
207
    // ping twice (2 control frames)
208
    ws.pingAll();
209
    ws.pingAll();
210
 
211
#ifdef ESP32
212
    Serial.printf("Uptime: %3lu s, Free heap: %" PRIu32 ", Min free heap: %" PRIu32 "\n", millis() / 1000, ESP.getFreeHeap(), ESP.getMinFreeHeap());
213
#endif
214
    lastHeap = now;
215
  }
216
}