### Handling Wi-Fi Credentials (Standalone Example) Source: https://github.com/hoeken/psychichttp/blob/master/README.md For standalone examples, rename the secrets.h.example file to secrets.h and fill in your Wi-Fi SSID and password. ```bash # standalone example (e.g. examples/pio-arduino) mv examples/pio-arduino/src/secrets.h.example examples/pio-arduino/src/secrets.h # then edit secrets.h and set WIFI_SSID / WIFI_PASS ``` -------------------------------- ### Create PsychicHttp Server Instance Source: https://github.com/hoeken/psychichttp/blob/master/README.md This is the typical server setup. It includes necessary includes, server instantiation, and essential calls within the setup() function such as attaching endpoints and handlers before calling server.begin(). ```cpp #include PsychicHttpServer server; void setup() { //optional low level setup server config stuff here. //server.config is an ESP-IDF httpd_config struct //see: https://docs.espressif.com/projects/esp-idf/en/v4.4.6/esp32/api-reference/protocols/esp_http_server.html#_CPPv412httpd_config //connect to wifi //call server methods to attach endpoints and handlers server.on(...); server.serveStatic(...); server.addHandler(...); //must be called after all server.on() registrations server.begin(); } ``` -------------------------------- ### PsychicEventSource Usage Example Source: https://github.com/hoeken/psychichttp/blob/master/README.md Basic example demonstrating how to set up and use the PsychicEventSource class. ```APIDOC ## PsychicEventSource Basic Usage ### Description Example of initializing and configuring PsychicEventSource. ### Code Example ```cpp // Create our handler (should be global or persistent) PsychicEventSource eventSource; eventSource.onOpen([](PsychicEventSourceClient *client) { Serial.printf("[eventsource] connection #%u connected from %s\n", client->socket(), client->remoteIP().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->remoteIP().toString().c_str()); }); // Attach the handler to the /events endpoint server.on("/events", &eventSource); ``` ``` -------------------------------- ### Alternative Server Initialization Source: https://github.com/hoeken/psychichttp/blob/master/CHANGELOG.md An alternative method to start the server after defining all endpoints. This is a required step for v2.0. ```cpp server.start() ``` -------------------------------- ### Build ESP-IDF Project Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/README.md Navigate to the example directory and build the ESP-IDF project using the idf.py tool. This command compiles the firmware and prepares it for flashing. ```bash cd PsychicHttp/examples/esp-idf idf.py build ``` -------------------------------- ### PlatformIO Installation (Native ESP-IDF Framework) Source: https://github.com/hoeken/psychichttp/blob/master/README.md Install PsychicHttp for native ESP-IDF projects in PlatformIO. Ensure WebSocket support is enabled in your sdkconfig.defaults. ```ini [env:myboard] platform = espressif32 board = ... framework = espidf lib_deps = hoeken/PsychicHttp ``` ```ini CONFIG_HTTPD_WS_SUPPORT=y # CONFIG_MBEDTLS_ROM_MD5 is not set ``` -------------------------------- ### Basic WebSocket Handler Example Source: https://github.com/hoeken/psychichttp/blob/master/README.md Demonstrates setting up and attaching a PsychicWebSocketHandler to a server endpoint. This handler manages WebSocket connections, including opening, receiving frames, and closing. ```cpp //create our handler... note this should be located as a global or somewhere it wont go out of scope and be destroyed. PsychicWebSocketHandler websocketHandler(); websocketHandler.onOpen([](PsychicWebSocketClient *client) { Serial.printf("[socket] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString().c_str()); client->sendMessage("Hello!"); }); websocketHandler.onFrame([](PsychicWebSocketRequest *request, httpd_ws_frame_t *frame) { Serial.printf("[socket] #%d sent: %s\n", request->client()->socket(), (char *)frame->payload); return response->send(frame); }); websocketHandler.onClose([](PsychicWebSocketClient *client) { Serial.printf("[socket] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString().c_str()); }); //attach the handler to /ws. You can then connect to ws://ip.address/ws server.on("/ws", &websocketHandler); ``` -------------------------------- ### Basic PsychicEventSource Setup Source: https://github.com/hoeken/psychichttp/blob/master/README.md Initialize and configure a PsychicEventSource handler to manage Server-Sent Events connections. Attach the handler to a specific server route. Callbacks for client connection and disconnection are defined. ```cpp //create our handler... note this should be located as a global or somewhere it wont go out of scope and be destroyed. PsychicEventSource eventSource; eventSource.onOpen([](PsychicEventSourceClient *client) { Serial.printf("[eventsource] connection #%u connected from %s\n", client->socket(), client->remoteIP().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->remoteIP().toString().c_str()); }); //attach the handler to /events server.on("/events", &eventSource); ``` -------------------------------- ### PlatformIO Installation (Arduino Framework) Source: https://github.com/hoeken/psychichttp/blob/master/README.md Add PsychicHttp to your project using the PlatformIO configuration file. You can specify the latest stable version or use a Git URL for the development version. ```ini [env:myboard] platform = espressif32 board = ... framework = arduino # using the latest stable version lib_deps = hoeken/PsychicHttp # or using GIT Url (the latest development version) lib_deps = https://github.com/hoeken/PsychicHttp ``` -------------------------------- ### Basic Upload Handler Setup Source: https://github.com/hoeken/psychichttp/blob/master/README.md Handles basic uploads where the file is the entire POST body. It defines onUpload and onRequest callbacks to process and respond to file uploads. The wildcard URL '/upload/*' is used to capture filenames. ```cpp //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 = "" + url + ""; return response->send(output.c_str()); }); //wildcard basic file upload - POST to /upload/filename.ext server.on("/upload/*", HTTP_POST, uploadHandler); ``` -------------------------------- ### Main WebSocket Test Execution Source: https://github.com/hoeken/psychichttp/blob/master/examples/pio-arduino/data/www/websocket-test.html Starts the WebSocket test by setting the test duration, determining the correct WebSocket protocol (ws:// or wss://), and establishing a connection to the server. It also disables the start buttons to prevent multiple test initiations. ```javascript function startTest() { if (testRunning) return; console.log("Payload length: " + JSON.stringify(payload).length); document.getElementById('startTestSmall').disabled = true; document.getElementById('startTestBig').disabled = true; document.getElementById('status').innerText = 'Connecting'; const durationInput = document.getElementById('duration').value; const testDuration = parseInt(durationInput) * 1000 || 60000; // default to 60 seconds if invalid // Determine the WebSocket protocol based on the current protocol const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; ws = new WebSocket( `${protocol}${window.location.host}/ws` ); ws.onopen = function () ``` -------------------------------- ### Serving a Template Request with PsychicStreamResponse Source: https://github.com/hoeken/psychichttp/blob/master/README.md An example of serving a web request using PsychicStreamResponse. It sets up a TemplatePrinter with a custom handler and prints template strings with dynamic values. ```C++ // Example serving a request server.on("/template", [](PsychicRequest *request) { PsychicStreamResponse response(request, "text/plain"); response.beginSend(); TemplatePrinter printer(response, templateHandler); printer.println("My ESP has %FREE_HEAP% left. Its lifetime minimum heap is %MIN_FREE_HEAP%."); printer.println("The maximum allocation size is %MAX_ALLOC_HEAP%, and its total size is %HEAP_SIZE%."); printer.println("This is an unhandled parameter: %UNHANDLED_PARAM% and this is an invalid param %INVALID PARAM%."); printer.println("This line finished with %UNFIN"); printer.flush(); return response.endSend(); }); ``` -------------------------------- ### Template Parameter Definition Examples Source: https://github.com/hoeken/psychichttp/blob/master/README.md Illustrates valid and invalid formats for template parameters. Parameters must start and end with a delimiter (default '%'), contain only alphanumeric characters and underscores, and have a maximum length of 63 characters. ```Text %MY_PARAM% %SOME1% ``` ```Text %MY PARAM% %SOME1 % %UNFINISHED %% ``` -------------------------------- ### Start WebSocket Test Functions Source: https://github.com/hoeken/psychichttp/blob/master/examples/pio-arduino/data/www/websocket-test.html Functions to initiate the WebSocket test with either the small or big payload. They set the `payload` variable and then call the main `startTest` function. The buttons to start the test are disabled during the test. ```javascript function startTestSmall() { payload = smallPayload; startTest(); } function startTestBig() { payload = bigPayload; startTest(); } ``` -------------------------------- ### Setup Captive Portal Source: https://github.com/hoeken/psychichttp/blob/master/examples/arduino/arduino_captive_portal/README.md Configures the DNS server to listen on port 53 for all domains and the IP address of the Access Point. It then creates and adds the captive portal handler to the Psychichttp server. Ensure this handler is added after other handlers if it should take precedence. ```cpp // captive portal dnsServer.start(53, "*", WiFi.softAPIP()); // DNS requests are executed over port 53 (standard) captivehandler= new CaptiveRequestHandler(); // create captive portal handler, important : after server.on since handlers are triggered on a first created/first trigerred basis server.addHandler(captivehandler); // captive portal handler (last handler) ``` -------------------------------- ### Clone PsychicHttp Project Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/README.md Clone the PsychicHttp project repository, including submodules, using Git. Ensure you have Git installed and configured. ```bash git clone --recursive git@github.com:hoeken/PsychicHttp.git ``` -------------------------------- ### Send Client IP on /ip Source: https://github.com/hoeken/psychichttp/blob/master/README.md A basic `PsychicWebHandler` example that sends the client's IP address back to the client when they request the `/ip` URL. ```cpp server.on("/ip", [](PsychicRequest *request) { String output = "Your IP is: " + request->client()->remoteIP().toString(); return response->send(output.c_str()); }); ``` -------------------------------- ### Multipart Upload Handler Setup Source: https://github.com/hoeken/psychichttp/blob/master/README.md Handles multipart form uploads, allowing access to form variables and file information. It defines onUpload and onRequest callbacks for processing uploaded files and form data. The endpoint '/multipart' is used for these uploads. ```cpp //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 += "" + url + "
\n"; output += "Bytes: " + String(file->size()) + "
\n"; output += "Param 1: " + String(request->getParam("param1", "")) + "
\n"; output += "Param 2: " + String(request->getParam("param2", "")) + "
\n"; return response->send(output.c_str()); }); //upload to /multipart url server.on("/multipart", HTTP_POST, multipartHandler); ``` -------------------------------- ### Flash and Monitor ESP-IDF App Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/README.md Flash the application firmware to the ESP32 device and start the serial monitor. The IP address of the device will be displayed in the console, allowing access via a web browser. ```bash idf.py flash monitor ``` -------------------------------- ### Generate Self-Signed Certificate and Key Source: https://github.com/hoeken/psychichttp/blob/master/README.md Use OpenSSL to generate a self-signed RSA certificate and private key for HTTPS server setup. This command creates a 4096-bit key and a certificate valid for 365 days. ```bash openssl req -x509 -newkey rsa:4096 -nodes -keyout server.key -out server.crt -sha256 -days 365 ``` -------------------------------- ### Server Initialization Source: https://github.com/hoeken/psychichttp/blob/master/CHANGELOG.md Ensure server begins operation after all endpoints are defined. This is a required step for v2.0. ```cpp server.begin() ``` -------------------------------- ### Get Regex Match Data Source: https://github.com/hoeken/psychichttp/blob/master/CHANGELOG.md Retrieve captured data from a regex-matched URI. This is available when using regex matching for endpoints. ```cpp request->getRegexMatches() ``` -------------------------------- ### Send a File Response Directly Source: https://github.com/hoeken/psychichttp/blob/master/README.md Demonstrates how to manually create and send a file response for a specific file. This approach allows for more control over the response, such as custom handling logic. ```cpp server.on("/ip", [](PsychicRequest *request) { String filename = "/path/to/file"; PsychicFileResponse response(request, LittleFS, filename); return response.send(); }); ``` -------------------------------- ### Template a File using TemplatePrinter::start Source: https://github.com/hoeken/psychichttp/blob/master/README.md An RAII-style approach to template a file from SD card using TemplatePrinter::start. Requires 'templateHandler' to be defined. ```C++ server.on("/home", [](PsychicRequest *request) { PsychicStreamResponse response(request, "text/html"); File file = SD.open("/www/index.html"); response.beginSend(); TemplatePrinter::start(response, templateHandler, [&file](TemplatePrinter &printer){ printer.copyFrom(file); }); file.close(); return response.endSend(); }); ``` -------------------------------- ### Handling Wi-Fi Credentials (Root PlatformIO) Source: https://github.com/hoeken/psychichttp/blob/master/README.md When building from the root PlatformIO configuration, place a secrets.h file at the repository root and edit it with your Wi-Fi credentials. ```bash mv secrets.h.example secrets.h # then edit secrets.h and set WIFI_SSID / WIFI_PASS ``` -------------------------------- ### WebSocket Connection and Event Handling Source: https://github.com/hoeken/psychichttp/blob/master/examples/websockets/data/www/index.html Establishes a WebSocket connection and sets up event listeners for open, message, error, and close events. Ensure the server address is correct. ```javascript 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`; }); } ``` -------------------------------- ### Event Listeners for Test Controls Source: https://github.com/hoeken/psychichttp/blob/master/examples/pio-arduino/data/www/websocket-test.html Attaches click event listeners to buttons that initiate the WebSocket performance test. These listeners call the respective functions to start the test with different configurations. ```javascript document.getElementById('startTestSmall').addEventListener('click', startTestSmall); documentument.getElementById('startTestBig').addEventListener('click', startTestBig); ``` -------------------------------- ### Set HTTPS Certificate and Key Source: https://github.com/hoeken/psychichttp/blob/master/README.md Configure the PsychicHttpsServer with your server certificate and private key. Ensure these are provided as const char*. ```cpp #include #include PsychicHttpsServer server; server.setCertificate(server_cert, server_key); ``` -------------------------------- ### Conditional Server Initialization with SSL Source: https://github.com/hoeken/psychichttp/blob/master/README.md Conditionally initialize PsychicHttpsServer or PsychicHttpServer based on the PSY_ENABLE_SSL macro. This allows your code to adapt to the availability of SSL support. ```cpp //our main server object #ifdef PSY_ENABLE_SSL PsychicHttpsServer server; #else PsychicHttpServer server; #endif ``` -------------------------------- ### Serve a Single Static File Source: https://github.com/hoeken/psychichttp/blob/master/README.md Serves a single static file named '/custom.txt' from LittleFS, mapped to the '/myfile.txt' URL path. ```cpp server.serveStatic("/myfile.txt", LittleFS, "/custom.txt"); ``` -------------------------------- ### Template a File with PsychicHTTP Source: https://github.com/hoeken/psychichttp/blob/master/README.md Serve an HTML file from SD card, substituting template variables. Ensure the 'templateHandler' is defined elsewhere. ```C++ server.on("/home", [](PsychicRequest *request) { PsychicStreamResponse response(request, "text/html"); File file = SD.open("/www/index.html"); response.beginSend(); TemplatePrinter printer(response, templateHandler); printer.copyFrom(file); printer.flush(); file.close(); return response.endSend(); }); ``` -------------------------------- ### Serve Static Files from a Specific Directory Source: https://github.com/hoeken/psychichttp/blob/master/README.md Serves static files from the '/img/' directory on LittleFS, mapped to the '/img' URL path. While possible, serving from a single 'www' directory is generally more efficient. ```cpp server.serveStatic("/img", LittleFS, "/img/"); ``` -------------------------------- ### Template a String with TemplatePrinter::start Source: https://github.com/hoeken/psychichttp/blob/master/README.md Dynamically template a plain text string using TemplatePrinter::start. This method allows for direct printing of template variables. ```C++ server.on("/template2", [](PsychicRequest *request) { PsychicStreamResponse response(request, "text/plain"); response.beginSend(); TemplatePrinter::start(response, templateHandler, [](TemplatePrinter &printer){ printer.println("My ESP has %FREE_HEAP% left. Its lifetime minimum heap is %MIN_FREE_HEAP%."); printer.println("The maximum allocation size is %MAX_ALLOC_HEAP%, and its total size is %HEAP_SIZE%."); printer.println("This is an unhandled parameter: %UNHANDLED_PARAM% and this is an invalid param %INVALID PARAM%."); }); return response.endSend(); }); ``` -------------------------------- ### Basic CMakeLists.txt for ESP-IDF PIO Project Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf-pio/CMakeLists.txt This snippet shows the minimal CMakeLists.txt required to initialize an ESP-IDF project within a PlatformIO environment. It sets the minimum CMake version and includes the main ESP-IDF build script. ```cmake cmake_minimum_required(VERSION 3.16.0) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(esp-idf-pio) ``` -------------------------------- ### Setting Include Directories Source: https://github.com/hoeken/psychichttp/blob/master/CMakeLists.txt Specifies the directories where header files for the component can be found. ```cmake set(COMPONENT_ADD_INCLUDEDIRS "src" ) ``` -------------------------------- ### Create LittleFS Partition Image Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/main/CMakeLists.txt Generates a LittleFS partition image from the 'data' directory in the project. This command is used to prepare filesystem data for the ESP32's flash memory. ```cmake littlefs_create_partition_image(littlefs ${project_dir}/data FLASH_IN_PROJECT) ``` -------------------------------- ### ESP-ROM Boot Information Source: https://github.com/hoeken/psychichttp/blob/master/examples/arduino/arduino_ota/README.md Displays system boot information from the ESP-ROM, including build details and boot mode. This is typically seen after a device reset or power-on. ```text ESP-ROM:esp32s3-20210327 Build:Mar 27 2021 rst:0xc (RTC_SW_CPU_RST),boot:0x8 (SPI_FAST_FLASH_BOOT) Saved PC:0x420984ae SPIWP:0xee mode:DIO, clock div:1 load:0x3fce3808,len:0x4bc load:0x403c9700,len:0xbd8 load:0x403cc700,len:0x2a0c entry 0x403cc700 ``` -------------------------------- ### Add Middleware to Endpoint Source: https://github.com/hoeken/psychichttp/blob/master/README.md Apply middleware, like authentication, to an endpoint by chaining `addMiddleware()` to the pointer returned by `server.on()`. ```cpp AuthenticationMiddleware auth; auth.setUsername("user"); auth.setPassword("pass"); auth.setRealm("My Realm"); auth.setAuthType(BASIC_AUTH); server.on("/url", HTTP_GET, request_callback)->addMiddleware(&auth); ``` -------------------------------- ### Create Endpoint Handler Source: https://github.com/hoeken/psychichttp/blob/master/README.md Use `server.on()` to create an endpoint handler for a specific URL and HTTP method. The handler can be a callback function. ```cpp server.on("/url", HTTP_GET, request_callback); ``` ```cpp server.on("/url", request_callback); ``` -------------------------------- ### Setting Source Directories Source: https://github.com/hoeken/psychichttp/blob/master/CMakeLists.txt Defines the directories containing the source files for the component. ```cmake set(COMPONENT_SRCDIRS "src" ) ``` -------------------------------- ### Enabling Heap Optimizations for WebSockets Source: https://github.com/hoeken/psychichttp/blob/master/README.md Configures build flags to optimize WebSocket frame handling on heap-constrained boards (no PSRAM). PSYCHIC_WS_MAX_FRAME_SIZE limits frame size, and PSYCHIC_WS_RX_STATIC_BUFFER uses a static buffer to prevent heap fragmentation. ```ini build_flags = -D PSYCHIC_WS_MAX_FRAME_SIZE=2048 -D PSYCHIC_WS_RX_STATIC_BUFFER ``` -------------------------------- ### HTTP to HTTPS Redirect Server Source: https://github.com/hoeken/psychichttp/blob/master/README.md Set up a secondary HTTP server on port 80 to redirect all incoming requests to the HTTPS version of the site. This ensures all traffic uses the secure connection. ```cpp //this creates a 2nd server listening on port 80 and redirects all requests HTTPS PsychicHttpServer *redirectServer = new PsychicHttpServer(); redirectServer->config.ctrl_port = 20420; // just a random port different from the default one redirectServer->onNotFound([](PsychicRequest *request) { String url = "https://" + request->host() + request->url(); return response->redirect(url.c_str()); }); ``` -------------------------------- ### Registering the Component Source: https://github.com/hoeken/psychichttp/blob/master/CMakeLists.txt Registers the current directory as an ESP-IDF component. ```cmake register_component() ``` -------------------------------- ### Configure WebSocket Static RX Buffer and Max Frame Size Source: https://github.com/hoeken/psychichttp/blob/master/CHANGELOG.md Define build flags to enable static RX buffer and maximum frame size guard for PsychicWebSocket. This optimizes memory usage on boards without PSRAM or with limited heap. Ensure PSYCHIC_WS_MAX_FRAME_SIZE is defined when using PSYCHIC_WS_RX_STATIC_BUFFER. ```cpp // In build flags (e.g. platformio.ini): // -D PSYCHIC_WS_MAX_FRAME_SIZE=2048 // -D PSYCHIC_WS_RX_STATIC_BUFFER // That's it — the buffer is allocated for you in server.begin(). ``` -------------------------------- ### ESP-IDF Native Configuration Source: https://github.com/hoeken/psychichttp/blob/master/README.md Add these configurations to your `sdkconfig.defaults` file for native ESP-IDF projects. Enable WebSocket support and HTTPS server if needed. ```ini CONFIG_HTTPD_WS_SUPPORT=y # CONFIG_MBEDTLS_ROM_MD5 is not set # Add this if you are using PsychicHttpsServer: CONFIG_ESP_HTTPS_SERVER_ENABLE=y ``` -------------------------------- ### Serve Static Files from LittleFS on STA Network Source: https://github.com/hoeken/psychichttp/blob/master/README.md Serves static files from the '/www/' directory on LittleFS, accessible only to clients on the same Wi-Fi network. This is typically used for serving '/index.html'. ```cpp server.serveStatic("/", LittleFS, "/www/")->addFilter(ON_STA_FILTER); ``` -------------------------------- ### Setting Compile Definitions and Options Source: https://github.com/hoeken/psychichttp/blob/master/CMakeLists.txt Configures compilation settings for the component, including defining 'ESP32' and disabling RTTI. ```cmake target_compile_definitions(${COMPONENT_TARGET} PUBLIC -DESP32) target_compile_options(${COMPONENT_TARGET} PRIVATE -fno-rtti) ``` -------------------------------- ### Flash LittleFS Filesystem Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/README.md Write the LittleFS filesystem binary to the specified flash address using esptool.py. This step is crucial for applications that serve files from flash. ```bash esptool.py write_flash --flash_mode dio --flash_freq 40m --flash_size 4MB 0x317000 build/littlefs.bin ``` -------------------------------- ### Serve Static Files from LittleFS on AP Network Source: https://github.com/hoeken/psychichttp/blob/master/README.md Serves static files from the '/www-ap/' directory on LittleFS, accessible only to clients connected via the SoftAP. This is typically used for serving '/index.html' in AP mode. ```cpp server.serveStatic("/", LittleFS, "/www-ap/")->addFilter(ON_AP_FILTER); ``` -------------------------------- ### Attach WebSocket Handler Source: https://github.com/hoeken/psychichttp/blob/master/README.md Attach a `PsychicWebSocketHandler` to a specific URL endpoint using `server.on()`. ```cpp PsychicWebSocketHandler websocketHandler; server.on("/ws", &websocketHandler); ``` -------------------------------- ### WebSocket Connection and Messaging Source: https://github.com/hoeken/psychichttp/blob/master/examples/pio-arduino/data/www/index.html Establishes a WebSocket connection, handles incoming messages, sends messages, and manages connection events. Requires a WebSocket server endpoint. ```javascript let socket; const outputText = document.getElementById('websocket_output'); const messageInput = document.getElementById('message_input'); function websocketConnect() { // Create a WebSocket connection socket = new WebSocket((location.protocol === "https:" ? "wss://" : "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; } } ``` -------------------------------- ### Register ESP-IDF Component with Source Files Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf-pio/src/CMakeLists.txt This snippet demonstrates how to register a component for an ESP-IDF project. It uses `FILE(GLOB_RECURSE)` to find all source files in the 'src' directory and then registers them with `idf_component_register`. ```cmake FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) idf_component_register(SRCS ${app_sources}) ``` -------------------------------- ### EventSource Connection and Event Handling Source: https://github.com/hoeken/psychichttp/blob/master/examples/pio-arduino/data/www/index.html Connects to an EventSource endpoint and listens for different types of events ('millis', 'message', 'error'). Handles connection opening and closing. ```javascript 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(); }; } ``` -------------------------------- ### Psychic HTTP Server Callbacks Source: https://github.com/hoeken/psychichttp/blob/master/examples/arduino/arduino_ota/README.md Logs new client connections and disconnections for the Psychic HTTP Server. Useful for monitoring network activity. ```cpp [ 54415][I][PsychicHttpServer.cpp:236] openCallback(): [psychic] New client connected 52 [ 54424][I][PsychicHttpServer.cpp:262] closeCallback(): [psychic] Client disconnected 52 ``` -------------------------------- ### Define Captive Portal Handler Source: https://github.com/hoeken/psychichttp/blob/master/examples/arduino/arduino_captive_portal/README.md Defines a custom request handler for the captive portal. The `canHandle` method should return true to activate the portal, and `handleRequest` sends the portal content. Uncomment the `PsychicFileResponse` lines to serve a specific HTML file. ```cpp #include DNSServer dnsServer; class CaptiveRequestHandler : public PsychicWebHandler { // handler public: CaptiveRequestHandler() {}; virtual ~CaptiveRequestHandler() {}; bool canHandle(PsychicRequest*request){ // ... if needed some tests ... return(false); return true; // activate captive portal } esp_err_t handleRequest(PsychicRequest *request) { //PsychicFileResponse response(request, LittleFS, "/captiveportal.html"); // uncomment : for captive portal page, if any, eg "captiveportal.html" //return response.send(); // uncomment : return captive portal page return response->send(200,"text/html","Welcome to captive portal !"); // simple text, comment if captive portal page } }; CaptiveRequestHandler *captivehandler=NULL; // handler for captive portal ``` -------------------------------- ### Cross-Platform Handler Methods Source: https://github.com/hoeken/psychichttp/blob/master/README.md Use these `*CStr()` helper methods to ensure your handlers compile identically on both Arduino and native ESP-IDF platforms. They consistently return `const char*`. ```cpp // These work identically on both Arduino and ESP-IDF native: request->uriCStr() // instead of request->uri() request->bodyCStr() // instead of request->body() request->headerCStr(name) // instead of request->header(name) request->pathCStr() // instead of request->path() request->queryCStr() // instead of request->query() request->methodStrCStr() // instead of request->methodStr() request->getFilenameCStr() // instead of request->getFilename() request->getSessionKeyCStr(key) // instead of request->getSessionKey(key) endpoint->uriCStr() // instead of endpoint->uri() param->nameCStr() // instead of param->name() param->valueCStr() // instead of param->value() ``` -------------------------------- ### Conditional Include Directories for LittleFS Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/CMakeLists.txt Includes header files for the 'joltwallet/littlefs' component only if the IDF version is less than 5. This ensures compatibility with older ESP-IDF versions. ```cmake if(${IDF_VERSION_MAJOR} LESS "5") include_directories("managed_components/joltwallet__littlefs/include") endif() ``` -------------------------------- ### PsychicWebSocketHandler Callback Definitions Source: https://github.com/hoeken/psychichttp/blob/master/README.md Defines the function signatures for WebSocket event callbacks: onOpen, onFrame, and onClose. ```cpp void open_function(PsychicWebSocketClient *client); esp_err_t frame_function(PsychicWebSocketRequest *request, httpd_ws_frame_t *frame); void close_function(PsychicWebSocketClient *client); ``` -------------------------------- ### Configure HTTP Component Path Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/CMakeLists.txt Sets the HTTP_PATH variable, which is used to locate external components. It prioritizes the HTTP_PATH environment variable and falls back to relative paths. ```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}) ``` -------------------------------- ### Restart ESP Device Source: https://github.com/hoeken/psychichttp/blob/master/examples/arduino/arduino_ota/data/update.html Initiates a device restart by sending a POST request to the '/restart' endpoint. It updates the status display with the response from the server. ```javascript function esprestartButton() { /* restart ESP */ var urltocall = "/restart"; xhr=new XMLHttpRequest(); xhr.onreadystatechange = function() { if (this.readyState == 4) document.getElementById("status").innerHTML = xhr.responseText; }; xhr.open("POST", urltocall, false); // trigger restart request with file parameter xhr.send(); } ``` -------------------------------- ### JavaScript WebSocket Connection and Messaging Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/data/www/index.html Establishes a WebSocket connection, handles open, message, error, and close events, and sends messages to the server. Includes input validation for messages. ```javascript 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; } } ``` -------------------------------- ### Configure Build Flags for Backpressure Source: https://github.com/hoeken/psychichttp/blob/master/README.md Set build flags to customize the maximum number of pending frames per client or to enable PSRAM allocation for payloads. This helps manage memory usage and prevent heap exhaustion under high load. ```ini build_flags = -D PSYCHIC_WS_MAX_PENDING_FRAMES=16 ; optional: override the default cap of 8 -D PSYCHIC_WS_PSRAM_PAYLOAD ; optional: payload copies in PSRAM ``` -------------------------------- ### JavaScript EventSource Connection and Event Handling Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/data/www/index.html Connects to an EventSource stream, handling 'open', 'message', and 'error' events, as well as custom 'millis' events. Displays received data in a textarea. ```javascript 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(); }; } ``` -------------------------------- ### Project Name Declaration Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/CMakeLists.txt Declares the name of the project. This should be set after all other configurations. ```cmake project(PsychicHttp_IDF) ``` -------------------------------- ### Add Extra Component Directories Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/CMakeLists.txt Specifies additional directories where CMake should look for components. This is typically set using the HTTP_PATH variable. ```cmake set(EXTRA_COMPONENT_DIRS ${HTTP_PATH}) ``` -------------------------------- ### ESP-IDF Project Boilerplate Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/CMakeLists.txt Essential boilerplate for an ESP-IDF project's CMakeLists.txt. Must be included in this exact order for cmake to function correctly. ```cmake cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) ``` -------------------------------- ### Conditional Component Requirements (Arduino Framework) Source: https://github.com/hoeken/psychichttp/blob/master/CMakeLists.txt Determines the component's dependencies based on whether the Arduino framework is detected in the project's build components. Adds specific components like 'arduino', 'esp_https_server', etc., and conditionally 'esp_rom' for IDF version 6 and above. ```cmake idf_build_get_property(build_components BUILD_COMPONENTS) if("arduino" IN_LIST build_components) set(COMPONENT_REQUIRES "arduino" "esp_https_server" "esp_http_server" "mbedtls" "arduinojson" ) if(IDF_VERSION_MAJOR GREATER_EQUAL 6) list(APPEND COMPONENT_REQUIRES "esp_rom") endif() else() set(COMPONENT_REQUIRES "esp_https_server" "esp_http_server" "esp_netif" "mbedtls" "arduinojson" ) if(IDF_VERSION_MAJOR GREATER_EQUAL 6) list(APPEND COMPONENT_REQUIRES "esp_rom") endif() endif() ``` -------------------------------- ### JavaScript File Upload with XMLHttpRequest Source: https://github.com/hoeken/psychichttp/blob/master/examples/esp-idf/data/www/index.html Handles file selection, path setting, and uploads a file to the server using XMLHttpRequest. Includes validation for file size, path, and name. ```javascript 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); } } ```