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
// Shows how to trigger an async client request from a browser request and send the client response back to the browser through websocket
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
#define WIFI_SSID     "IoT"
23
#define WIFI_PASSWORD ""
24
 
25
static AsyncWebServer server(80);
26
static AsyncWebSocketMessageHandler wsHandler;
27
static AsyncWebSocket ws("/ws", wsHandler.eventHandler());
28
 
29
static const char *htmlContent PROGMEM = R"(
30
  <!DOCTYPE html>
31
  <html>
32
  <head>
33
    <title>WebSocket Tunnel Example</title>
34
  </head>
35
  <body>
36
    <h1>WebSocket Tunnel Example</h1>
37
    <div><input type="text" id="url" value="http://www.google.com" /></div>
38
    <div><button onclick='fetch()'>Fetch</button></div>
39
    <div><pre id="response"></pre></div>
40
    <script>
41
      var ws = new WebSocket('/ws');
42
      ws.binaryType = "arraybuffer";
43
      ws.onopen = function() {
44
        console.log("WebSocket connected");
45
      };
46
      ws.onmessage = function(event) {
47
        let uint8array = new Uint8Array(event.data);
48
        let string = new TextDecoder().decode(uint8array);
49
        console.log("WebSocket message: " + string);
50
        document.getElementById("response").innerText += string;
51
      };
52
      ws.onclose = function() {
53
        console.log("WebSocket closed");
54
      };
55
      ws.onerror = function(error) {
56
        console.log("WebSocket error: " + error);
57
      };
58
      function fetch() {
59
        document.getElementById("response").innerText = "";
60
        var url = document.getElementById("url").value;
61
        ws.send(url);
62
        console.log("WebSocket sent: " + url);
63
      }
64
    </script>
65
  </body>
66
  </html>
67
    )";
68
static const size_t htmlContentLength = strlen_P(htmlContent);
69
 
70
void setup() {
71
  Serial.begin(115200);
72
 
73
#if ASYNCWEBSERVER_WIFI_SUPPORTED
74
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
75
  while (WiFi.status() != WL_CONNECTED) {
76
    delay(500);
77
  }
78
  Serial.println("Connected to WiFi!");
79
  Serial.println(WiFi.localIP());
80
#endif
81
 
82
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
83
    request->send(200, "text/html", (const uint8_t *)htmlContent, htmlContentLength);
84
  });
85
 
86
  wsHandler.onMessage([](AsyncWebSocket *server, AsyncWebSocketClient *wsClient, const uint8_t *data, size_t len) {
87
    String url;
88
    String host;
89
    String port;
90
    String path;
91
 
92
    url.concat((const char *)data, len);
93
 
94
    if (!url.startsWith("http://")) {
95
      return;
96
    }
97
 
98
    if (!url.endsWith("/")) {
99
      url += "/";
100
    }
101
 
102
    // Parse the URL to extract the host and port
103
    int start = url.indexOf("://") + 3;
104
    int end = url.indexOf("/", start);
105
    if (end == -1) {
106
      end = url.length();
107
    }
108
    String hostPort = url.substring(start, end);
109
    int colonIndex = hostPort.indexOf(":");
110
    if (colonIndex != -1) {
111
      host = hostPort.substring(0, colonIndex);
112
      port = hostPort.substring(colonIndex + 1);
113
    } else {
114
      host = hostPort;
115
      port = "80";  // Default HTTP port
116
    }
117
    path = url.substring(end);
118
 
119
    Serial.printf("Host: %s\n", host.c_str());
120
    Serial.printf("Port: %s\n", port.c_str());
121
    Serial.printf("Path: %s\n", path.c_str());
122
 
123
    // Ensure client does not get deleted while the websocket holds a reference to it
124
    std::shared_ptr<AsyncClient> *safeAsyncClient = new std::shared_ptr<AsyncClient>(std::make_shared<AsyncClient>());
125
    AsyncClient *asyncClient = safeAsyncClient->get();
126
 
127
    asyncClient->onDisconnect([safeAsyncClient](void *arg, AsyncClient *client) {
128
      Serial.printf("Tunnel disconnected!\n");
129
      delete safeAsyncClient;
130
    });
131
 
132
    // register a callback when an error occurs
133
    // note: onDisconnect also called on error
134
    asyncClient->onError([](void *arg, AsyncClient *client, int8_t error) {
135
      Serial.printf("Tunnel error: %s\n", client->errorToString(error));
136
    });
137
 
138
    // register a callback when data arrives, to accumulate it
139
    asyncClient->onPacket(
140
      [safeAsyncClient](void *arg, AsyncClient *, struct pbuf *pb) {
141
        std::shared_ptr<AsyncClient> safeAsyncClientRef = *safeAsyncClient;  // add a reference
142
        AsyncWebSocketClient *wsClient = (AsyncWebSocketClient *)arg;
143
        Serial.printf("Tunnel received %u bytes\n", pb->len);
144
        AsyncWebSocketSharedBuffer wsBuffer =
145
          AsyncWebSocketSharedBuffer(new std::vector<uint8_t>((uint8_t *)pb->payload, (uint8_t *)pb->payload + pb->len), [=](std::vector<uint8_t> *bufptr) {
146
            delete bufptr;
147
            Serial.printf("ACK %u bytes\n", pb->len);
148
            safeAsyncClientRef->ackPacket(pb);
149
          });
150
        Serial.printf("Tunnel sending %u bytes\n", wsBuffer->size());
151
        Serial.printf("%.*s\n", (int)wsBuffer->size(), wsBuffer->data());
152
        wsClient->binary(std::move(wsBuffer));
153
      },
154
      wsClient
155
    );
156
 
157
    asyncClient->onConnect([=](void *arg, AsyncClient *client) {
158
      Serial.printf("Tunnel connected!\n");
159
 
160
      client->write("GET ");
161
      client->write(path.c_str());
162
      client->write(" HTTP/1.1\r\n");
163
      client->write("Host: ");
164
      client->write(host.c_str());
165
      client->write(":");
166
      client->write(port.c_str());
167
      client->write("\r\n");
168
      client->write("User-Agent: ESP32\r\n");
169
      client->write("Accept: */*\r\n");
170
      client->write("Connection: close\r\n");
171
      client->write("\r\n");
172
    });
173
 
174
    Serial.printf("Fetching: http://%s:%s%s\n", host.c_str(), port.c_str(), path.c_str());
175
 
176
    if (!asyncClient->connect(host.c_str(), port.toInt())) {
177
      Serial.printf("Failed to open tunnel!\n");
178
      delete safeAsyncClient;
179
    }
180
  });
181
 
182
  wsHandler.onConnect([](AsyncWebSocket *server, AsyncWebSocketClient *client) {
183
    Serial.printf("Client %" PRIu32 " connected\n", client->id());
184
    client->binary("WebSocket connected!");
185
  });
186
 
187
  wsHandler.onDisconnect([](AsyncWebSocket *server, uint32_t clientId) {
188
    Serial.printf("Client %" PRIu32 " disconnected\n", clientId);
189
  });
190
 
191
  server.addHandler(&ws);
192
  server.begin();
193
  Serial.println("Server started!");
194
}
195
 
196
static uint32_t lastHeap = 0;
197
 
198
void loop() {
199
  ws.cleanupClients(2);
200
 
201
#ifdef ESP32
202
  uint32_t now = millis();
203
  if (now - lastHeap >= 2000) {
204
    Serial.printf("Uptime: %3lu s, Free heap: %" PRIu32 "\n", millis() / 1000, ESP.getFreeHeap());
205
    lastHeap = now;
206
  }
207
#endif
208
 
209
  delay(500);
210
}