Switch HTTP Server
This commit is contained in:
8
lib/PsychicHttp/examples/websockets/.gitignore
vendored
Normal file
8
lib/PsychicHttp/examples/websockets/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
.pio
|
||||
.vscode/
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
src/_secret.h
|
||||
lib/PsychicHttp/
|
||||
BIN
lib/PsychicHttp/examples/websockets/data/www/favicon.ico
Normal file
BIN
lib/PsychicHttp/examples/websockets/data/www/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
76
lib/PsychicHttp/examples/websockets/data/www/index.html
Normal file
76
lib/PsychicHttp/examples/websockets/data/www/index.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PsychicHTTP Websocket Demo</title>
|
||||
<link rel="icon" href="./favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Websocket Demo</h1>
|
||||
<input type="text" id="message_input" placeholder="Type your message">
|
||||
<button onclick="sendMessage()">Send Message</button>
|
||||
<button onclick="websocketConnect()">Connect</button>
|
||||
<div>
|
||||
<textarea id="websocket_output" style="width: 50%; height: 250px;"></textarea>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let socket;
|
||||
const outputText = document.getElementById('websocket_output');
|
||||
const messageInput = document.getElementById('message_input');
|
||||
|
||||
function websocketConnect()
|
||||
{
|
||||
// Create a WebSocket connection
|
||||
socket = new WebSocket('ws://' + window.location.host + '/ws');
|
||||
|
||||
// Event handler for when the WebSocket connection is open
|
||||
socket.addEventListener('open', (event) => {
|
||||
outputText.value += `[socket] connection opened!\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
});
|
||||
|
||||
// Event handler for when a message is received from the WebSocket server
|
||||
socket.addEventListener('message', (event) => {
|
||||
// Echo the received message into the output div
|
||||
let data = event.data.trim();
|
||||
outputText.value += `[received] ${data}\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
});
|
||||
|
||||
// Event handler for when an error occurs with the WebSocket connection
|
||||
socket.addEventListener('error', (event) => {
|
||||
let data = event.data.trim();
|
||||
outputText.value += `[error] ${event.data}\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
});
|
||||
|
||||
// Event handler for when the WebSocket connection is closed
|
||||
socket.addEventListener('close', (event) => {
|
||||
outputText.value += `[socket] connection closed!\n`;
|
||||
});
|
||||
}
|
||||
|
||||
// Function to send a message to the WebSocket server
|
||||
function sendMessage() {
|
||||
if (socket.readyState == WebSocket.OPEN) {
|
||||
const message = messageInput.value.trim();
|
||||
if (message) {
|
||||
socket.send(message);
|
||||
messageInput.value = ''; // Clear the input field after sending the message
|
||||
outputText.value += `[sent] ${message}\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outputText.value += `[error] Not Connected\n`;
|
||||
outputText.scrollTop = outputText.scrollHeight;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
39
lib/PsychicHttp/examples/websockets/include/README
Normal file
39
lib/PsychicHttp/examples/websockets/include/README
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||
46
lib/PsychicHttp/examples/websockets/lib/README
Normal file
46
lib/PsychicHttp/examples/websockets/lib/README
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||
25
lib/PsychicHttp/examples/websockets/platformio.ini
Normal file
25
lib/PsychicHttp/examples/websockets/platformio.ini
Normal file
@@ -0,0 +1,25 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder
|
||||
lib_deps =
|
||||
; devmode: with this disabled make a symlink from platformio/lib to the PsychicHttp directory
|
||||
;hoeken/PsychicHttp
|
||||
bblanchon/ArduinoJson
|
||||
board_build.filesystem = littlefs
|
||||
|
||||
[env:default]
|
||||
build_flags =
|
||||
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_WARN
|
||||
225
lib/PsychicHttp/examples/websockets/src/main.cpp
Normal file
225
lib/PsychicHttp/examples/websockets/src/main.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
PsychicHTTP Server 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.
|
||||
*/
|
||||
|
||||
/**********************************************************************************************
|
||||
* Note: this demo relies on various files to be uploaded on the LittleFS partition
|
||||
* PlatformIO -> Build Filesystem Image and then PlatformIO -> Upload Filesystem Image
|
||||
**********************************************************************************************/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <LittleFS.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <esp_sntp.h>
|
||||
#include "_secret.h"
|
||||
#include <PsychicHttp.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
#ifndef WIFI_SSID
|
||||
#error "You need to enter your wifi credentials. Rename secret.h to _secret.h and enter your credentials there."
|
||||
#endif
|
||||
|
||||
//Enter your WIFI credentials in secret.h
|
||||
const char *ssid = WIFI_SSID;
|
||||
const char *password = WIFI_PASS;
|
||||
|
||||
//hostname for mdns (psychic.local)
|
||||
const char *local_hostname = "psychic";
|
||||
|
||||
PsychicHttpServer server;
|
||||
PsychicWebSocketHandler websocketHandler;
|
||||
|
||||
typedef struct {
|
||||
int socket;
|
||||
char *buffer;
|
||||
size_t len;
|
||||
} WebsocketMessage;
|
||||
|
||||
QueueHandle_t wsMessages;
|
||||
|
||||
bool connectToWifi()
|
||||
{
|
||||
Serial.print("[WiFi] Connecting to ");
|
||||
Serial.println(ssid);
|
||||
|
||||
//setup our wifi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
// Will try for about 10 seconds (20x 500ms)
|
||||
int tryDelay = 500;
|
||||
int numberOfTries = 20;
|
||||
|
||||
// Wait for the WiFi event
|
||||
while (true)
|
||||
{
|
||||
switch (WiFi.status())
|
||||
{
|
||||
case WL_NO_SSID_AVAIL:
|
||||
Serial.println("[WiFi] SSID not found");
|
||||
break;
|
||||
case WL_CONNECT_FAILED:
|
||||
Serial.print("[WiFi] Failed - WiFi not connected! Reason: ");
|
||||
return false;
|
||||
break;
|
||||
case WL_CONNECTION_LOST:
|
||||
Serial.println("[WiFi] Connection was lost");
|
||||
break;
|
||||
case WL_SCAN_COMPLETED:
|
||||
Serial.println("[WiFi] Scan is completed");
|
||||
break;
|
||||
case WL_DISCONNECTED:
|
||||
Serial.println("[WiFi] WiFi is disconnected");
|
||||
break;
|
||||
case WL_CONNECTED:
|
||||
Serial.println("[WiFi] WiFi is connected!");
|
||||
Serial.print("[WiFi] IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
Serial.print("[WiFi] WiFi Status: ");
|
||||
Serial.println(WiFi.status());
|
||||
break;
|
||||
}
|
||||
delay(tryDelay);
|
||||
|
||||
if (numberOfTries <= 0)
|
||||
{
|
||||
Serial.print("[WiFi] Failed to connect to WiFi!");
|
||||
// Use disconnect function to force stop trying to connect
|
||||
WiFi.disconnect();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
numberOfTries--;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
|
||||
//prepare our message queue of 10 messages
|
||||
wsMessages = xQueueCreate(10, sizeof(WebsocketMessage));
|
||||
if (wsMessages == 0)
|
||||
Serial.printf("Failed to create queue= %p\n", wsMessages);
|
||||
|
||||
// We start by connecting to a WiFi network
|
||||
// To debug, please enable Core Debug Level to Verbose
|
||||
if (connectToWifi())
|
||||
{
|
||||
//set up our esp32 to listen on the local_hostname.local domain
|
||||
if (!MDNS.begin(local_hostname)) {
|
||||
Serial.println("Error starting mDNS");
|
||||
return;
|
||||
}
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
if(!LittleFS.begin())
|
||||
{
|
||||
Serial.println("LittleFS Mount Failed. Do Platform -> Build Filesystem Image and Platform -> Upload Filesystem Image from VSCode");
|
||||
return;
|
||||
}
|
||||
|
||||
server.listen(80);
|
||||
|
||||
//this is where our /index.html file lives
|
||||
// curl -i http://psychic.local/
|
||||
PsychicStaticFileHandler* handler = server.serveStatic("/", LittleFS, "/www/");
|
||||
|
||||
//a websocket echo server
|
||||
// npm install -g wscat
|
||||
// wscat -c ws://psychic.local/ws
|
||||
websocketHandler.onOpen([](PsychicWebSocketClient *client) {
|
||||
Serial.printf("[socket] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString());
|
||||
client->sendMessage("Hello!");
|
||||
});
|
||||
websocketHandler.onFrame([](PsychicWebSocketRequest *request, httpd_ws_frame *frame)
|
||||
{
|
||||
Serial.printf("[socket] #%d sent: %s\n", request->client()->socket(), (char *)frame->payload);
|
||||
|
||||
//we are allocating memory here, and the worker will free it
|
||||
WebsocketMessage wm;
|
||||
wm.socket = request->client()->socket();
|
||||
wm.len = frame->len;
|
||||
wm.buffer = (char *)malloc(frame->len);
|
||||
|
||||
//did we flame out?
|
||||
if (wm.buffer == NULL)
|
||||
{
|
||||
Serial.printf("Queue message: unable to allocate %d bytes\n", frame->len);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
//okay, copy it over
|
||||
memcpy(wm.buffer, frame->payload, frame->len);
|
||||
|
||||
//try to throw it in our queue
|
||||
if (xQueueSend(wsMessages, &wm, 1) != pdTRUE)
|
||||
{
|
||||
Serial.printf("[socket] queue full #%d\n", wm.socket);
|
||||
|
||||
//free the memory... no worker to do it for us.
|
||||
free(wm.buffer);
|
||||
}
|
||||
|
||||
//send a throttle message if we're full
|
||||
if (!uxQueueSpacesAvailable(wsMessages))
|
||||
return request->reply("Queue Full");
|
||||
|
||||
return ESP_OK;
|
||||
});
|
||||
websocketHandler.onClose([](PsychicWebSocketClient *client) {
|
||||
Serial.printf("[socket] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString());
|
||||
});
|
||||
server.on("/ws", &websocketHandler);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long lastUpdate = 0;
|
||||
char output[60];
|
||||
|
||||
void loop()
|
||||
{
|
||||
//process our websockets outside the callback.
|
||||
WebsocketMessage message;
|
||||
while (xQueueReceive(wsMessages, &message, 0) == pdTRUE)
|
||||
{
|
||||
//make sure our client is still good.
|
||||
PsychicWebSocketClient *client = websocketHandler.getClient(message.socket);
|
||||
if (client == NULL) {
|
||||
Serial.printf("[socket] client #%d bad, bailing\n", message.socket);
|
||||
return;
|
||||
}
|
||||
|
||||
//echo it back to the client.
|
||||
//alternatively, this is where you would deserialize a json message, parse it, and generate a response if needed
|
||||
client->sendMessage(HTTPD_WS_TYPE_TEXT, message.buffer, message.len);
|
||||
|
||||
//make sure to release our memory!
|
||||
free(message.buffer);
|
||||
}
|
||||
|
||||
//send a periodic update to all clients
|
||||
if (millis() - lastUpdate > 2000)
|
||||
{
|
||||
sprintf(output, "Millis: %d\n", millis());
|
||||
websocketHandler.sendAll(output);
|
||||
|
||||
lastUpdate = millis();
|
||||
}
|
||||
}
|
||||
2
lib/PsychicHttp/examples/websockets/src/secret.h
Normal file
2
lib/PsychicHttp/examples/websockets/src/secret.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#define WIFI_SSID "Your_SSID"
|
||||
#define WIFI_PASS "Your_PASS"
|
||||
11
lib/PsychicHttp/examples/websockets/test/README
Normal file
11
lib/PsychicHttp/examples/websockets/test/README
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PlatformIO Test Runner and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||
Reference in New Issue
Block a user