Switch HTTP Server

This commit is contained in:
iranl
2024-08-26 21:47:10 +02:00
parent d3c3589233
commit ca9c2feebc
234 changed files with 20090 additions and 8061 deletions

View File

@@ -0,0 +1,4 @@
build/
sdkconfig
sdkconfig.old
components/

View File

@@ -0,0 +1,19 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
if(DEFINED ENV{HTTP_PATH})
set(HTTP_PATH $ENV{HTTP_PATH})
else()
#these both work
set(HTTP_PATH "../../")
#set(HTTP_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../)
#this does not work for me...
#set(HTTP_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../PsychicHttp)
endif(DEFINED ENV{HTTP_PATH})
set(EXTRA_COMPONENT_DIRS ${HTTP_PATH})
project(PsychicHttp_IDF)

View File

@@ -0,0 +1,7 @@
# PsychicHttp - ESP IDF Example
* Download and install [ESP IDF 4.4.7](https://github.com/espressif/esp-idf/releases/tag/v4.4.7) (or later version)
* Clone the project: ```git clone --recursive git@github.com:hoeken/PsychicHttp.git```
* Run build command: ```cd PsychicHttp/examples/esp-idf``` and then ```idf.py build```
* Flash the LittleFS filesystem: ```esptool.py write_flash --flash_mode dio --flash_freq 40m --flash_size 4MB 0x317000 build/littlefs.bin```
* Flash the app firmware: ```idf.py flash monitor``` and visit the IP address shown in the console with a web browser.
* Learn more about [Arduino as ESP-IDF Component](https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html)

View File

@@ -0,0 +1 @@
Custom text file.

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDKzCCAhOgAwIBAgIUBxM3WJf2bP12kAfqhmhhjZWv0ukwDQYJKoZIhvcNAQEL
BQAwJTEjMCEGA1UEAwwaRVNQMzIgSFRUUFMgc2VydmVyIGV4YW1wbGUwHhcNMTgx
MDE3MTEzMjU3WhcNMjgxMDE0MTEzMjU3WjAlMSMwIQYDVQQDDBpFU1AzMiBIVFRQ
UyBzZXJ2ZXIgZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ALBint6nP77RCQcmKgwPtTsGK0uClxg+LwKJ3WXuye3oqnnjqJCwMEneXzGdG09T
sA0SyNPwrEgebLCH80an3gWU4pHDdqGHfJQa2jBL290e/5L5MB+6PTs2NKcojK/k
qcZkn58MWXhDW1NpAnJtjVniK2Ksvr/YIYSbyD+JiEs0MGxEx+kOl9d7hRHJaIzd
GF/vO2pl295v1qXekAlkgNMtYIVAjUy9CMpqaQBCQRL+BmPSJRkXBsYk8GPnieS4
sUsp53DsNvCCtWDT6fd9D1v+BB6nDk/FCPKhtjYOwOAZlX4wWNSZpRNr5dfrxKsb
jAn4PCuR2akdF4G8WLUeDWECAwEAAaNTMFEwHQYDVR0OBBYEFMnmdJKOEepXrHI/
ivM6mVqJgAX8MB8GA1UdIwQYMBaAFMnmdJKOEepXrHI/ivM6mVqJgAX8MA8GA1Ud
EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADiXIGEkSsN0SLSfCF1VNWO3
emBurfOcDq4EGEaxRKAU0814VEmU87btIDx80+z5Dbf+GGHCPrY7odIkxGNn0DJY
W1WcF+DOcbiWoUN6DTkAML0SMnp8aGj9ffx3x+qoggT+vGdWVVA4pgwqZT7Ybntx
bkzcNFW0sqmCv4IN1t4w6L0A87ZwsNwVpre/j6uyBw7s8YoJHDLRFT6g7qgn0tcN
ZufhNISvgWCVJQy/SZjNBHSpnIdCUSJAeTY2mkM4sGxY0Widk8LnjydxZUSxC3Nl
hb6pnMh3jRq4h0+5CZielA4/a+TdrNPv/qok67ot/XJdY3qHCCd8O2b14OVq9jo=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwYp7epz++0QkH
JioMD7U7BitLgpcYPi8Cid1l7snt6Kp546iQsDBJ3l8xnRtPU7ANEsjT8KxIHmyw
h/NGp94FlOKRw3ahh3yUGtowS9vdHv+S+TAfuj07NjSnKIyv5KnGZJ+fDFl4Q1tT
aQJybY1Z4itirL6/2CGEm8g/iYhLNDBsRMfpDpfXe4URyWiM3Rhf7ztqZdveb9al
3pAJZIDTLWCFQI1MvQjKamkAQkES/gZj0iUZFwbGJPBj54nkuLFLKedw7DbwgrVg
0+n3fQ9b/gQepw5PxQjyobY2DsDgGZV+MFjUmaUTa+XX68SrG4wJ+DwrkdmpHReB
vFi1Hg1hAgMBAAECggEAaTCnZkl/7qBjLexIryC/CBBJyaJ70W1kQ7NMYfniWwui
f0aRxJgOdD81rjTvkINsPp+xPRQO6oOadjzdjImYEuQTqrJTEUnntbu924eh+2D9
Mf2CAanj0mglRnscS9mmljZ0KzoGMX6Z/EhnuS40WiJTlWlH6MlQU/FDnwC6U34y
JKy6/jGryfsx+kGU/NRvKSru6JYJWt5v7sOrymHWD62IT59h3blOiP8GMtYKeQlX
49om9Mo1VTIFASY3lrxmexbY+6FG8YO+tfIe0tTAiGrkb9Pz6tYbaj9FjEWOv4Vc
+3VMBUVdGJjgqvE8fx+/+mHo4Rg69BUPfPSrpEg7sQKBgQDlL85G04VZgrNZgOx6
pTlCCl/NkfNb1OYa0BELqWINoWaWQHnm6lX8YjrUjwRpBF5s7mFhguFjUjp/NW6D
0EEg5BmO0ePJ3dLKSeOA7gMo7y7kAcD/YGToqAaGljkBI+IAWK5Su5yldrECTQKG
YnMKyQ1MWUfCYEwHtPvFvE5aPwKBgQDFBWXekpxHIvt/B41Cl/TftAzE7/f58JjV
MFo/JCh9TDcH6N5TMTRS1/iQrv5M6kJSSrHnq8pqDXOwfHLwxetpk9tr937VRzoL
CuG1Ar7c1AO6ujNnAEmUVC2DppL/ck5mRPWK/kgLwZSaNcZf8sydRgphsW1ogJin
7g0nGbFwXwKBgQCPoZY07Pr1TeP4g8OwWTu5F6dSvdU2CAbtZthH5q98u1n/cAj1
noak1Srpa3foGMTUn9CHu+5kwHPIpUPNeAZZBpq91uxa5pnkDMp3UrLIRJ2uZyr8
4PxcknEEh8DR5hsM/IbDcrCJQglM19ZtQeW3LKkY4BsIxjDf45ymH407IQKBgE/g
Ul6cPfOxQRlNLH4VMVgInSyyxWx1mODFy7DRrgCuh5kTVh+QUVBM8x9lcwAn8V9/
nQT55wR8E603pznqY/jX0xvAqZE6YVPcw4kpZcwNwL1RhEl8GliikBlRzUL3SsW3
q30AfqEViHPE3XpE66PPo6Hb1ymJCVr77iUuC3wtAoGBAIBrOGunv1qZMfqmwAY2
lxlzRgxgSiaev0lTNxDzZkmU/u3dgdTwJ5DDANqPwJc6b8SGYTp9rQ0mbgVHnhIB
jcJQBQkTfq6Z0H6OoTVi7dPs3ibQJFrtkoyvYAbyk36quBmNRjVh6rc8468bhXYr
v/t+MeGJP/0Zw8v/X2CFll96
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PsychicHTTP SoftAP Demo</title>
<link rel="icon" href="./favicon.ico" type="image/x-icon">
</head>
<body>
<main>
<h1>SoftAP Demo</h1>
<p>You are connected to the ESP in SoftAP mode.</p>
</main>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -0,0 +1,236 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PsychicHTTP Demo</title>
<link rel="icon" href="./favicon.ico" type="image/x-icon">
</head>
<body>
<main>
<h1>Basic Request Examples</h1>
<ul>
<li><a href="/api?foo=bar">API Call</a></li>
<li><a href="/redirect">Redirection</a></li>
<li><a href="/auth-digest">Authentication (Digest)</a></li>
<li><a href="/auth-basic">Authentication (Basic)</a></li>
<li><a href="/cookies">Cookies Demo</a></li>
<li><a href="/404">404</a></li>
</ul>
<h1>Static Serving</h1>
<p>
<a href="/alien.png"><img width="60" src="/alien.png"></a>
<a href="/img/request_flow.png"><img width="60" src="/img/request_flow.png"></a>
</p>
<p><a href="/myfile.txt">Text File</a></p>
<h1>Simple POST Form</h1>
<form action="/post" method="post">
<label for="param1">Parameter 1:</label>
<input type="text" id="param1" name="param1" value="Parameter 1">
<br>
<label for="param2">Parameter 2:</label>
<input type="text" id="param2" name="param2" value="Parameter 2">
<br>
<input type="submit" value="Submit">
</form>
<h1>Basic File Upload</h1>
<table border="0">
<tr>
<td>
<label for="newfile">Upload a file</label>
</td>
<td colspan="2">
<input id="newfile" type="file" onchange="setpath()" style="width:100%;">
</td>
</tr>
<tr>
<td>
<label for="filepath">Set path on server</label>
</td>
<td>
<input id="filepath" type="text" style="width:100%;">
</td>
</tr>
<tr>
<td>
<button id="upload" type="button" onclick="upload()">Upload</button>
</td>
</tr>
</table>
<script>
function setpath() {
var default_path = document.getElementById("newfile").files[0].name;
document.getElementById("filepath").value = default_path;
}
function upload() {
var filePath = document.getElementById("filepath").value;
var upload_path = "/upload/" + filePath;
var fileInput = document.getElementById("newfile").files;
/* Max size of an individual file. Make sure this
* value is same as that set in file_server.c */
var MAX_FILE_SIZE = 2048*1024;
var MAX_FILE_SIZE_STR = "2MB";
if (fileInput.length == 0) {
alert("No file selected!");
} else if (filePath.length == 0) {
alert("File path on server is not set!");
} else if (filePath.indexOf(' ') >= 0) {
alert("File path on server cannot have spaces!");
} else if (filePath[filePath.length-1] == '/') {
alert("File name not specified after path!");
} else if (fileInput[0].size > 200*1024) {
alert("File size must be less than 200KB!");
} else {
document.getElementById("newfile").disabled = true;
document.getElementById("filepath").disabled = true;
document.getElementById("upload").disabled = true;
var file = fileInput[0];
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4) {
if (xhttp.status == 200) {
document.open();
document.write(xhttp.responseText);
document.close();
} else if (xhttp.status == 0) {
alert("Server closed the connection abruptly!");
location.reload()
} else {
alert(xhttp.status + " Error!\n" + xhttp.responseText);
location.reload()
}
}
};
xhttp.open("POST", upload_path, true);
xhttp.send(file);
}
}
</script>
<h1>Multipart POST Form</h1>
<form action="/multipart" method="post" enctype="multipart/form-data">
<label for="param1">Parameter 1:</label>
<input type="text" id="param1" name="param1" value="Parameter 1">
<br>
<label for="param2">Parameter 2:</label>
<input type="text" id="param2" name="param2" value="Parameter 2">
<br>
<label for="file-upload">File Upload:</label>
<input type="file" id="file-upload" name="file_upload" accept=".txt, .html, .pdf, .png, .jpg, .gif" required>
<br>
<input type="submit" value="Upload File">
</form>
<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 opened!\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>
<h1>EventSource Demo</h1>
<button onclick="eventSourceConnect()">Connect</button>
<div>
<textarea id="eventsource_output" style="width: 50%; height: 250px;"></textarea>
</div>
<script>
const dataElement = document.getElementById('eventsource_output');
function eventSourceConnect()
{
const eventSource = new EventSource('/events');
eventSource.onopen = () => {
dataElement.value += `[connected]\n`;
dataElement.scrollTop = dataElement.scrollHeight;
};
eventSource.addEventListener('millis', (event) => {
let data = event.data.trim()
dataElement.value += `[millis] ${data}\n`;
dataElement.scrollTop = dataElement.scrollHeight;
});
eventSource.onmessage = (event) => {
let data = event.data.trim()
dataElement.value += `[message] ${data}\n`;
dataElement.scrollTop = dataElement.scrollHeight;
};
eventSource.onerror = (error) => {
dataElement.value += `[error] ${error}\n`;
dataElement.scrollTop = dataElement.scrollHeight;
eventSource.close();
};
}
</script>
</main>
</body>
</html>

View File

@@ -0,0 +1 @@
Test File.

View 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

View 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

View File

@@ -0,0 +1,8 @@
# This file was automatically generated for projects
# without default 'CMakeLists.txt' file.
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS ".")
littlefs_create_partition_image(littlefs ${project_dir}/data)

View File

@@ -0,0 +1,510 @@
/*
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 the following libraries (Install via Library Manager)
* ArduinoJson UrlEncode
**********************************************************************************************/
/**********************************************************************************************
* Note: this demo relies on various files to be uploaded on the LittleFS partition
* Follow instructions here: https://randomnerdtutorials.com/esp32-littlefs-arduino-ide/
**********************************************************************************************/
#include <Arduino.h>
#include <WiFi.h>
#include <LittleFS.h>
#include <ArduinoJson.h>
#include <ESPmDNS.h>
#include "secret.h"
#include <PsychicHttp.h>
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE //set this to y in menuconfig to enable SSL
#include <PsychicHttpsServer.h>
#endif
#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;
// Set your SoftAP credentials
const char *softap_ssid = "PsychicHttp";
const char *softap_password = "";
IPAddress softap_ip(10, 0, 0, 1);
//credentials for the /auth-basic and /auth-digest examples
const char *app_user = "admin";
const char *app_pass = "admin";
const char *app_name = "Your App";
//hostname for mdns (psychic.local)
const char *local_hostname = "psychic";
//#define CONFIG_ESP_HTTPS_SERVER_ENABLE to enable ssl
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
bool app_enable_ssl = true;
String server_cert;
String server_key;
#endif
//our main server object
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
PsychicHttpsServer server;
#else
PsychicHttpServer server;
#endif
PsychicWebSocketHandler websocketHandler;
PsychicEventSource eventSource;
bool connectToWifi()
{
//dual client and AP mode
WiFi.mode(WIFI_AP_STA);
// Configure SoftAP
WiFi.softAPConfig(softap_ip, softap_ip, IPAddress(255, 255, 255, 0)); // subnet FF FF FF 00
WiFi.softAP(softap_ssid, softap_password);
IPAddress myIP = WiFi.softAPIP();
Serial.print("SoftAP IP Address: ");
Serial.println(myIP);
Serial.println();
Serial.print("[WiFi] Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
// Auto reconnect is set true as default
// To set auto connect off, use the following function
// WiFi.setAutoReconnect(false);
// 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);
// 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;
}
//look up our keys?
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
if (app_enable_ssl)
{
File fp = LittleFS.open("/server.crt");
if (fp)
{
server_cert = fp.readString();
// Serial.println("Server Cert:");
// Serial.println(server_cert);
}
else
{
Serial.println("server.pem not found, SSL not available");
app_enable_ssl = false;
}
fp.close();
File fp2 = LittleFS.open("/server.key");
if (fp2)
{
server_key = fp2.readString();
// Serial.println("Server Key:");
// Serial.println(server_key);
}
else
{
Serial.println("server.key not found, SSL not available");
app_enable_ssl = false;
}
fp2.close();
}
#endif
//setup server config stuff here
server.config.max_uri_handlers = 20; //maximum number of uri handlers (.on() calls)
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
server.ssl_config.httpd.max_uri_handlers = 20; //maximum number of uri handlers (.on() calls)
//do we want secure or not?
if (app_enable_ssl)
{
server.listen(443, server_cert.c_str(), server_key.c_str());
//this creates a 2nd server listening on port 80 and redirects all requests HTTPS
PsychicHttpServer *redirectServer = new PsychicHttpServer();
redirectServer->config.ctrl_port = 20424; // just a random port different from the default one
redirectServer->listen(80);
redirectServer->onNotFound([](PsychicRequest *request) {
String url = "https://" + request->host() + request->url();
return request->redirect(url.c_str());
});
}
else
server.listen(80);
#else
server.listen(80);
#endif
//serve static files from LittleFS/www on / only to clients on same wifi network
//this is where our /index.html file lives
server.serveStatic("/", LittleFS, "/www/")->setFilter(ON_STA_FILTER);
//serve static files from LittleFS/www-ap on / only to clients on SoftAP
//this is where our /index.html file lives
server.serveStatic("/", LittleFS, "/www-ap/")->setFilter(ON_AP_FILTER);
//serve static files from LittleFS/img on /img
//it's more efficient to serve everything from a single www directory, but this is also possible.
server.serveStatic("/img", LittleFS, "/img/");
//you can also serve single files
server.serveStatic("/myfile.txt", LittleFS, "/custom.txt");
//example callback everytime a connection is opened
server.onOpen([](PsychicClient *client) {
Serial.printf("[http] connection #%u connected from %s\n", client->socket(), client->localIP().toString().c_str());
});
//example callback everytime a connection is closed
server.onClose([](PsychicClient *client) {
Serial.printf("[http] connection #%u closed from %s\n", client->socket(), client->localIP().toString().c_str());
});
//api - json message passed in as post body
server.on("/api", HTTP_POST, [](PsychicRequest *request)
{
//load our JSON request
JsonDocument json;
String body = request->body();
DeserializationError err = deserializeJson(json, body);
//create our response json
JsonDocument output;
output["msg"] = "status";
//did it parse?
if (err)
{
output["status"] = "failure";
output["error"] = err.c_str();
}
else
{
output["status"] = "success";
output["millis"] = millis();
//work with some params
if (json.containsKey("foo"))
{
String foo = json["foo"];
output["foo"] = foo;
}
}
//serialize and return
String jsonBuffer;
serializeJson(output, jsonBuffer);
return request->reply(200, "application/json", jsonBuffer.c_str());
});
//api - parameters passed in via query eg. /api/endpoint?foo=bar
server.on("/ip", HTTP_GET, [](PsychicRequest *request)
{
String output = "Your IP is: " + request->client()->remoteIP().toString();
return request->reply(output.c_str());
});
//api - parameters passed in via query eg. /api/endpoint?foo=bar
server.on("/api", HTTP_GET, [](PsychicRequest *request)
{
//create a response object
JsonDocument output;
output["msg"] = "status";
output["status"] = "success";
output["millis"] = millis();
//work with some params
if (request->hasParam("foo"))
{
String foo = request->getParam("foo")->name();
output["foo"] = foo;
}
//serialize and return
String jsonBuffer;
serializeJson(output, jsonBuffer);
return request->reply(200, "application/json", jsonBuffer.c_str());
});
//how to redirect a request
server.on("/redirect", HTTP_GET, [](PsychicRequest *request)
{
return request->redirect("/alien.png");
});
//how to do basic auth
server.on("/auth-basic", HTTP_GET, [](PsychicRequest *request)
{
if (!request->authenticate(app_user, app_pass))
return request->requestAuthentication(BASIC_AUTH, app_name, "You must log in.");
return request->reply("Auth Basic Success!");
});
//how to do digest auth
server.on("/auth-digest", HTTP_GET, [](PsychicRequest *request)
{
if (!request->authenticate(app_user, app_pass))
return request->requestAuthentication(DIGEST_AUTH, app_name, "You must log in.");
return request->reply("Auth Digest Success!");
});
//example of getting / setting cookies
server.on("/cookies", HTTP_GET, [](PsychicRequest *request)
{
PsychicResponse response(request);
int counter = 0;
if (request->hasCookie("counter"))
{
counter = std::stoi(request->getCookie("counter").c_str());
counter++;
}
char cookie[12];
sprintf(cookie, "%i", counter);
response.setCookie("counter", cookie);
response.setContent(cookie);
return response.send();
});
//example of getting POST variables
server.on("/post", HTTP_POST, [](PsychicRequest *request)
{
String output;
output += "Param 1: " + request->getParam("param1")->value() + "<br/>\n";
output += "Param 2: " + request->getParam("param2")->value() + "<br/>\n";
return request->reply(output.c_str());
});
//you can set up a custom 404 handler.
server.onNotFound([](PsychicRequest *request)
{
return request->reply(404, "text/html", "Custom 404 Handler");
});
//handle a very basic upload as post body
PsychicUploadHandler *uploadHandler = new PsychicUploadHandler();
uploadHandler->onUpload([](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool last) {
File file;
String path = "/www/" + filename;
Serial.printf("Writing %d/%d bytes to: %s\n", (int)index+(int)len, request->contentLength(), path.c_str());
if (last)
Serial.printf("%s is finished. Total bytes: %d\n", path.c_str(), (int)index+(int)len);
//our first call?
if (!index)
file = LittleFS.open(path, FILE_WRITE);
else
file = LittleFS.open(path, FILE_APPEND);
if(!file) {
Serial.println("Failed to open file");
return ESP_FAIL;
}
if(!file.write(data, len)) {
Serial.println("Write failed");
return ESP_FAIL;
}
return ESP_OK;
});
//gets called after upload has been handled
uploadHandler->onRequest([](PsychicRequest *request)
{
String url = "/" + request->getFilename();
String output = "<a href=\"" + url + "\">" + url + "</a>";
return request->reply(output.c_str());
});
//wildcard basic file upload - POST to /upload/filename.ext
server.on("/upload/*", HTTP_POST, uploadHandler);
//a little bit more complicated multipart form
PsychicUploadHandler *multipartHandler = new PsychicUploadHandler();
multipartHandler->onUpload([](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool last) {
File file;
String path = "/www/" + filename;
//some progress over serial.
Serial.printf("Writing %d bytes to: %s\n", (int)len, path.c_str());
if (last)
Serial.printf("%s is finished. Total bytes: %d\n", path.c_str(), (int)index+(int)len);
//our first call?
if (!index)
file = LittleFS.open(path, FILE_WRITE);
else
file = LittleFS.open(path, FILE_APPEND);
if(!file) {
Serial.println("Failed to open file");
return ESP_FAIL;
}
if(!file.write(data, len)) {
Serial.println("Write failed");
return ESP_FAIL;
}
return ESP_OK;
});
//gets called after upload has been handled
multipartHandler->onRequest([](PsychicRequest *request)
{
PsychicWebParameter *file = request->getParam("file_upload");
String url = "/" + file->value();
String output;
output += "<a href=\"" + url + "\">" + url + "</a><br/>\n";
output += "Bytes: " + String(file->size()) + "<br/>\n";
output += "Param 1: " + request->getParam("param1")->value() + "<br/>\n";
output += "Param 2: " + request->getParam("param2")->value() + "<br/>\n";
return request->reply(output.c_str());
});
//wildcard basic file upload - POST to /upload/filename.ext
server.on("/multipart", HTTP_POST, multipartHandler);
//a websocket echo server
websocketHandler.onOpen([](PsychicWebSocketClient *client) {
Serial.printf("[socket] connection #%u connected from %s\n", client->socket(), client->localIP().toString().c_str());
client->sendMessage("Hello!");
});
websocketHandler.onFrame([](PsychicWebSocketRequest *request, httpd_ws_frame *frame) {
Serial.printf("[socket] #%d sent: %s\n", request->client()->socket(), (char *)frame->payload);
return request->reply(frame);
});
websocketHandler.onClose([](PsychicWebSocketClient *client) {
Serial.printf("[socket] connection #%u closed from %s\n", client->socket(), client->localIP().toString().c_str());
});
server.on("/ws", &websocketHandler);
//EventSource server
eventSource.onOpen([](PsychicEventSourceClient *client) {
Serial.printf("[eventsource] connection #%u connected from %s\n", client->socket(), client->localIP().toString().c_str());
client->send("Hello user!", NULL, millis(), 1000);
});
eventSource.onClose([](PsychicEventSourceClient *client) {
Serial.printf("[eventsource] connection #%u closed from %s\n", client->socket(), client->localIP().toString().c_str());
});
server.on("/events", &eventSource);
}
}
unsigned long lastUpdate = 0;
char output[60];
void loop()
{
if (millis() - lastUpdate > 2000)
{
sprintf(output, "Millis: %lu\n", millis());
websocketHandler.sendAll(output);
sprintf(output, "%lu", millis());
eventSource.send(output, "millis", millis(), 0);
lastUpdate = millis();
}
vTaskDelay(1 / portTICK_PERIOD_MS); // Feed WDT
}

View File

@@ -0,0 +1,2 @@
#define WIFI_SSID "WIFI_SSID"
#define WIFI_PASS "WIFI_PASS"

View File

@@ -0,0 +1,7 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x11000, 0xC000
otadata, data, ota, 0x1D000, 0x2000
phy_init, data, phy, 0x1F000, 0x1000
app0, app, ota_0, 0x20000, 0x177000
app1, app, ota_1, 0x1A0000, 0x177000
littlefs, data, spiffs, 0x317000, 0xE1000
1 # Name, Type, SubType, Offset, Size, Flags
2 nvs, data, nvs, 0x11000, 0xC000
3 otadata, data, ota, 0x1D000, 0x2000
4 phy_init, data, phy, 0x1F000, 0x1000
5 app0, app, ota_0, 0x20000, 0x177000
6 app1, app, ota_1, 0x1A0000, 0x177000
7 littlefs, data, spiffs, 0x317000, 0xE1000

View File

@@ -0,0 +1,64 @@
CONFIG_AUTOSTART_ARDUINO=y
# CONFIG_WS2812_LED_ENABLE is not set
CONFIG_FREERTOS_HZ=1000
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
#
# Serial flasher config
#
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
#
# Partition Table
#
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_custom.csv"
#CONFIG_PARTITION_TABLE_FILENAME="partitions_custom.csv"
#CONFIG_PARTITION_TABLE_OFFSET=0xE000
CONFIG_PARTITION_TABLE_MD5=y
#
# ESP HTTPS OTA
#
CONFIG_ESP_HTTPS_OTA_DECRYPT_CB=y
CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y
# end of ESP HTTPS OTA
#
# ESP HTTP client
#
CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=y
CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH=y
# end of ESP HTTP client
#
# HTTP Server
#
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
CONFIG_HTTPD_PURGE_BUF_LEN=32
# CONFIG_HTTPD_LOG_PURGE_DATA is not set
CONFIG_HTTPD_WS_SUPPORT=y
# end of HTTP Server
#
# ESP HTTPS server
#
CONFIG_ESP_HTTPS_SERVER_ENABLE=n
# end of ESP HTTPS server
#
# TLS Key Exchange Methods
#
# 2 option require for arduino Arduino
CONFIG_MBEDTLS_PSK_MODES=y
CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y