Switch HTTP Server
6
lib/PsychicHttp/examples/arduino/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.pio
|
||||
.vscode/
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
497
lib/PsychicHttp/examples/arduino/arduino.ino
Normal file
@@ -0,0 +1,497 @@
|
||||
/*
|
||||
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>
|
||||
#include <PsychicHttpsServer.h> //uncomment this to enable HTTPS / SSL
|
||||
|
||||
#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 PSY_ENABLE_SSL to enable ssl
|
||||
#ifdef PSY_ENABLE_SSL
|
||||
bool app_enable_ssl = true;
|
||||
String server_cert;
|
||||
String server_key;
|
||||
#endif
|
||||
|
||||
//our main server object
|
||||
#ifdef PSY_ENABLE_SSL
|
||||
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 PSY_ENABLE_SSL
|
||||
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 PSY_ENABLE_SSL
|
||||
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());
|
||||
});
|
||||
|
||||
//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());
|
||||
});
|
||||
|
||||
//api - json message passed in as post body
|
||||
server.on("/api", HTTP_POST, [](PsychicRequest *request)
|
||||
{
|
||||
//load our JSON request
|
||||
StaticJsonDocument<1024> json;
|
||||
String body = request->body();
|
||||
DeserializationError err = deserializeJson(json, body);
|
||||
|
||||
//create our response json
|
||||
StaticJsonDocument<128> output;
|
||||
output["msg"] = "status";
|
||||
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
|
||||
StaticJsonDocument<128> 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[10];
|
||||
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());
|
||||
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());
|
||||
});
|
||||
server.on("/ws", &websocketHandler);
|
||||
|
||||
//EventSource server
|
||||
eventSource.onOpen([](PsychicEventSourceClient *client) {
|
||||
Serial.printf("[eventsource] connection #%u connected from %s\n", client->socket(), client->localIP().toString());
|
||||
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());
|
||||
});
|
||||
server.on("/events", &eventSource);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long lastUpdate = 0;
|
||||
char output[60];
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (millis() - lastUpdate > 2000)
|
||||
{
|
||||
sprintf(output, "Millis: %d\n", millis());
|
||||
websocketHandler.sendAll(output);
|
||||
|
||||
sprintf(output, "%d", millis());
|
||||
eventSource.send(output, "millis", millis(), 0);
|
||||
|
||||
lastUpdate = millis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
**1) SUMMARY**
|
||||
|
||||
This example implements a **captive portal** with library DNSServer, ie web page which opens automatically when user connects Wifi network (eg "PsychitHttp").
|
||||
|
||||
Captiveportal is implemented in **ESPAsyncWebServer** [https://github.com/me-no-dev/ESPAsyncWebServer/blob/master/examples/CaptivePortal/CaptivePortal.ino](url) and in **arduino-esp32 examples** [https://github.com/espressif/arduino-esp32/blob/master/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino](url)
|
||||
|
||||
This feature can be implemented with Psychichttp with a **dedicated handler**, as shown in code below.
|
||||
|
||||
Code highlights are added below for reference.
|
||||
|
||||
**2) CODE**
|
||||
|
||||
**Definitions**
|
||||
```
|
||||
// captiveportal
|
||||
// credits https://github.com/me-no-dev/ESPAsyncWebServer/blob/master/examples/CaptivePortal/CaptivePortal.ino
|
||||
//https://github.com/espressif/arduino-esp32/blob/master/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino
|
||||
#include <DNSServer.h>
|
||||
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 request->reply(200,"text/html","Welcome to captive portal !"); // simple text, comment if captive portal page
|
||||
}
|
||||
};
|
||||
CaptiveRequestHandler *captivehandler=NULL; // handler for captive portal
|
||||
```
|
||||
|
||||
**setup()**
|
||||
```
|
||||
// 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)
|
||||
```
|
||||
|
||||
**loop()**
|
||||
```
|
||||
dnsServer.processNextRequest(); // captive portal
|
||||
```
|
||||
|
||||
**3) RESULT**
|
||||
|
||||
**Access Point (web page is opened automatically when connecting to PsychicHttp AP)**
|
||||

|
||||
|
||||
**Station (web page is shown whatever url for Station IP, eg 192.168.1.50/abcdefg**
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
PsychicHTTP Server Captive Portal 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.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <LittleFS.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <PsychicHttp.h>
|
||||
|
||||
char* TAG = "CAPTPORT";
|
||||
|
||||
// captiveportal
|
||||
// credits https://github.com/me-no-dev/ESPAsyncWebServer/blob/master/examples/CaptivePortal/CaptivePortal.ino
|
||||
//https://github.com/espressif/arduino-esp32/blob/master/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino
|
||||
#include <DNSServer.h>
|
||||
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 request->reply(200,"text/html","Welcome to captive portal !"); // simple text, comment if captive portal page
|
||||
}
|
||||
};
|
||||
CaptiveRequestHandler *captivehandler=NULL; // handler for captive portal
|
||||
|
||||
const char* ssid = "mySSID"; // replace with your SSID (mode STATION)
|
||||
const char* password = "myPassword"; // replace with you password (mode STATION)
|
||||
|
||||
// Set your SoftAP credentials
|
||||
const char *softap_ssid = "PsychicHttp";
|
||||
const char *softap_password = "";
|
||||
IPAddress softap_ip(10, 0, 0, 1);
|
||||
|
||||
//hostname for mdns (psychic.local)
|
||||
const char *local_hostname = "psychic";
|
||||
|
||||
//our main server object
|
||||
PsychicHttpServer server;
|
||||
|
||||
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();
|
||||
ESP_LOGI(TAG,"SoftAP IP Address: %s", myIP.toString().c_str());
|
||||
ESP_LOGI(TAG,"[WiFi] Connecting to %s", ssid);
|
||||
|
||||
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:
|
||||
ESP_LOGE(TAG,"[WiFi] SSID not found");
|
||||
break;
|
||||
case WL_CONNECT_FAILED:
|
||||
ESP_LOGI(TAG,"[WiFi] Failed - WiFi not connected! Reason: ");
|
||||
return false;
|
||||
break;
|
||||
case WL_CONNECTION_LOST:
|
||||
ESP_LOGI(TAG,"[WiFi] Connection was lost");
|
||||
break;
|
||||
case WL_SCAN_COMPLETED:
|
||||
ESP_LOGI(TAG,"[WiFi] Scan is completed");
|
||||
break;
|
||||
case WL_DISCONNECTED:
|
||||
ESP_LOGI(TAG,"[WiFi] WiFi is disconnected");
|
||||
break;
|
||||
case WL_CONNECTED:
|
||||
ESP_LOGI(TAG,"[WiFi] WiFi is connected, IP address %s",WiFi.localIP().toString().c_str());
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG,"[WiFi] WiFi Status: %d",WiFi.status());
|
||||
break;
|
||||
}
|
||||
delay(tryDelay);
|
||||
|
||||
if (numberOfTries <= 0) {
|
||||
ESP_LOGI(TAG,"[WiFi] Failed to connect to WiFi!");
|
||||
// Use disconnect function to force stop trying to connect
|
||||
WiFi.disconnect();
|
||||
return false;
|
||||
}
|
||||
else numberOfTries--;
|
||||
}
|
||||
|
||||
return false;
|
||||
} // end connectToWifi
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
|
||||
// Wifi
|
||||
if (connectToWifi()) { // set up our esp32 to listen on the local_hostname.local domain
|
||||
if (!MDNS.begin(local_hostname)) {
|
||||
ESP_LOGE(TAG,"Error starting mDNS");
|
||||
return;
|
||||
}
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
if(!LittleFS.begin()) {
|
||||
ESP_LOGI(TAG,"ERROR : LittleFS Mount Failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
//setup server config stuff here
|
||||
server.config.max_uri_handlers = 20; //maximum number of uri handlers (.on() calls)
|
||||
server.listen(80);
|
||||
|
||||
DefaultHeaders::Instance().addHeader("Server", "PsychicHttp");
|
||||
|
||||
// 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)
|
||||
} // end set up our esp32 to listen on the local_hostname.local domain
|
||||
} // end setup
|
||||
|
||||
void loop() {
|
||||
dnsServer.processNextRequest(); // captive portal
|
||||
}
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
138
lib/PsychicHttp/examples/arduino/arduino_ota/README.md
Normal file
@@ -0,0 +1,138 @@
|
||||
**OTA update example for PsychicHttp**
|
||||
|
||||
Example of OTA (Over The Air) update implementation for PsychicHttp, using Arduino IDE.
|
||||
|
||||
**Requirements**
|
||||
Requirements for project are :
|
||||
- OTA update for code (firmware)
|
||||
- OTA update for data (littlefs)
|
||||
- manual restart of ESP32, triggered by User (no automatic restart after code or data file upload)
|
||||
|
||||
**Implementation**
|
||||
|
||||
OTA update relies on handler PsychicUploadHandler.
|
||||
|
||||
Screenshots and Code are shown below.
|
||||
|
||||
**Credits**
|
||||
|
||||
https://github.com/hoeken/PsychicHttp/blob/master/src/PsychicUploadHandler.cpp
|
||||
|
||||
https://github.com/hoeken/PsychicHttp/issues/30
|
||||
|
||||
**Configuration**
|
||||
|
||||
Example has been implemented with following configuration :\
|
||||
Arduino IDE 1.8.19\
|
||||
arduino-32 v2.0.15\
|
||||
PsychicHttp 1.1.1\
|
||||
ESP32S3
|
||||
|
||||
**Example Files Structure**
|
||||
|
||||
```
|
||||
arduino_ota
|
||||
data
|
||||
| update.html
|
||||
arduino_ota.ino
|
||||
code.bin
|
||||
littlefs.bin
|
||||
README
|
||||
```
|
||||
"code.bin" and "littlefs.bin" are example update files which can be used to update respectily code (firmware) or data (littlefs).
|
||||
|
||||
"Real" update files can be generated on Arduino IDE 1.x :
|
||||
- for code, menu "Sketch -> Export bin"
|
||||
- for data, using plugin arduino-esp32fs-plugin https://github.com/lorol/arduino-esp32fs-plugin/releases
|
||||
|
||||
**SCREENSHOTS**
|
||||
|
||||
**Update code (firmware)**
|
||||

|
||||
|
||||
\
|
||||
```ESP-ROM:esp32s3-20210327
|
||||
Build:Mar 27 2021
|
||||
rst:0x1 (POWERON),boot:0x2b (SPI_FAST_FLASH_BOOT)
|
||||
SPIWP:0xee
|
||||
mode:DIO, clock div:1
|
||||
load:0x3fce3808,len:0x4bc
|
||||
load:0x403c9700,len:0xbd8
|
||||
load:0x403cc700,len:0x2a0c
|
||||
entry 0x403c98d0
|
||||
[332885][I][PsychicHttpServer.cpp:236] openCallback(): [psychic] New client connected 51
|
||||
[332895][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 867306
|
||||
[332908][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 862016
|
||||
[332919][I][arduino_ota.ino:128] operator()(): [OTA] updateHandler->onUpload _error 0 Update.hasError() 0 last 0
|
||||
[332929][I][arduino_ota.ino:133] operator()(): [OTA] update begin, filename code.bin
|
||||
[333082][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 856272
|
||||
[333095][I][arduino_ota.ino:128] operator()(): [OTA] updateHandler->onUpload _error 0 Update.hasError() 0 last 0
|
||||
[snip]
|
||||
[339557][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 416
|
||||
[339566][I][arduino_ota.ino:128] operator()(): [OTA] updateHandler->onUpload _error 0 Update.hasError() 0 last 1
|
||||
[339718][I][arduino_ota.ino:165] operator()(): [OTA] Update Success: 867072 written
|
||||
[339726][I][arduino_ota.ino:184] operator()(): [OTA] Update code or data OK Update.errorString() No Error
|
||||
[339738][I][PsychicHttpServer.cpp:236] openCallback(): [psychic] New client connected 52
|
||||
[339747][I][PsychicHttpServer.cpp:262] closeCallback(): [psychic] Client disconnected 52
|
||||
|
||||
```
|
||||
|
||||
|
||||
**Update data (littlefs)**
|
||||
|
||||

|
||||
```
|
||||
[ 48216][I][PsychicHttpServer.cpp:236] openCallback(): [psychic] New client connected 51
|
||||
[ 48226][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 1573100
|
||||
[ 48239][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 1567810
|
||||
[ 48250][I][arduino_ota.ino:128] operator()(): [OTA] updateHandler->onUpload _error 0 Update.hasError() 0 last 0
|
||||
[ 48261][I][arduino_ota.ino:133] operator()(): [OTA] update begin, filename littlefs.bin
|
||||
[ 48376][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 1562066
|
||||
[ 48389][I][arduino_ota.ino:128] operator()(): [OTA] updateHandler->onUpload _error 0 Update.hasError() 0 last 0
|
||||
[ 48408][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 1556322
|
||||
[ 48421][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 1550578
|
||||
[ 48432][I][arduino_ota.ino:128] operator()(): [OTA] updateHandler->onUpload _error 0 Update.hasError() 0 last 0
|
||||
[snip]
|
||||
[ 54317][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 16930
|
||||
[ 54327][I][arduino_ota.ino:128] operator()(): [OTA] updateHandler->onUpload _error 0 Update.hasError() 0 last 0
|
||||
[ 54340][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 11186
|
||||
[ 54351][I][arduino_ota.ino:128] operator()(): [OTA] updateHandler->onUpload _error 0 Update.hasError() 0 last 0
|
||||
[ 54363][I][PsychicUploadHandler.cpp:164] _multipartUploadHandler(): [psychic] Remaining size : 5442
|
||||
[ 54375][I][arduino_ota.ino:128] operator()(): [OTA] updateHandler->onUpload _error 0 Update.hasError() 0 last 0
|
||||
[ 54386][I][arduino_ota.ino:128] operator()(): [OTA] updateHandler->onUpload _error 0 Update.hasError() 0 last 1
|
||||
[ 54396][I][arduino_ota.ino:165] operator()(): [OTA] Update Success: 1572864 written
|
||||
[ 54404][I][arduino_ota.ino:184] operator()(): [OTA] Update code or data OK Update.errorString() No Error
|
||||
[ 54415][I][PsychicHttpServer.cpp:236] openCallback(): [psychic] New client connected 52
|
||||
[ 54424][I][PsychicHttpServer.cpp:262] closeCallback(): [psychic] Client disconnected 52
|
||||
|
||||
```
|
||||
|
||||
**Restart**
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
```
|
||||
[110318][I][PsychicHttpServer.cpp:236] openCallback(): [psychic] New client connected 51
|
||||
[110327][I][arduino_ota.ino:205] operator()(): [OTA] <b style='color:green'>Restarting ...</b>
|
||||
[110338][I][PsychicHttpServer.cpp:236] openCallback(): [psychic] New client connected 52
|
||||
[111317][W][WiFiGeneric.cpp:1062] _eventCallback(): Reason: 8 - ASSOC_LEAVE
|
||||
[111319][I][PsychicHttpServer.cpp:262] closeCallback(): [psychic] Client disconnected 51
|
||||
[111332][I][PsychicHttpServer.cpp:262] closeCallback(): [psychic] Client disconnected 52
|
||||
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 0x403c98d0
|
||||
[ 283][I][arduino_ota.ino:57] connectToWifi(): [OTA] [WiFi] WiFi is disconnected
|
||||
[ 791][I][arduino_ota.ino:60] connectToWifi(): [OTA] [WiFi] WiFi is connected, IP address 192.168.1.50
|
||||
|
||||
```
|
||||
|
||||
|
||||
219
lib/PsychicHttp/examples/arduino/arduino_ota/arduino_ota.ino
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
Over The Air (OTA) update example for PsychicHttp web server
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
char *TAG = "OTA"; // ESP_LOG tag
|
||||
|
||||
// PsychicHttp
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <LittleFS.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <PsychicHttp.h>
|
||||
PsychicHttpServer server; // main server object
|
||||
const char *local_hostname = "psychichttp"; // hostname for mdns
|
||||
|
||||
// OTA
|
||||
#include <Update.h>
|
||||
bool esprestart=false; // true if/when ESP should be restarted, after OTA update
|
||||
|
||||
// Wifi
|
||||
const char *ssid = "SSID"; // your SSID
|
||||
const char *password = "PASSWORD"; // your PASSWORD
|
||||
|
||||
bool connectToWifi() { // Wifi
|
||||
//client in STA mode
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
WiFi.begin(ssid,password);
|
||||
|
||||
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:
|
||||
ESP_LOGE(TAG,"[WiFi] SSID not found");
|
||||
break;
|
||||
case WL_CONNECT_FAILED:
|
||||
ESP_LOGI(TAG,"[WiFi] Failed - WiFi not connected! Reason: ");
|
||||
return false;
|
||||
break;
|
||||
case WL_CONNECTION_LOST:
|
||||
ESP_LOGI(TAG,"[WiFi] Connection was lost");
|
||||
break;
|
||||
case WL_SCAN_COMPLETED:
|
||||
ESP_LOGI(TAG,"[WiFi] Scan is completed");
|
||||
break;
|
||||
case WL_DISCONNECTED:
|
||||
ESP_LOGI(TAG,"[WiFi] WiFi is disconnected");
|
||||
break;
|
||||
case WL_CONNECTED:
|
||||
ESP_LOGI(TAG,"[WiFi] WiFi is connected, IP address %s",WiFi.localIP().toString().c_str());
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG,"[WiFi] WiFi Status: %d",WiFi.status());
|
||||
break;
|
||||
}
|
||||
delay(tryDelay);
|
||||
|
||||
if (numberOfTries <= 0) {
|
||||
ESP_LOGI(TAG,"[WiFi] Failed to connect to WiFi!");
|
||||
// Use disconnect function to force stop trying to connect
|
||||
WiFi.disconnect();
|
||||
return false;
|
||||
}
|
||||
else numberOfTries--;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// =======================================================================
|
||||
// setup
|
||||
// =======================================================================
|
||||
void setup()
|
||||
{ Serial.begin(115200);
|
||||
delay(10);
|
||||
|
||||
// Wifi
|
||||
if (connectToWifi()) { //set up our esp32 to listen on the local_hostname.local domain
|
||||
if (!MDNS.begin(local_hostname)) {
|
||||
ESP_LOGE(TAG,"Error starting mDNS");
|
||||
return;
|
||||
}
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
if(!LittleFS.begin()) {
|
||||
ESP_LOGI(TAG,"ERROR : LittleFS Mount Failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
//setup server config stuff here
|
||||
server.config.max_uri_handlers = 20; //maximum number of uri handlers (.on() calls)
|
||||
server.listen(80);
|
||||
|
||||
DefaultHeaders::Instance().addHeader("Server", "PsychicHttp");
|
||||
|
||||
//server.maxRequestBodySize=2*1024*1024; // 2Mb, change default value if needed
|
||||
//server.maxUploadSize=64*1024*1024; // 64Mb, change default value if needed
|
||||
|
||||
//you can set up a custom 404 handler.
|
||||
// curl -i http://psychic.local/404
|
||||
server.onNotFound([](PsychicRequest *request) {
|
||||
return request->reply(404, "text/html", "Custom 404 Handler");
|
||||
});
|
||||
|
||||
// OTA
|
||||
PsychicUploadHandler *updateHandler = new PsychicUploadHandler(); // create handler for OTA update
|
||||
updateHandler->onUpload([](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool last) { // onUpload
|
||||
/* callback to upload code (firmware) or data (littlefs)
|
||||
* callback is triggered for each file chunk, from first chunk (index is 0) to last chunk (last is true)
|
||||
* callback is triggered by handler in handleRequest(), after _multipartUploadHandler() *
|
||||
* filename : name of file to upload, with naming convention below
|
||||
* "*code.bin" for code (firmware) update, eg "v1_code.bin"
|
||||
* "*littlefs.bin" for data (little fs) update, eg "v1_littlefs.bin" *
|
||||
*/
|
||||
|
||||
int command; //command : firmware and filesystem update type, ie code (U_FLASH 0) or data (U_SPIFFS 100)
|
||||
ESP_LOGI(TAG,"updateHandler->onUpload _error %d Update.hasError() %d last %d", Update.getError(), Update.hasError(), last);
|
||||
|
||||
// Update.abort() replaces 1st error (eg "UPDATE_ERROR_ERASE") with abort error ("UPDATE_ERROR_ABORT") so root cause is lost
|
||||
if (!Update.hasError()) { // no error encountered so far during update, process current chunk
|
||||
if (!index){ // index is 0, begin update (first chunk)
|
||||
ESP_LOGI(TAG,"update begin, filename %s", filename.c_str());
|
||||
Update.clearError(); // first chunk, clear Update error if any
|
||||
// check if update file is code, data or sd card one
|
||||
if (!filename.endsWith("code.bin") && !filename.endsWith("littlefs.bin")) { // incorrect file name
|
||||
ESP_LOGE(TAG,"ERROR : filename %s format is incorrect", filename.c_str());
|
||||
if (!Update.hasError()) Update.abort();
|
||||
return(ESP_FAIL);
|
||||
} // end incorrect file name
|
||||
else { // file name is correct
|
||||
// check update type : code or data
|
||||
if (filename.endsWith("code.bin")) command=U_FLASH; // update code
|
||||
else command=U_SPIFFS; // update data
|
||||
if (!Update.begin(UPDATE_SIZE_UNKNOWN, command)) { // start update with max available size
|
||||
// error, begin is KO
|
||||
if (!Update.hasError()) Update.abort(); // abort
|
||||
ESP_LOGE(TAG,"ERROR : update.begin error Update.errorString() %s",Update.errorString());
|
||||
return(ESP_FAIL);
|
||||
}
|
||||
} // end file name is correct
|
||||
} // end begin update
|
||||
|
||||
if ((len) && (!Update.hasError())) { // ongoing update if no error encountered
|
||||
if (Update.write(data, len) != len) {
|
||||
// error, write is KO
|
||||
if (!Update.hasError()) Update.abort();
|
||||
ESP_LOGE(TAG,"ERROR : update.write len %d Update.errorString() %s",len, Update.errorString()) ;
|
||||
return(ESP_FAIL);
|
||||
}
|
||||
} // end ongoing update
|
||||
|
||||
if ((last) && (!Update.hasError())) { // last update if no error encountered
|
||||
if (Update.end(true)) { // update end is OKTEST
|
||||
ESP_LOGI(TAG, "Update Success: %u written", index+len);
|
||||
}
|
||||
else { // update end is KO
|
||||
if (!Update.hasError()) Update.abort(); // abort
|
||||
ESP_LOGE(TAG,"ERROR : update end error Update.errorString() %s", Update.errorString());
|
||||
return(ESP_FAIL);
|
||||
}
|
||||
} // last update if no error encountered
|
||||
return(ESP_OK);
|
||||
} // end no error encountered so far during update, process current chunk
|
||||
else { // error encountered so far during update
|
||||
return(ESP_FAIL);
|
||||
}
|
||||
}); // end onUpload
|
||||
|
||||
updateHandler->onRequest([](PsychicRequest *request) { // triggered when update is completed (either OK or KO) and returns request's response (important)
|
||||
String result; // request result
|
||||
// code below is executed when update is finished
|
||||
if (!Update.hasError()) { // update is OK
|
||||
ESP_LOGI(TAG,"Update code or data OK Update.errorString() %s", Update.errorString());
|
||||
result = "<b style='color:green'>Update done for file.</b>";
|
||||
return request->reply(200,"text/html",result.c_str());
|
||||
// ESP.restart(); // restart ESP if needed
|
||||
} // end update is OK
|
||||
else { // update is KO, send request with pretty print error
|
||||
result = " Update.errorString() " + String(Update.errorString());
|
||||
ESP_LOGE(TAG,"ERROR : error %s",result.c_str());
|
||||
return request->reply(500, "text/html", result.c_str());
|
||||
} // end update is KO
|
||||
});
|
||||
|
||||
server.on("/update", HTTP_GET, [](PsychicRequest*request){
|
||||
PsychicFileResponse response(request, LittleFS, "/update.html");
|
||||
return response.send();
|
||||
});
|
||||
|
||||
server.on("/update", HTTP_POST, updateHandler);
|
||||
|
||||
server.on("/restart", HTTP_POST, [](PsychicRequest *request) {
|
||||
String output = "<b style='color:green'>Restarting ...</b>";
|
||||
ESP_LOGI(TAG,"%s",output.c_str());
|
||||
esprestart=true;
|
||||
return request->reply(output.c_str());
|
||||
});
|
||||
} // end onRequest
|
||||
|
||||
} // end setup
|
||||
|
||||
// =======================================================================
|
||||
// loop
|
||||
// =======================================================================
|
||||
void loop() {
|
||||
delay(2000);
|
||||
if (esprestart) ESP.restart(); // restart ESP
|
||||
} // end loop
|
||||
BIN
lib/PsychicHttp/examples/arduino/arduino_ota/code.bin
Normal file
166
lib/PsychicHttp/examples/arduino/arduino_ota/data/update.html
Normal file
@@ -0,0 +1,166 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>PSYCHICHTTP</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<style>
|
||||
input[type=file]::file-selector-button {
|
||||
margin-right: 20px;
|
||||
border: none;
|
||||
background: #3498db; /* blue */
|
||||
padding: 10px 20px;
|
||||
border-radius: 30px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: background .2s ease-in-out;
|
||||
}
|
||||
|
||||
input[type=file]::file-selector-button:hover {
|
||||
background: green ;
|
||||
}
|
||||
|
||||
.drop-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 150px;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
border: 2px dashed #555;
|
||||
color: #444;
|
||||
cursor: pointer;
|
||||
transition: background .2s ease-in-out, border .2s ease-in-out;
|
||||
}
|
||||
|
||||
.drop-container:hover {
|
||||
background: #eee;
|
||||
border-color: #111;
|
||||
}
|
||||
|
||||
.drop-container:hover .drop-title {
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.drop-title {
|
||||
color: #444;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
transition: color .2s ease-in-out;
|
||||
}
|
||||
|
||||
.drop-container.drag-active {
|
||||
background: #eee;
|
||||
border-color: #111;
|
||||
}
|
||||
|
||||
.drop-container.drag-active .drop-title {
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.restart-button {
|
||||
margin-right: 20px;
|
||||
border: none;
|
||||
background: #3498db; /* blue */
|
||||
padding: 10px 20px;
|
||||
border-radius: 30px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: background .2s ease-in-out;
|
||||
}
|
||||
|
||||
.restart-button:hover {
|
||||
background: green ;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="row">
|
||||
<h1>PsychicHttp OTA Update</h1>
|
||||
</div>
|
||||
|
||||
<div><p style="text-align: left">This page allows to test OTA update with PsychicHttp, and file naming convention below :
|
||||
<ul>
|
||||
<li><b>"*code.bin"</b> for code (firmware) update, eg "v1_code.bin"</li>
|
||||
<li><b>"*littlefs.bin"</b> for data (littlefs) update, eg "v1_littlefs.bin" </li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<form id="upload_form" enctype="multipart/form-data" method="post">
|
||||
<label for="images" class="drop-container" id="dropcontainer">
|
||||
<input type="file" class="inputfile" name="updatefile" id="updatefile" value="updatefile" accept=".bin" onchange="uploadFile()" required>
|
||||
<span class="drop-title">Or drop file here</span>
|
||||
<progress id="progressBar" value="0" max="100" style="width:300px;"></progress>
|
||||
<font color='black' id="status"></font>
|
||||
<p id="loaded_n_total"></p>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p style="text-align: left">Update must be done for each of the files provided (code, littlefs). Once updates are made, the ESP32 can be restarted.
|
||||
<button class="restart-button" onclick="esprestartButton()">Restart</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
function uploadFile() {
|
||||
var urltocall = "/update";
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.upload.addEventListener("progress", progressHandler, false);
|
||||
xhr.addEventListener("error", errorHandler, false);
|
||||
var fileupdate = document.getElementById('updatefile'); // get file structure in DOM
|
||||
var file = fileupdate.files[0]; // get file element
|
||||
//alert(file.name+" | "+file.size+" | "+file.type);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
document.getElementById("status").innerHTML = xhr.responseText; // success, show response on page
|
||||
}
|
||||
else {
|
||||
document.getElementById("status").innerHTML = xhr.responseText; // error, show response on page
|
||||
}
|
||||
};
|
||||
var formdata = new FormData();
|
||||
formdata.append("file", file);
|
||||
xhr.open("POST", urltocall); // trigger upload request with file parameter
|
||||
xhr.send(formdata);
|
||||
}
|
||||
|
||||
function progressHandler(event) {
|
||||
// document.getElementById("loaded_n_total").innerHTML = "Chargé " + event.loaded + " octets"; // do not display bytes loaded
|
||||
var percent = (event.loaded / event.total) * 100;
|
||||
document.getElementById("progressBar").value = Math.round(percent);
|
||||
document.getElementById("status").innerHTML = Math.round(percent) + "% uploaded...";
|
||||
if (percent >= 100) {
|
||||
document.getElementById("status").innerHTML = "Writing file ...";
|
||||
}
|
||||
}
|
||||
|
||||
function errorHandler(event) {
|
||||
document.getElementById("status").innerHTML = "<b style='color:red'>Error event catched by errorHandler !</b>";
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
</script>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 59 KiB |
BIN
lib/PsychicHttp/examples/arduino/arduino_ota/littlefs.bin
Normal file
1
lib/PsychicHttp/examples/arduino/data/custom.txt
Normal file
@@ -0,0 +1 @@
|
||||
Custom text file.
|
||||
BIN
lib/PsychicHttp/examples/arduino/data/img/request_flow.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
19
lib/PsychicHttp/examples/arduino/data/server.crt
Normal 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-----
|
||||
28
lib/PsychicHttp/examples/arduino/data/server.key
Normal 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-----
|
||||
15
lib/PsychicHttp/examples/arduino/data/www-ap/index.html
Normal 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>
|
||||
BIN
lib/PsychicHttp/examples/arduino/data/www/alien.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
lib/PsychicHttp/examples/arduino/data/www/favicon.ico
Normal file
|
After Width: | Height: | Size: 66 KiB |
236
lib/PsychicHttp/examples/arduino/data/www/index.html
Normal 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>
|
||||
1
lib/PsychicHttp/examples/arduino/data/www/text.txt
Normal file
@@ -0,0 +1 @@
|
||||
Test File.
|
||||
2
lib/PsychicHttp/examples/arduino/secret.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#define WIFI_SSID "Your_SSID"
|
||||
#define WIFI_PASS "Your_PASS"
|
||||
4
lib/PsychicHttp/examples/esp-idf/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
build/
|
||||
sdkconfig
|
||||
sdkconfig.old
|
||||
components/
|
||||
19
lib/PsychicHttp/examples/esp-idf/CMakeLists.txt
Normal 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)
|
||||
7
lib/PsychicHttp/examples/esp-idf/README.md
Normal 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)
|
||||
1
lib/PsychicHttp/examples/esp-idf/data/custom.txt
Normal file
@@ -0,0 +1 @@
|
||||
Custom text file.
|
||||
BIN
lib/PsychicHttp/examples/esp-idf/data/img/request_flow.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
19
lib/PsychicHttp/examples/esp-idf/data/server.crt
Normal 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-----
|
||||
28
lib/PsychicHttp/examples/esp-idf/data/server.key
Normal 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-----
|
||||
15
lib/PsychicHttp/examples/esp-idf/data/www-ap/index.html
Normal 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>
|
||||
BIN
lib/PsychicHttp/examples/esp-idf/data/www/alien.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
lib/PsychicHttp/examples/esp-idf/data/www/favicon.ico
Normal file
|
After Width: | Height: | Size: 66 KiB |
236
lib/PsychicHttp/examples/esp-idf/data/www/index.html
Normal 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>
|
||||
1
lib/PsychicHttp/examples/esp-idf/data/www/text.txt
Normal file
@@ -0,0 +1 @@
|
||||
Test File.
|
||||
39
lib/PsychicHttp/examples/esp-idf/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/esp-idf/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
|
||||
8
lib/PsychicHttp/examples/esp-idf/main/CMakeLists.txt
Normal 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)
|
||||
510
lib/PsychicHttp/examples/esp-idf/main/main.cpp
Normal 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
|
||||
}
|
||||
2
lib/PsychicHttp/examples/esp-idf/main/secret.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#define WIFI_SSID "WIFI_SSID"
|
||||
#define WIFI_PASS "WIFI_PASS"
|
||||
7
lib/PsychicHttp/examples/esp-idf/partitions_custom.csv
Normal 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
|
||||
|
64
lib/PsychicHttp/examples/esp-idf/sdkconfig.defaults
Normal 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
|
||||
|
||||
39
lib/PsychicHttp/examples/old/esp_ota_http_server/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
.pioenvs
|
||||
.clang_complete
|
||||
.gcc-flags.json
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
# Fortran module files
|
||||
*.mod
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
# Visual Studio/VisualMicro stuff
|
||||
Visual\ Micro
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.suo
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.pio
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/settings.json
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/ipch
|
||||
@@ -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/old/esp_ota_http_server/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
|
||||
@@ -0,0 +1,74 @@
|
||||
; 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
|
||||
|
||||
[common]
|
||||
lib_deps = ArduinoMongoose
|
||||
monitor_speed = 115200
|
||||
monitor_port = /dev/ttyUSB1
|
||||
build_flags =
|
||||
-DENABLE_DEBUG
|
||||
# -DCS_ENABLE_STDIO
|
||||
-DMG_ENABLE_HTTP_STREAMING_MULTIPART=1
|
||||
|
||||
build_flags_secure =
|
||||
-DSIMPLE_SERVER_SECURE
|
||||
-DMG_ENABLE_SSL=1
|
||||
|
||||
# -DMG_SSL_IF=MG_SSL_IF_OPENSSL
|
||||
# -DKR_VERSION
|
||||
|
||||
-DMG_SSL_MBED_DUMMY_RANDOM=1
|
||||
-DMG_SSL_IF=MG_SSL_IF_MBEDTLS
|
||||
-DMG_SSL_IF_MBEDTLS_FREE_CERTS=1
|
||||
-DMG_SSL_IF_MBEDTLS_MAX_FRAG_LEN=2048
|
||||
|
||||
build_flags_auth =
|
||||
-DADMIN_USER='"admin"'
|
||||
-DADMIN_PASS='"admin"'
|
||||
-DADMIN_REALM='"esp_ota_http_server"'
|
||||
|
||||
#[env:huzzah]
|
||||
#platform = espressif8266
|
||||
#board = huzzah
|
||||
#framework = arduino
|
||||
#monitor_speed = ${common.monitor_speed}
|
||||
#monitor_port = ${common.monitor_port}
|
||||
#lib_deps = ${common.lib_deps}
|
||||
#build_flags = ${common.build_flags}
|
||||
|
||||
[env:esp-wrover-kit]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp-wrover-kit
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
monitor_port = ${common.monitor_port}
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags}
|
||||
|
||||
[env:esp-wrover-kit-secure]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp-wrover-kit
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
monitor_port = ${common.monitor_port}
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags} ${common.build_flags_secure}
|
||||
|
||||
[env:esp-wrover-kit-auth]
|
||||
extends = env:esp-wrover-kit
|
||||
build_flags = ${common.build_flags} ${common.build_flags_auth} -ggdb
|
||||
|
||||
#[env:linux_x86_64]
|
||||
#platform = linux_x86_64
|
||||
#framework = arduino
|
||||
#board = generic
|
||||
#lib_deps = ${common.lib_deps}
|
||||
#build_flags = ${common.build_flags}
|
||||
#build_flags = -DSERIAL_TO_CONSOLE
|
||||
@@ -0,0 +1,229 @@
|
||||
//
|
||||
// A simple server implementation showing how to:
|
||||
// * serve static messages
|
||||
// * read GET and POST parameters
|
||||
// * handle missing pages / 404s
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <MongooseCore.h>
|
||||
#include <MongooseHttpServer.h>
|
||||
#include <Update.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#define START_ESP_WIFI
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#define START_ESP_WIFI
|
||||
#else
|
||||
#error Platform not supported
|
||||
#endif
|
||||
|
||||
MongooseHttpServer server;
|
||||
|
||||
const char *ssid = "wifi";
|
||||
const char *password = "password";
|
||||
|
||||
const char *server_pem =
|
||||
"-----BEGIN CERTIFICATE-----\r\n"
|
||||
"MIIDDjCCAfagAwIBAgIBBDANBgkqhkiG9w0BAQsFADA/MRkwFwYDVQQDDBB0ZXN0\r\n"
|
||||
"LmNlc2FudGEuY29tMRAwDgYDVQQKDAdDZXNhbnRhMRAwDgYDVQQLDAd0ZXN0aW5n\r\n"
|
||||
"MB4XDTE2MTExMzEzMTgwMVoXDTI2MDgxMzEzMTgwMVowFDESMBAGA1UEAwwJbG9j\r\n"
|
||||
"YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAro8CW1X0xaGm\r\n"
|
||||
"GkDaMxKbXWA5Lw+seA61tioGrSIQzuqLYeJoFnwVgF0jB5PTj+3EiGMBcA/mh73V\r\n"
|
||||
"AthTFmJBxj+agIp7/cvUBpgfLClmSYL2fZi6Fodz+f9mcry3XRw7O6vlamtWfTX8\r\n"
|
||||
"TAmMSR6PXVBHLgjs5pDOFFmrNAsM5sLYU1/1MFvE2Z9InTI5G437IE1WchRSbpYd\r\n"
|
||||
"HchC39XzpDGoInZB1a3OhcHm+xUtLpMJ0G0oE5VFEynZreZoEIY4JxspQ7LPsay9\r\n"
|
||||
"fx3Tlk09gEMQgVCeCNiQwUxZdtLau2x61LNcdZCKN7FbFLJszv1U2uguELsTmi7E\r\n"
|
||||
"6pHrTziosQIDAQABo0AwPjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIDqDATBgNVHSUE\r\n"
|
||||
"DDAKBggrBgEFBQcDATAPBgNVHREECDAGhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IB\r\n"
|
||||
"AQBUw0hbTcT6crzODO4QAXU7z4Xxn0LkxbXEsoThG1QCVgMc4Bhpx8gyz5CLyHYz\r\n"
|
||||
"AiJOBFEeV0XEqoGTNMMFelR3Q5Tg9y1TYO3qwwAWxe6/brVzpts6NiG1uEMBnBFg\r\n"
|
||||
"oN1x3I9x4NpOxU5MU1dlIxvKs5HQCoNJ8D0SqOX9BV/pZqwEgiCbuWDWQAlxkFpn\r\n"
|
||||
"iLonlkVI5hTuybCSBsa9FEI9M6JJn9LZmlH90FYHeS4t6P8eOJCeekHL0jUG4Iae\r\n"
|
||||
"DMP12h8Sd0yxIKmmZ+Q/p/D/BkuHf5Idv3hgyLkZ4mNznjK49wHaYM+BgBoL3Zeg\r\n"
|
||||
"gJ2sWjUlokrbHswSBLLbUJIF\r\n"
|
||||
"-----END CERTIFICATE-----\r\n";
|
||||
|
||||
const char *server_key =
|
||||
"-----BEGIN PRIVATE KEY-----\r\n"
|
||||
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCujwJbVfTFoaYa\r\n"
|
||||
"QNozEptdYDkvD6x4DrW2KgatIhDO6oth4mgWfBWAXSMHk9OP7cSIYwFwD+aHvdUC\r\n"
|
||||
"2FMWYkHGP5qAinv9y9QGmB8sKWZJgvZ9mLoWh3P5/2ZyvLddHDs7q+Vqa1Z9NfxM\r\n"
|
||||
"CYxJHo9dUEcuCOzmkM4UWas0CwzmwthTX/UwW8TZn0idMjkbjfsgTVZyFFJulh0d\r\n"
|
||||
"yELf1fOkMagidkHVrc6Fweb7FS0ukwnQbSgTlUUTKdmt5mgQhjgnGylDss+xrL1/\r\n"
|
||||
"HdOWTT2AQxCBUJ4I2JDBTFl20tq7bHrUs1x1kIo3sVsUsmzO/VTa6C4QuxOaLsTq\r\n"
|
||||
"ketPOKixAgMBAAECggEAI+uNwpnHirue4Jwjyoqzqd1ZJxQEm5f7UIcJZKsz5kBh\r\n"
|
||||
"ej0KykWybv27bZ2/1UhKPv6QlyzOdXRc1v8I6fxCKLeB5Z2Zsjo1YT4AfCfwwoPO\r\n"
|
||||
"kT3SXTx2YyVpQYcP/HsIvVi8FtALtixbxJHaall9iugwHYr8pN17arihAE6d0wZC\r\n"
|
||||
"JXtXRjUWwjKzXP8FoH4KhyadhHbDwIbbJe3cyLfdvp54Gr0YHha0JcOxYgDYNya4\r\n"
|
||||
"OKxlCluI+hPF31iNzOmFLQVrdYynyPcR6vY5XOiANKE2iNbqCzRb54CvW9WMqObX\r\n"
|
||||
"RD9t3DMOxGsbVNIwyzZndWy13HoQMGnrHfnGak9ueQKBgQDiVtOqYfLnUnTxvJ/b\r\n"
|
||||
"qlQZr2ZmsYPZztxlP+DSqZGPD+WtGSo9+rozWfzjTv3KGIDLvf+GFVmjVHwlLQfd\r\n"
|
||||
"u7eTemWHFc4HK68wruzPO/FdyVpQ4w9v3Usg+ll4a/PDEId0fDMjAr6kk4LC6t8y\r\n"
|
||||
"9fJR0HjOz57jVnlrDt3v50G8BwKBgQDFbw+jRiUxXnBbDyXZLi+I4iGBGdC+CbaJ\r\n"
|
||||
"CmsM6/TsOFc+GRsPwQF1gCGqdaURw76noIVKZJOSc8I+yiwU6izyh/xaju5JiWQd\r\n"
|
||||
"kwbU1j4DE6GnxmT3ARmB7VvCxjaEZEAtICWs1QTKRz7PcTV8yr7Ng1A3VIy+NSpo\r\n"
|
||||
"LFMMmk83hwKBgQDVCEwpLg/mUeHoNVVw95w4oLKNLb+gHeerFLiTDy8FrDzM88ai\r\n"
|
||||
"l37yHly7xflxYia3nZkHpsi7xiUjCINC3BApKyasQoWskh1OgRY653yCfaYYQ96f\r\n"
|
||||
"t3WjEH9trI2+p6wWo1+uMEMnu/9zXoW9/WeaQdGzNg+igh29+jxCNTPVuQKBgGV4\r\n"
|
||||
"CN9vI5pV4QTLqjYOSJvfLDz/mYqxz0BrPE1tz3jAFAZ0PLZCCY/sBGFpCScyJQBd\r\n"
|
||||
"vWNYgYeZOtGuci1llSgov4eDQfBFTlDsyWwFl+VY55IkoqtXw1ZFOQ3HdSlhpKIM\r\n"
|
||||
"jZBgApA7QYq3sjeqs5lHzahCKftvs5XKgfxOKjxtAoGBALdnYe6xkDvGLvI51Yr+\r\n"
|
||||
"Dy0TNcB5W84SxUKvM7DVEomy1QPB57ZpyQaoBq7adOz0pWJXfp7qo4950ZOhBGH1\r\n"
|
||||
"hKbZ6c4ggwVJy2j49EgMok5NGCKvPAtabbR6H8Mz8DW9aXURxhWJvij+Qw1fWK4b\r\n"
|
||||
"7G/qUI9iE5iUU7MkIcLIbTf/\r\n"
|
||||
"-----END PRIVATE KEY-----\r\n";
|
||||
|
||||
const char* server_index =
|
||||
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
|
||||
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
|
||||
"<input type='file' name='update'>"
|
||||
"<input type='submit' value='Update'>"
|
||||
"</form>"
|
||||
"<div id='prg'>progress: 0%</div>"
|
||||
"<script>"
|
||||
"$('form').submit(function(e){"
|
||||
"e.preventDefault();"
|
||||
"var form = $('#upload_form')[0];"
|
||||
"var data = new FormData(form);"
|
||||
"$.ajax({"
|
||||
"url: '/update',"
|
||||
"type: 'POST',"
|
||||
"data: data,"
|
||||
"contentType: false,"
|
||||
"processData:false,"
|
||||
"xhr: function() {"
|
||||
"var xhr = new window.XMLHttpRequest();"
|
||||
"xhr.upload.addEventListener('progress', function(evt) {"
|
||||
"if (evt.lengthComputable) {"
|
||||
"var per = evt.loaded / evt.total;"
|
||||
"$('#prg').html('progress: ' + Math.round(per*100) + '%');"
|
||||
"}"
|
||||
"}, false);"
|
||||
"return xhr;"
|
||||
"},"
|
||||
"success:function(d, s) {"
|
||||
"console.log('success!')"
|
||||
"},"
|
||||
"error: function (a, b, c) {"
|
||||
"}"
|
||||
"});"
|
||||
"});"
|
||||
"</script>";
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
bool updateCompleted = false;
|
||||
|
||||
static void updateError(MongooseHttpServerRequest *request)
|
||||
{
|
||||
MongooseHttpServerResponseStream *resp = request->beginResponseStream();
|
||||
resp->setCode(500);
|
||||
resp->setContentType("text/plain");
|
||||
resp->printf("Error: %d", Update.getError());
|
||||
request->send(resp);
|
||||
|
||||
// Anoyingly this uses Stream rather than Print...
|
||||
Update.printError(Serial);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
#ifdef START_ESP_WIFI
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED)
|
||||
{
|
||||
Serial.printf("WiFi Failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.print("IP Address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
Serial.print("Hostname: ");
|
||||
#ifdef ESP32
|
||||
Serial.println(WiFi.getHostname());
|
||||
#elif defined(ESP8266)
|
||||
Serial.println(WiFi.hostname());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Mongoose.begin();
|
||||
|
||||
#ifdef SIMPLE_SERVER_SECURE
|
||||
if(false == server.begin(443, server_pem, server_key)) {
|
||||
Serial.print("Failed to start server");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
server.begin(80);
|
||||
#endif
|
||||
|
||||
server.on("/$", HTTP_GET, [](MongooseHttpServerRequest *request) {
|
||||
#if defined(ADMIN_USER) && defined(ADMIN_PASS) && defined(ADMIN_REALM)
|
||||
if(false == request->authenticate(ADMIN_USER, ADMIN_PASS)) {
|
||||
request->requestAuthentication(ADMIN_REALM);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
request->send(200, "text/html", server_index);
|
||||
});
|
||||
|
||||
server.on("/update$", HTTP_POST)->
|
||||
onRequest([](MongooseHttpServerRequest *request) {
|
||||
#if defined(ADMIN_USER) && defined(ADMIN_PASS) && defined(ADMIN_REALM)
|
||||
if(false == request->authenticate(ADMIN_USER, ADMIN_PASS)) {
|
||||
request->requestAuthentication(ADMIN_REALM);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
updateCompleted = false;
|
||||
})->
|
||||
onUpload([](MongooseHttpServerRequest *request, int ev, MongooseString filename, uint64_t index, uint8_t *data, size_t len)
|
||||
{
|
||||
if(MG_EV_HTTP_PART_BEGIN == ev) {
|
||||
Serial.printf("Update Start: %s\n", filename.c_str());
|
||||
|
||||
if (!Update.begin()) { //start with max available size
|
||||
updateError(request);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Update.hasError())
|
||||
{
|
||||
Serial.printf("Update Writing %llu\n", index);
|
||||
if(Update.write(data, len) != len) {
|
||||
updateError(request);
|
||||
}
|
||||
}
|
||||
|
||||
if(MG_EV_HTTP_PART_END == ev) {
|
||||
Serial.println("Data finished");
|
||||
if(Update.end(true)) {
|
||||
Serial.printf("Update Success: %lluB\n", index+len);
|
||||
request->send(200, "text/plain", "OK");
|
||||
updateCompleted = true;
|
||||
} else {
|
||||
updateError(request);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
})->
|
||||
onClose([](MongooseHttpServerRequest *request)
|
||||
{
|
||||
if(updateCompleted) {
|
||||
ESP.restart();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
Mongoose.poll(1000);
|
||||
}
|
||||
11
lib/PsychicHttp/examples/old/esp_ota_http_server/test/README
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PIO Unit Testing 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 PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
||||
39
lib/PsychicHttp/examples/old/simple_http_server/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
.pioenvs
|
||||
.clang_complete
|
||||
.gcc-flags.json
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
# Fortran module files
|
||||
*.mod
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
# Visual Studio/VisualMicro stuff
|
||||
Visual\ Micro
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.suo
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.pio
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/settings.json
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/ipch
|
||||
@@ -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/old/simple_http_server/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
|
||||
@@ -0,0 +1,64 @@
|
||||
; 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
|
||||
|
||||
[common]
|
||||
lib_deps = ArduinoMongoose
|
||||
monitor_speed = 115200
|
||||
monitor_port = /dev/ttyUSB1
|
||||
build_flags =
|
||||
-DENABLE_DEBUG
|
||||
# -DCS_ENABLE_STDIO
|
||||
|
||||
build_flags_secure =
|
||||
-DSIMPLE_SERVER_SECURE
|
||||
-DMG_ENABLE_SSL=1
|
||||
|
||||
# -DMG_SSL_IF=MG_SSL_IF_OPENSSL
|
||||
# -DKR_VERSION
|
||||
|
||||
-DMG_SSL_MBED_DUMMY_RANDOM=1
|
||||
-DMG_SSL_IF=MG_SSL_IF_MBEDTLS
|
||||
-DMG_SSL_IF_MBEDTLS_FREE_CERTS=1
|
||||
-DMG_SSL_IF_MBEDTLS_MAX_FRAG_LEN=2048
|
||||
|
||||
[env:huzzah]
|
||||
platform = espressif8266
|
||||
board = huzzah
|
||||
framework = arduino
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
monitor_port = ${common.monitor_port}
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags}
|
||||
|
||||
[env:esp-wrover-kit]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp-wrover-kit
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
monitor_port = ${common.monitor_port}
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags}
|
||||
|
||||
[env:esp-wrover-kit-secure]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp-wrover-kit
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
monitor_port = ${common.monitor_port}
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags} ${common.build_flags_secure}
|
||||
|
||||
#[env:linux_x86_64]
|
||||
#platform = linux_x86_64
|
||||
#framework = arduino
|
||||
#board = generic
|
||||
#lib_deps = ${common.lib_deps}
|
||||
#build_flags = ${common.build_flags}
|
||||
#build_flags = -DSERIAL_TO_CONSOLE
|
||||
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// A simple server implementation showing how to:
|
||||
// * serve static messages
|
||||
// * read GET and POST parameters
|
||||
// * handle missing pages / 404s
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <MongooseCore.h>
|
||||
#include <MongooseHttpServer.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#define START_ESP_WIFI
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#define START_ESP_WIFI
|
||||
#else
|
||||
#error Platform not supported
|
||||
#endif
|
||||
|
||||
MongooseHttpServer server;
|
||||
|
||||
const char *ssid = "wifi";
|
||||
const char *password = "password";
|
||||
|
||||
const char *PARAM_MESSAGE = "message";
|
||||
|
||||
const char *server_pem =
|
||||
"-----BEGIN CERTIFICATE-----\r\n"
|
||||
"MIIDDjCCAfagAwIBAgIBBDANBgkqhkiG9w0BAQsFADA/MRkwFwYDVQQDDBB0ZXN0\r\n"
|
||||
"LmNlc2FudGEuY29tMRAwDgYDVQQKDAdDZXNhbnRhMRAwDgYDVQQLDAd0ZXN0aW5n\r\n"
|
||||
"MB4XDTE2MTExMzEzMTgwMVoXDTI2MDgxMzEzMTgwMVowFDESMBAGA1UEAwwJbG9j\r\n"
|
||||
"YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAro8CW1X0xaGm\r\n"
|
||||
"GkDaMxKbXWA5Lw+seA61tioGrSIQzuqLYeJoFnwVgF0jB5PTj+3EiGMBcA/mh73V\r\n"
|
||||
"AthTFmJBxj+agIp7/cvUBpgfLClmSYL2fZi6Fodz+f9mcry3XRw7O6vlamtWfTX8\r\n"
|
||||
"TAmMSR6PXVBHLgjs5pDOFFmrNAsM5sLYU1/1MFvE2Z9InTI5G437IE1WchRSbpYd\r\n"
|
||||
"HchC39XzpDGoInZB1a3OhcHm+xUtLpMJ0G0oE5VFEynZreZoEIY4JxspQ7LPsay9\r\n"
|
||||
"fx3Tlk09gEMQgVCeCNiQwUxZdtLau2x61LNcdZCKN7FbFLJszv1U2uguELsTmi7E\r\n"
|
||||
"6pHrTziosQIDAQABo0AwPjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIDqDATBgNVHSUE\r\n"
|
||||
"DDAKBggrBgEFBQcDATAPBgNVHREECDAGhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IB\r\n"
|
||||
"AQBUw0hbTcT6crzODO4QAXU7z4Xxn0LkxbXEsoThG1QCVgMc4Bhpx8gyz5CLyHYz\r\n"
|
||||
"AiJOBFEeV0XEqoGTNMMFelR3Q5Tg9y1TYO3qwwAWxe6/brVzpts6NiG1uEMBnBFg\r\n"
|
||||
"oN1x3I9x4NpOxU5MU1dlIxvKs5HQCoNJ8D0SqOX9BV/pZqwEgiCbuWDWQAlxkFpn\r\n"
|
||||
"iLonlkVI5hTuybCSBsa9FEI9M6JJn9LZmlH90FYHeS4t6P8eOJCeekHL0jUG4Iae\r\n"
|
||||
"DMP12h8Sd0yxIKmmZ+Q/p/D/BkuHf5Idv3hgyLkZ4mNznjK49wHaYM+BgBoL3Zeg\r\n"
|
||||
"gJ2sWjUlokrbHswSBLLbUJIF\r\n"
|
||||
"-----END CERTIFICATE-----\r\n";
|
||||
|
||||
const char *server_key =
|
||||
"-----BEGIN PRIVATE KEY-----\r\n"
|
||||
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCujwJbVfTFoaYa\r\n"
|
||||
"QNozEptdYDkvD6x4DrW2KgatIhDO6oth4mgWfBWAXSMHk9OP7cSIYwFwD+aHvdUC\r\n"
|
||||
"2FMWYkHGP5qAinv9y9QGmB8sKWZJgvZ9mLoWh3P5/2ZyvLddHDs7q+Vqa1Z9NfxM\r\n"
|
||||
"CYxJHo9dUEcuCOzmkM4UWas0CwzmwthTX/UwW8TZn0idMjkbjfsgTVZyFFJulh0d\r\n"
|
||||
"yELf1fOkMagidkHVrc6Fweb7FS0ukwnQbSgTlUUTKdmt5mgQhjgnGylDss+xrL1/\r\n"
|
||||
"HdOWTT2AQxCBUJ4I2JDBTFl20tq7bHrUs1x1kIo3sVsUsmzO/VTa6C4QuxOaLsTq\r\n"
|
||||
"ketPOKixAgMBAAECggEAI+uNwpnHirue4Jwjyoqzqd1ZJxQEm5f7UIcJZKsz5kBh\r\n"
|
||||
"ej0KykWybv27bZ2/1UhKPv6QlyzOdXRc1v8I6fxCKLeB5Z2Zsjo1YT4AfCfwwoPO\r\n"
|
||||
"kT3SXTx2YyVpQYcP/HsIvVi8FtALtixbxJHaall9iugwHYr8pN17arihAE6d0wZC\r\n"
|
||||
"JXtXRjUWwjKzXP8FoH4KhyadhHbDwIbbJe3cyLfdvp54Gr0YHha0JcOxYgDYNya4\r\n"
|
||||
"OKxlCluI+hPF31iNzOmFLQVrdYynyPcR6vY5XOiANKE2iNbqCzRb54CvW9WMqObX\r\n"
|
||||
"RD9t3DMOxGsbVNIwyzZndWy13HoQMGnrHfnGak9ueQKBgQDiVtOqYfLnUnTxvJ/b\r\n"
|
||||
"qlQZr2ZmsYPZztxlP+DSqZGPD+WtGSo9+rozWfzjTv3KGIDLvf+GFVmjVHwlLQfd\r\n"
|
||||
"u7eTemWHFc4HK68wruzPO/FdyVpQ4w9v3Usg+ll4a/PDEId0fDMjAr6kk4LC6t8y\r\n"
|
||||
"9fJR0HjOz57jVnlrDt3v50G8BwKBgQDFbw+jRiUxXnBbDyXZLi+I4iGBGdC+CbaJ\r\n"
|
||||
"CmsM6/TsOFc+GRsPwQF1gCGqdaURw76noIVKZJOSc8I+yiwU6izyh/xaju5JiWQd\r\n"
|
||||
"kwbU1j4DE6GnxmT3ARmB7VvCxjaEZEAtICWs1QTKRz7PcTV8yr7Ng1A3VIy+NSpo\r\n"
|
||||
"LFMMmk83hwKBgQDVCEwpLg/mUeHoNVVw95w4oLKNLb+gHeerFLiTDy8FrDzM88ai\r\n"
|
||||
"l37yHly7xflxYia3nZkHpsi7xiUjCINC3BApKyasQoWskh1OgRY653yCfaYYQ96f\r\n"
|
||||
"t3WjEH9trI2+p6wWo1+uMEMnu/9zXoW9/WeaQdGzNg+igh29+jxCNTPVuQKBgGV4\r\n"
|
||||
"CN9vI5pV4QTLqjYOSJvfLDz/mYqxz0BrPE1tz3jAFAZ0PLZCCY/sBGFpCScyJQBd\r\n"
|
||||
"vWNYgYeZOtGuci1llSgov4eDQfBFTlDsyWwFl+VY55IkoqtXw1ZFOQ3HdSlhpKIM\r\n"
|
||||
"jZBgApA7QYq3sjeqs5lHzahCKftvs5XKgfxOKjxtAoGBALdnYe6xkDvGLvI51Yr+\r\n"
|
||||
"Dy0TNcB5W84SxUKvM7DVEomy1QPB57ZpyQaoBq7adOz0pWJXfp7qo4950ZOhBGH1\r\n"
|
||||
"hKbZ6c4ggwVJy2j49EgMok5NGCKvPAtabbR6H8Mz8DW9aXURxhWJvij+Qw1fWK4b\r\n"
|
||||
"7G/qUI9iE5iUU7MkIcLIbTf/\r\n"
|
||||
"-----END PRIVATE KEY-----\r\n";
|
||||
|
||||
static void notFound(MongooseHttpServerRequest *request);
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
#ifdef START_ESP_WIFI
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED)
|
||||
{
|
||||
Serial.printf("WiFi Failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.print("IP Address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
Serial.print("Hostname: ");
|
||||
#ifdef ESP32
|
||||
Serial.println(WiFi.getHostname());
|
||||
#elif defined(ESP8266)
|
||||
Serial.println(WiFi.hostname());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Mongoose.begin();
|
||||
|
||||
#ifdef SIMPLE_SERVER_SECURE
|
||||
if(false == server.begin(443, server_pem, server_key)) {
|
||||
Serial.print("Failed to start server");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
server.begin(80);
|
||||
#endif
|
||||
|
||||
server.on("/$", HTTP_GET, [](MongooseHttpServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello world");
|
||||
});
|
||||
|
||||
// Send a GET request to <IP>/get?message=<message>
|
||||
server.on("/get$", HTTP_GET, [](MongooseHttpServerRequest *request) {
|
||||
String message;
|
||||
if (request->hasParam(PARAM_MESSAGE))
|
||||
{
|
||||
message = request->getParam(PARAM_MESSAGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "No message sent";
|
||||
}
|
||||
request->send(200, "text/plain", "Hello, GET: " + message);
|
||||
});
|
||||
|
||||
// Send a POST request to <IP>/post with a form field message set to <message>
|
||||
server.on("/post$", HTTP_POST, [](MongooseHttpServerRequest *request) {
|
||||
String message;
|
||||
if (request->hasParam(PARAM_MESSAGE))
|
||||
{
|
||||
message = request->getParam(PARAM_MESSAGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "No message sent";
|
||||
}
|
||||
request->send(200, "text/plain", "Hello, POST: " + message);
|
||||
});
|
||||
|
||||
// Test the basic response class
|
||||
server.on("/basic$", HTTP_GET, [](MongooseHttpServerRequest *request) {
|
||||
MongooseHttpServerResponseBasic *resp = request->beginResponse();
|
||||
resp->setCode(200);
|
||||
resp->setContentType("text/html");
|
||||
resp->addHeader("Cache-Control", "max-age=300");
|
||||
resp->addHeader("X-hello", "world");
|
||||
resp->setContent(
|
||||
"<html>\n"
|
||||
"<head>\n"
|
||||
"<title>Basic Page</title>\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
"<h1>Basic Page</h1>\n"
|
||||
"<p>\n"
|
||||
"This page has been sent using the MongooseHttpServerResponseBasic class\n"
|
||||
"</p>\n"
|
||||
"</body>\n"
|
||||
"</html>\n");
|
||||
request->send(resp);
|
||||
});
|
||||
|
||||
// Test the stream response class
|
||||
server.on("/stream$", HTTP_GET, [](MongooseHttpServerRequest *request) {
|
||||
MongooseHttpServerResponseStream *resp = request->beginResponseStream();
|
||||
resp->setCode(200);
|
||||
resp->setContentType("text/html");
|
||||
resp->addHeader("Cache-Control", "max-age=300");
|
||||
resp->addHeader("X-hello", "world");
|
||||
|
||||
resp->println("<html>");
|
||||
resp->println("<head>");
|
||||
resp->println("<title>Stream Page</title>");
|
||||
resp->println("</head>");
|
||||
resp->println("<body>");
|
||||
resp->println("<h1>Stream Page</h1>");
|
||||
resp->println("<p>");
|
||||
resp->println("This page has been sent using the MongooseHttpServerResponseStream class");
|
||||
resp->println("</p>");
|
||||
resp->println("<p>");
|
||||
resp->printf("micros = %lu<br/>", micros());
|
||||
resp->printf("free = %u<br/>", ESP.getFreeHeap());
|
||||
resp->println("</p>");
|
||||
resp->println("</body>");
|
||||
resp->println("</html>");
|
||||
|
||||
request->send(resp);
|
||||
});
|
||||
|
||||
server.onNotFound(notFound);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
Mongoose.poll(1000);
|
||||
Serial.printf("Free memory %u\n", ESP.getFreeHeap());
|
||||
}
|
||||
|
||||
static void notFound(MongooseHttpServerRequest *request)
|
||||
{
|
||||
request->send(404, "text/plain", "Not found");
|
||||
}
|
||||
11
lib/PsychicHttp/examples/old/simple_http_server/test/README
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PIO Unit Testing 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 PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
||||
@@ -0,0 +1,35 @@
|
||||
# Name: REST Client
|
||||
# Id: humao.rest-client
|
||||
# Description: REST Client for Visual Studio Code
|
||||
# Version: 0.21.3
|
||||
# Publisher: Huachao Mao
|
||||
# VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=humao.rest-client
|
||||
|
||||
@baseUrl = http://172.16.0.87
|
||||
|
||||
###
|
||||
|
||||
GET {{baseUrl}}/ HTTP/1.1
|
||||
|
||||
###
|
||||
|
||||
GET {{baseUrl}}/get?message=Hello+World HTTP/1.1
|
||||
|
||||
###
|
||||
|
||||
POST {{baseUrl}}/post HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
|
||||
|
||||
message=Hello+World
|
||||
|
||||
###
|
||||
|
||||
GET {{baseUrl}}/someRandomFile HTTP/1.1
|
||||
|
||||
###
|
||||
|
||||
GET {{baseUrl}}/basic HTTP/1.1
|
||||
|
||||
###
|
||||
|
||||
GET {{baseUrl}}/stream HTTP/1.1
|
||||
39
lib/PsychicHttp/examples/old/simplest_web_server_esp/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
.pioenvs
|
||||
.clang_complete
|
||||
.gcc-flags.json
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
# Fortran module files
|
||||
*.mod
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
# Visual Studio/VisualMicro stuff
|
||||
Visual\ Micro
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.suo
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.pio
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/settings.json
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/ipch
|
||||
@@ -0,0 +1,67 @@
|
||||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Template #1: General project. Test it using existing `platformio.ini`.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio run
|
||||
|
||||
|
||||
#
|
||||
# Template #2: The project is intended to be used as a library with examples.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# env:
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/directory
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,40 @@
|
||||
; 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
|
||||
|
||||
[common]
|
||||
lib_deps = ArduinoMongoose
|
||||
monitor_speed = 115200
|
||||
build_flags =
|
||||
|
||||
[espressif8266]
|
||||
build_flags = -DMG_ESP8266
|
||||
|
||||
[espressif32]
|
||||
build_flags =
|
||||
|
||||
[env:huzzah]
|
||||
platform = espressif8266
|
||||
framework = arduino
|
||||
board = huzzah
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags =
|
||||
${espressif8266.build_flags}
|
||||
${common.build_flags}
|
||||
|
||||
[env:espwroverkit]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp-wrover-kit
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags =
|
||||
${espressif32.build_flags}
|
||||
${common.build_flags}
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2015 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#else
|
||||
#error Platform not supported
|
||||
#endif
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
const char* ssid = "my-ssid";
|
||||
const char* password = "my-password";
|
||||
|
||||
static const char *s_http_port = "80";
|
||||
//static struct mg_serve_http_opts s_http_server_opts;
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *p, void *d) {
|
||||
static const char *reply_fmt =
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"\r\n"
|
||||
"Hello %s\n";
|
||||
|
||||
switch (ev) {
|
||||
case MG_EV_ACCEPT: {
|
||||
char addr[32];
|
||||
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
|
||||
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
||||
Serial.printf("Connection %p from %s\n", nc, addr);
|
||||
break;
|
||||
}
|
||||
case MG_EV_HTTP_REQUEST: {
|
||||
char addr[32];
|
||||
struct http_message *hm = (struct http_message *) p;
|
||||
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
|
||||
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
||||
Serial.printf("HTTP request from %s: %.*s %.*s\n", addr, (int) hm->method.len,
|
||||
hm->method.p, (int) hm->uri.len, hm->uri.p);
|
||||
mg_printf(nc, reply_fmt, addr);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
break;
|
||||
}
|
||||
case MG_EV_CLOSE: {
|
||||
Serial.printf("Connection %p closed\n", nc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.print("Connecting to ");
|
||||
Serial.println(ssid);
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
Serial.printf("Starting web server on port %s\n", s_http_port);
|
||||
nc = mg_bind(&mgr, s_http_port, ev_handler, NULL);
|
||||
if (nc == NULL) {
|
||||
Serial.printf("Failed to create listener\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up HTTP server parameters
|
||||
mg_set_protocol_http_websocket(nc);
|
||||
// s_http_server_opts.document_root = "."; // Serve current directory
|
||||
// s_http_server_opts.enable_directory_listing = "yes";
|
||||
}
|
||||
|
||||
static uint32_t count = 0;
|
||||
void loop()
|
||||
{
|
||||
mg_mgr_poll(&mgr, 1000);
|
||||
//Serial.println(count++);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PIO Unit Testing 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 PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
||||
39
lib/PsychicHttp/examples/old/websocket_chat/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
.pioenvs
|
||||
.clang_complete
|
||||
.gcc-flags.json
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
# Fortran module files
|
||||
*.mod
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
# Visual Studio/VisualMicro stuff
|
||||
Visual\ Micro
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.suo
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.pio
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/settings.json
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/ipch
|
||||
39
lib/PsychicHttp/examples/old/websocket_chat/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/old/websocket_chat/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
|
||||
64
lib/PsychicHttp/examples/old/websocket_chat/platformio.ini
Normal file
@@ -0,0 +1,64 @@
|
||||
; 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
|
||||
|
||||
[common]
|
||||
lib_deps = ArduinoMongoose
|
||||
monitor_speed = 115200
|
||||
monitor_port = /dev/ttyUSB2
|
||||
build_flags =
|
||||
-DENABLE_DEBUG
|
||||
# -DCS_ENABLE_STDIO
|
||||
|
||||
build_flags_secure =
|
||||
-DSIMPLE_SERVER_SECURE
|
||||
-DMG_ENABLE_SSL=1
|
||||
|
||||
# -DMG_SSL_IF=MG_SSL_IF_OPENSSL
|
||||
# -DKR_VERSION
|
||||
|
||||
-DMG_SSL_MBED_DUMMY_RANDOM=1
|
||||
-DMG_SSL_IF=MG_SSL_IF_MBEDTLS
|
||||
-DMG_SSL_IF_MBEDTLS_FREE_CERTS=1
|
||||
-DMG_SSL_IF_MBEDTLS_MAX_FRAG_LEN=2048
|
||||
|
||||
[env:huzzah]
|
||||
platform = espressif8266
|
||||
board = huzzah
|
||||
framework = arduino
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
monitor_port = ${common.monitor_port}
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags}
|
||||
|
||||
[env:esp-wrover-kit]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp-wrover-kit
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
monitor_port = ${common.monitor_port}
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags}
|
||||
|
||||
[env:esp-wrover-kit-secure]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp-wrover-kit
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
monitor_port = ${common.monitor_port}
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags} ${common.build_flags_secure}
|
||||
|
||||
#[env:linux_x86_64]
|
||||
#platform = linux_x86_64
|
||||
#framework = arduino
|
||||
#board = generic
|
||||
#lib_deps = ${common.lib_deps}
|
||||
#build_flags = ${common.build_flags}
|
||||
#build_flags = -DSERIAL_TO_CONSOLE
|
||||
@@ -0,0 +1,235 @@
|
||||
//
|
||||
// A simple server implementation showing how to:
|
||||
// * serve static messages
|
||||
// * read GET and POST parameters
|
||||
// * handle missing pages / 404s
|
||||
//
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <MongooseCore.h>
|
||||
#include <MongooseHttpServer.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#define START_ESP_WIFI
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#define START_ESP_WIFI
|
||||
#else
|
||||
#error Platform not supported
|
||||
#endif
|
||||
|
||||
MongooseHttpServer server;
|
||||
|
||||
const char *ssid = "wifi";
|
||||
const char *password = "password";
|
||||
|
||||
const char *server_pem =
|
||||
"-----BEGIN CERTIFICATE-----\r\n"
|
||||
"MIIDDjCCAfagAwIBAgIBBDANBgkqhkiG9w0BAQsFADA/MRkwFwYDVQQDDBB0ZXN0\r\n"
|
||||
"LmNlc2FudGEuY29tMRAwDgYDVQQKDAdDZXNhbnRhMRAwDgYDVQQLDAd0ZXN0aW5n\r\n"
|
||||
"MB4XDTE2MTExMzEzMTgwMVoXDTI2MDgxMzEzMTgwMVowFDESMBAGA1UEAwwJbG9j\r\n"
|
||||
"YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAro8CW1X0xaGm\r\n"
|
||||
"GkDaMxKbXWA5Lw+seA61tioGrSIQzuqLYeJoFnwVgF0jB5PTj+3EiGMBcA/mh73V\r\n"
|
||||
"AthTFmJBxj+agIp7/cvUBpgfLClmSYL2fZi6Fodz+f9mcry3XRw7O6vlamtWfTX8\r\n"
|
||||
"TAmMSR6PXVBHLgjs5pDOFFmrNAsM5sLYU1/1MFvE2Z9InTI5G437IE1WchRSbpYd\r\n"
|
||||
"HchC39XzpDGoInZB1a3OhcHm+xUtLpMJ0G0oE5VFEynZreZoEIY4JxspQ7LPsay9\r\n"
|
||||
"fx3Tlk09gEMQgVCeCNiQwUxZdtLau2x61LNcdZCKN7FbFLJszv1U2uguELsTmi7E\r\n"
|
||||
"6pHrTziosQIDAQABo0AwPjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIDqDATBgNVHSUE\r\n"
|
||||
"DDAKBggrBgEFBQcDATAPBgNVHREECDAGhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IB\r\n"
|
||||
"AQBUw0hbTcT6crzODO4QAXU7z4Xxn0LkxbXEsoThG1QCVgMc4Bhpx8gyz5CLyHYz\r\n"
|
||||
"AiJOBFEeV0XEqoGTNMMFelR3Q5Tg9y1TYO3qwwAWxe6/brVzpts6NiG1uEMBnBFg\r\n"
|
||||
"oN1x3I9x4NpOxU5MU1dlIxvKs5HQCoNJ8D0SqOX9BV/pZqwEgiCbuWDWQAlxkFpn\r\n"
|
||||
"iLonlkVI5hTuybCSBsa9FEI9M6JJn9LZmlH90FYHeS4t6P8eOJCeekHL0jUG4Iae\r\n"
|
||||
"DMP12h8Sd0yxIKmmZ+Q/p/D/BkuHf5Idv3hgyLkZ4mNznjK49wHaYM+BgBoL3Zeg\r\n"
|
||||
"gJ2sWjUlokrbHswSBLLbUJIF\r\n"
|
||||
"-----END CERTIFICATE-----\r\n";
|
||||
|
||||
const char *server_key =
|
||||
"-----BEGIN PRIVATE KEY-----\r\n"
|
||||
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCujwJbVfTFoaYa\r\n"
|
||||
"QNozEptdYDkvD6x4DrW2KgatIhDO6oth4mgWfBWAXSMHk9OP7cSIYwFwD+aHvdUC\r\n"
|
||||
"2FMWYkHGP5qAinv9y9QGmB8sKWZJgvZ9mLoWh3P5/2ZyvLddHDs7q+Vqa1Z9NfxM\r\n"
|
||||
"CYxJHo9dUEcuCOzmkM4UWas0CwzmwthTX/UwW8TZn0idMjkbjfsgTVZyFFJulh0d\r\n"
|
||||
"yELf1fOkMagidkHVrc6Fweb7FS0ukwnQbSgTlUUTKdmt5mgQhjgnGylDss+xrL1/\r\n"
|
||||
"HdOWTT2AQxCBUJ4I2JDBTFl20tq7bHrUs1x1kIo3sVsUsmzO/VTa6C4QuxOaLsTq\r\n"
|
||||
"ketPOKixAgMBAAECggEAI+uNwpnHirue4Jwjyoqzqd1ZJxQEm5f7UIcJZKsz5kBh\r\n"
|
||||
"ej0KykWybv27bZ2/1UhKPv6QlyzOdXRc1v8I6fxCKLeB5Z2Zsjo1YT4AfCfwwoPO\r\n"
|
||||
"kT3SXTx2YyVpQYcP/HsIvVi8FtALtixbxJHaall9iugwHYr8pN17arihAE6d0wZC\r\n"
|
||||
"JXtXRjUWwjKzXP8FoH4KhyadhHbDwIbbJe3cyLfdvp54Gr0YHha0JcOxYgDYNya4\r\n"
|
||||
"OKxlCluI+hPF31iNzOmFLQVrdYynyPcR6vY5XOiANKE2iNbqCzRb54CvW9WMqObX\r\n"
|
||||
"RD9t3DMOxGsbVNIwyzZndWy13HoQMGnrHfnGak9ueQKBgQDiVtOqYfLnUnTxvJ/b\r\n"
|
||||
"qlQZr2ZmsYPZztxlP+DSqZGPD+WtGSo9+rozWfzjTv3KGIDLvf+GFVmjVHwlLQfd\r\n"
|
||||
"u7eTemWHFc4HK68wruzPO/FdyVpQ4w9v3Usg+ll4a/PDEId0fDMjAr6kk4LC6t8y\r\n"
|
||||
"9fJR0HjOz57jVnlrDt3v50G8BwKBgQDFbw+jRiUxXnBbDyXZLi+I4iGBGdC+CbaJ\r\n"
|
||||
"CmsM6/TsOFc+GRsPwQF1gCGqdaURw76noIVKZJOSc8I+yiwU6izyh/xaju5JiWQd\r\n"
|
||||
"kwbU1j4DE6GnxmT3ARmB7VvCxjaEZEAtICWs1QTKRz7PcTV8yr7Ng1A3VIy+NSpo\r\n"
|
||||
"LFMMmk83hwKBgQDVCEwpLg/mUeHoNVVw95w4oLKNLb+gHeerFLiTDy8FrDzM88ai\r\n"
|
||||
"l37yHly7xflxYia3nZkHpsi7xiUjCINC3BApKyasQoWskh1OgRY653yCfaYYQ96f\r\n"
|
||||
"t3WjEH9trI2+p6wWo1+uMEMnu/9zXoW9/WeaQdGzNg+igh29+jxCNTPVuQKBgGV4\r\n"
|
||||
"CN9vI5pV4QTLqjYOSJvfLDz/mYqxz0BrPE1tz3jAFAZ0PLZCCY/sBGFpCScyJQBd\r\n"
|
||||
"vWNYgYeZOtGuci1llSgov4eDQfBFTlDsyWwFl+VY55IkoqtXw1ZFOQ3HdSlhpKIM\r\n"
|
||||
"jZBgApA7QYq3sjeqs5lHzahCKftvs5XKgfxOKjxtAoGBALdnYe6xkDvGLvI51Yr+\r\n"
|
||||
"Dy0TNcB5W84SxUKvM7DVEomy1QPB57ZpyQaoBq7adOz0pWJXfp7qo4950ZOhBGH1\r\n"
|
||||
"hKbZ6c4ggwVJy2j49EgMok5NGCKvPAtabbR6H8Mz8DW9aXURxhWJvij+Qw1fWK4b\r\n"
|
||||
"7G/qUI9iE5iUU7MkIcLIbTf/\r\n"
|
||||
"-----END PRIVATE KEY-----\r\n";
|
||||
|
||||
const char *index_page =
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html lang=\"en\">\n"
|
||||
"<head>\n"
|
||||
" <meta charset=\"utf-8\" />\n"
|
||||
" <title>WebSocket Test</title>\n"
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
|
||||
" <style type=\"text/css\">\n"
|
||||
" body {\n"
|
||||
" background-color: #789; margin: 0;\n"
|
||||
" padding: 0; font: 14px Helvetica, Arial, sans-serif;\n"
|
||||
" }\n"
|
||||
" div.content {\n"
|
||||
" width: 800px; margin: 2em auto; padding: 20px 50px;\n"
|
||||
" background-color: #fff; border-radius: 1em;\n"
|
||||
" }\n"
|
||||
" #messages {\n"
|
||||
" border: 2px solid #fec; border-radius: 1em;\n"
|
||||
" height: 10em; overflow: scroll; padding: 0.5em 1em;\n"
|
||||
" }\n"
|
||||
" a:link, a:visited { color: #69c; text-decoration: none; }\n"
|
||||
" @media (max-width: 700px) {\n"
|
||||
" body { background-color: #fff; }\n"
|
||||
" div.content {\n"
|
||||
" width: auto; margin: 0 auto; border-radius: 0;\n"
|
||||
" padding: 1em;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"</style>\n"
|
||||
"\n"
|
||||
"<script language=\"javascript\" type=\"text/javascript\">\n"
|
||||
"\n"
|
||||
" var rooms = [];\n"
|
||||
" var ws = new WebSocket(\'ws://\' + location.host + \'/ws\');\n"
|
||||
"\n"
|
||||
" if (!window.console) { window.console = { log: function() {} } };\n"
|
||||
"\n"
|
||||
" ws.onopen = function(ev) { console.log(ev); };\n"
|
||||
" ws.onerror = function(ev) { console.log(ev); };\n"
|
||||
" ws.onclose = function(ev) { console.log(ev); };\n"
|
||||
" ws.onmessage = function(ev) {\n"
|
||||
" console.log(ev);\n"
|
||||
" var div = document.createElement(\'div\');\n"
|
||||
" div.innerHTML = ev.data;\n"
|
||||
" document.getElementById(\'messages\').appendChild(div);\n"
|
||||
"\n"
|
||||
" };\n"
|
||||
"\n"
|
||||
" window.onload = function() {\n"
|
||||
" document.getElementById(\'send_button\').onclick = function(ev) {\n"
|
||||
" var msg = document.getElementById(\'send_input\').value;\n"
|
||||
" document.getElementById(\'send_input\').value = \'\';\n"
|
||||
" ws.send(msg);\n"
|
||||
" };\n"
|
||||
" document.getElementById(\'send_input\').onkeypress = function(ev) {\n"
|
||||
" if (ev.keyCode == 13 || ev.which == 13) {\n"
|
||||
" document.getElementById(\'send_button\').click();\n"
|
||||
" }\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
"</script>\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
" <div class=\"content\">\n"
|
||||
" <h1>Websocket PubSub Demonstration</h1>\n"
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" This page demonstrates how Mongoose could be used to implement\n"
|
||||
" <a href=\"http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern\">\n"
|
||||
" publish–subscribe pattern</a>. Open this page in several browser\n"
|
||||
" windows. Each window initiates persistent\n"
|
||||
" <a href=\"http://en.wikipedia.org/wiki/WebSocket\">WebSocket</a>\n"
|
||||
" connection with the server, making each browser window a websocket client.\n"
|
||||
" Send messages, and see messages sent by other clients.\n"
|
||||
" </p>\n"
|
||||
"\n"
|
||||
" <div id=\"messages\">\n"
|
||||
" </div>\n"
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" <input type=\"text\" id=\"send_input\" />\n"
|
||||
" <button id=\"send_button\">Send Message</button>\n"
|
||||
" </p>\n"
|
||||
" </div>\n"
|
||||
"</body>\n"
|
||||
"</html>\n";
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void broadcast(MongooseHttpWebSocketConnection *from, MongooseString msg)
|
||||
{
|
||||
char buf[500];
|
||||
char addr[32];
|
||||
mg_sock_addr_to_str(from->getRemoteAddress(), addr, sizeof(addr),
|
||||
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s %.*s", addr, (int) msg.length(), msg.c_str());
|
||||
printf("%s\n", buf);
|
||||
server.sendAll(from, buf);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
#ifdef START_ESP_WIFI
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED)
|
||||
{
|
||||
Serial.printf("WiFi Failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.print("IP Address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
Serial.print("Hostname: ");
|
||||
#ifdef ESP32
|
||||
Serial.println(WiFi.getHostname());
|
||||
#elif defined(ESP8266)
|
||||
Serial.println(WiFi.hostname());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Mongoose.begin();
|
||||
|
||||
#ifdef SIMPLE_SERVER_SECURE
|
||||
if(false == server.begin(443, server_pem, server_key)) {
|
||||
Serial.print("Failed to start server");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
server.begin(80);
|
||||
#endif
|
||||
|
||||
server.on("/$", HTTP_GET, [](MongooseHttpServerRequest *request) {
|
||||
request->send(200, "text/html", index_page);
|
||||
});
|
||||
|
||||
// Test the stream response class
|
||||
server.on("/ws$")->
|
||||
onConnect([](MongooseHttpWebSocketConnection *connection) {
|
||||
broadcast(connection, MongooseString("++ joined"));
|
||||
})->
|
||||
onClose([](MongooseHttpServerRequest *c) {
|
||||
MongooseHttpWebSocketConnection *connection = static_cast<MongooseHttpWebSocketConnection *>(c);
|
||||
broadcast(connection, MongooseString("++ left"));
|
||||
})->
|
||||
onFrame([](MongooseHttpWebSocketConnection *connection, int flags, uint8_t *data, size_t len) {
|
||||
broadcast(connection, MongooseString((const char *)data, len));
|
||||
});
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
Mongoose.poll(1000);
|
||||
Serial.printf("Free memory %u\n", ESP.getFreeHeap());
|
||||
}
|
||||
11
lib/PsychicHttp/examples/old/websocket_chat/test/README
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PIO Unit Testing 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 PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
||||
35
lib/PsychicHttp/examples/old/websocket_chat/test/tests.rest
Normal file
@@ -0,0 +1,35 @@
|
||||
# Name: REST Client
|
||||
# Id: humao.rest-client
|
||||
# Description: REST Client for Visual Studio Code
|
||||
# Version: 0.21.3
|
||||
# Publisher: Huachao Mao
|
||||
# VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=humao.rest-client
|
||||
|
||||
@baseUrl = http://172.16.0.87
|
||||
|
||||
###
|
||||
|
||||
GET {{baseUrl}}/ HTTP/1.1
|
||||
|
||||
###
|
||||
|
||||
GET {{baseUrl}}/get?message=Hello+World HTTP/1.1
|
||||
|
||||
###
|
||||
|
||||
POST {{baseUrl}}/post HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
|
||||
|
||||
message=Hello+World
|
||||
|
||||
###
|
||||
|
||||
GET {{baseUrl}}/someRandomFile HTTP/1.1
|
||||
|
||||
###
|
||||
|
||||
GET {{baseUrl}}/basic HTTP/1.1
|
||||
|
||||
###
|
||||
|
||||
GET {{baseUrl}}/stream HTTP/1.1
|
||||
6
lib/PsychicHttp/examples/platformio/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.pio
|
||||
.vscode/
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
1
lib/PsychicHttp/examples/platformio/data/custom.txt
Normal file
@@ -0,0 +1 @@
|
||||
Custom text file.
|
||||
BIN
lib/PsychicHttp/examples/platformio/data/img/request_flow.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
19
lib/PsychicHttp/examples/platformio/data/server.crt
Normal 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-----
|
||||
28
lib/PsychicHttp/examples/platformio/data/server.key
Normal 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-----
|
||||
15
lib/PsychicHttp/examples/platformio/data/www-ap/index.html
Normal 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>
|
||||
BIN
lib/PsychicHttp/examples/platformio/data/www/alien.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
lib/PsychicHttp/examples/platformio/data/www/favicon.ico
Normal file
|
After Width: | Height: | Size: 66 KiB |
236
lib/PsychicHttp/examples/platformio/data/www/index.html
Normal 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((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;
|
||||
}
|
||||
}
|
||||
</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>
|
||||
1
lib/PsychicHttp/examples/platformio/data/www/text.txt
Normal file
@@ -0,0 +1 @@
|
||||
Test File.
|
||||
39
lib/PsychicHttp/examples/platformio/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/platformio/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
|
||||
30
lib/PsychicHttp/examples/platformio/platformio.ini
Normal file
@@ -0,0 +1,30 @@
|
||||
; 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 = esp32-s3-devkitc-1
|
||||
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
|
||||
;-D ENABLE_ASYNC
|
||||
|
||||
; [env:arduino3]
|
||||
; platform = https://github.com/platformio/platform-espressif32.git
|
||||
; platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32#master
|
||||
583
lib/PsychicHttp/examples/platformio/src/main.cpp
Normal file
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
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 <PsychicHttpsServer.h> //uncomment this to enable HTTPS / SSL
|
||||
|
||||
#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 PSY_ENABLE_SSL to enable ssl
|
||||
#ifdef PSY_ENABLE_SSL
|
||||
bool app_enable_ssl = true;
|
||||
String server_cert;
|
||||
String server_key;
|
||||
#endif
|
||||
|
||||
//our main server object
|
||||
#ifdef PSY_ENABLE_SSL
|
||||
PsychicHttpsServer server;
|
||||
#else
|
||||
PsychicHttpServer server;
|
||||
#endif
|
||||
PsychicWebSocketHandler websocketHandler;
|
||||
PsychicEventSource eventSource;
|
||||
|
||||
//NTP server stuff
|
||||
const char *ntpServer1 = "pool.ntp.org";
|
||||
const char *ntpServer2 = "time.nist.gov";
|
||||
const long gmtOffset_sec = 0;
|
||||
const int daylightOffset_sec = 0;
|
||||
struct tm timeinfo;
|
||||
|
||||
// Callback function (gets called when time adjusts via NTP)
|
||||
void timeAvailable(struct timeval *t)
|
||||
{
|
||||
if (!getLocalTime(&timeinfo)) {
|
||||
Serial.println("Failed to obtain time");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.print("NTP update: ");
|
||||
char buffer[40];
|
||||
strftime(buffer, 40, "%FT%T%z", &timeinfo);
|
||||
Serial.println(buffer);
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
//Setup our NTP to get the current time.
|
||||
sntp_set_time_sync_notification_cb(timeAvailable);
|
||||
sntp_servermode_dhcp(1); // (optional)
|
||||
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);
|
||||
|
||||
//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 PSY_ENABLE_SSL
|
||||
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 PSY_ENABLE_SSL
|
||||
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
|
||||
|
||||
DefaultHeaders::Instance().addHeader("Server", "PsychicHttp");
|
||||
|
||||
//serve static files from LittleFS/www on / only to clients on same wifi network
|
||||
//this is where our /index.html file lives
|
||||
// curl -i http://psychic.local/
|
||||
PsychicStaticFileHandler* handler = server.serveStatic("/", LittleFS, "/www/");
|
||||
handler->setFilter(ON_STA_FILTER);
|
||||
handler->setCacheControl("max-age=60");
|
||||
|
||||
//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.
|
||||
// curl -i http://psychic.local/img/request_flow.png
|
||||
server.serveStatic("/img", LittleFS, "/img/");
|
||||
|
||||
//you can also serve single files
|
||||
// curl -i http://psychic.local/myfile.txt
|
||||
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->remoteIP().toString());
|
||||
});
|
||||
|
||||
//example callback everytime a connection is closed
|
||||
server.onClose([](PsychicClient *client) {
|
||||
Serial.printf("[http] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString());
|
||||
});
|
||||
|
||||
//api - json message passed in as post body
|
||||
// curl -i -X POST -H "Content-Type: application/json" -d '{"foo":"bar"}' http://psychic.local/api
|
||||
server.on("/api", HTTP_POST, [](PsychicRequest *request, JsonVariant &json)
|
||||
{
|
||||
JsonObject input = json.as<JsonObject>();
|
||||
|
||||
//create our response json
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request);
|
||||
JsonObject output = response.getRoot();
|
||||
|
||||
output["msg"] = "status";
|
||||
output["status"] = "success";
|
||||
output["millis"] = millis();
|
||||
|
||||
//work with some params
|
||||
if (input.containsKey("foo"))
|
||||
{
|
||||
String foo = input["foo"];
|
||||
output["foo"] = foo;
|
||||
}
|
||||
|
||||
return response.send();
|
||||
});
|
||||
|
||||
//ip - get info about the client
|
||||
// curl -i http://psychic.local/ip
|
||||
server.on("/ip", HTTP_GET, [](PsychicRequest *request)
|
||||
{
|
||||
String output = "Your IP is: " + request->client()->remoteIP().toString();
|
||||
return request->reply(output.c_str());
|
||||
});
|
||||
|
||||
//client connect/disconnect to a url
|
||||
// curl -i http://psychic.local/handler
|
||||
PsychicWebHandler *connectionHandler = new PsychicWebHandler();
|
||||
connectionHandler->onRequest([](PsychicRequest *request)
|
||||
{
|
||||
return request->reply("OK");
|
||||
});
|
||||
connectionHandler->onOpen([](PsychicClient *client) {
|
||||
Serial.printf("[handler] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString());
|
||||
});
|
||||
connectionHandler->onClose([](PsychicClient *client) {
|
||||
Serial.printf("[handler] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString());
|
||||
});
|
||||
|
||||
//add it to our server
|
||||
server.on("/handler", connectionHandler);
|
||||
|
||||
//api - parameters passed in via query eg. /api?foo=bar
|
||||
// curl -i 'http://psychic.local/api?foo=bar'
|
||||
server.on("/api", HTTP_GET, [](PsychicRequest *request)
|
||||
{
|
||||
//showcase some of the variables
|
||||
Serial.println(request->host());
|
||||
Serial.println(request->uri());
|
||||
Serial.println(request->path());
|
||||
Serial.println(request->queryString());
|
||||
|
||||
//create a response object
|
||||
//create our response json
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request);
|
||||
JsonObject output = response.getRoot();
|
||||
|
||||
output["msg"] = "status";
|
||||
output["status"] = "success";
|
||||
output["millis"] = millis();
|
||||
|
||||
//work with some params
|
||||
if (request->hasParam("foo"))
|
||||
{
|
||||
String foo = request->getParam("foo")->value();
|
||||
output["foo"] = foo;
|
||||
}
|
||||
|
||||
return response.send();
|
||||
});
|
||||
|
||||
//JsonResponse example
|
||||
// curl -i http://psychic.local/json
|
||||
server.on("/json", HTTP_GET, [](PsychicRequest *request)
|
||||
{
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request);
|
||||
|
||||
char key[16];
|
||||
char value[32];
|
||||
JsonObject root = response.getRoot();
|
||||
for (int i=0; i<100; i++)
|
||||
{
|
||||
sprintf(key, "key%d", i);
|
||||
sprintf(value, "value is %d", i);
|
||||
root[key] = value;
|
||||
}
|
||||
|
||||
return response.send();
|
||||
});
|
||||
|
||||
//how to redirect a request
|
||||
// curl -i http://psychic.local/redirect
|
||||
server.on("/redirect", HTTP_GET, [](PsychicRequest *request)
|
||||
{
|
||||
return request->redirect("/alien.png");
|
||||
});
|
||||
|
||||
//how to do basic auth
|
||||
// curl -i --user admin:admin http://psychic.local/auth-basic
|
||||
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
|
||||
// curl -i --user admin:admin http://psychic.local/auth-digest
|
||||
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
|
||||
// curl -i -b cookie.txt -c cookie.txt http://psychic.local/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[10];
|
||||
sprintf(cookie, "%i", counter);
|
||||
|
||||
response.setCookie("counter", cookie);
|
||||
response.setContent(cookie);
|
||||
return response.send();
|
||||
});
|
||||
|
||||
//example of getting POST variables
|
||||
// curl -i -d "param1=value1¶m2=value2" -X POST http://psychic.local/post
|
||||
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.
|
||||
// curl -i http://psychic.local/404
|
||||
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
|
||||
// use http://psychic.local/ to test
|
||||
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)
|
||||
{
|
||||
if (request->hasParam("file_upload"))
|
||||
{
|
||||
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());
|
||||
}
|
||||
else
|
||||
return request->reply("No upload.");
|
||||
});
|
||||
|
||||
//wildcard basic file upload - POST to /upload/filename.ext
|
||||
// use http://psychic.local/ to test
|
||||
server.on("/multipart", HTTP_POST, multipartHandler);
|
||||
|
||||
//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);
|
||||
return request->reply(frame);
|
||||
});
|
||||
websocketHandler.onClose([](PsychicWebSocketClient *client) {
|
||||
Serial.printf("[socket] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString());
|
||||
});
|
||||
server.on("/ws", &websocketHandler);
|
||||
|
||||
//EventSource server
|
||||
// curl -i -N http://psychic.local/events
|
||||
eventSource.onOpen([](PsychicEventSourceClient *client) {
|
||||
Serial.printf("[eventsource] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString());
|
||||
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());
|
||||
});
|
||||
server.on("/events", &eventSource);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long lastUpdate = 0;
|
||||
char output[60];
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (millis() - lastUpdate > 2000)
|
||||
{
|
||||
sprintf(output, "Millis: %d\n", millis());
|
||||
websocketHandler.sendAll(output);
|
||||
|
||||
sprintf(output, "%d", millis());
|
||||
eventSource.send(output, "millis", millis(), 0);
|
||||
|
||||
lastUpdate = millis();
|
||||
}
|
||||
}
|
||||
2
lib/PsychicHttp/examples/platformio/src/secret.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#define WIFI_SSID "Your_SSID"
|
||||
#define WIFI_PASS "Your_PASS"
|
||||
11
lib/PsychicHttp/examples/platformio/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
|
||||
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
|
After Width: | Height: | Size: 66 KiB |
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,2 @@
|
||||
#define WIFI_SSID "Your_SSID"
|
||||
#define WIFI_PASS "Your_PASS"
|
||||
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
|
||||