From d45bcc1378e9d9b1d454e1e8b8136e5a4cf173a0 Mon Sep 17 00:00:00 2001 From: Kenneth Barbour Date: Thu, 5 Apr 2018 12:48:08 -0400 Subject: [PATCH 1/3] Removed unused constructors for HttpResponse --- Makefile | 2 +- src/HttpResponse.cpp | 31 ------------------------------- src/HttpResponse.h | 4 ---- 3 files changed, 1 insertion(+), 36 deletions(-) diff --git a/Makefile b/Makefile index 3b1d6e4..7d01de4 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ MODULES= src test SOURCES ?= $(wildcard $(addsuffix /*.cpp, $(MODULES))) OBJECTS := $(addsuffix .o, $(addprefix .build/, $(basename $(SOURCES)))) DEPFILES := $(subst .o,.dep, $(subst .build/,.deps/, $(OBJECTS))) -TESTCPPFLAGS = -D_TEST_ $(addprefix -I, $(MODULES)) -Iarduino -g +TESTCPPFLAGS = -D_TEST_ $(addprefix -I, $(MODULES)) -g CPPDEPFLAGS = -MMD -MP -MF .deps/$(basename $<).dep TEST_TARGET=run_tests diff --git a/src/HttpResponse.cpp b/src/HttpResponse.cpp index 45f467a..83fc945 100644 --- a/src/HttpResponse.cpp +++ b/src/HttpResponse.cpp @@ -10,22 +10,6 @@ HttpResponse::HttpResponse(Stream& buffer): httpver() {}; -HttpResponse::HttpResponse(Stream& buffer, unsigned int code): - content(&buffer), - code(code), - reason(), - httpver() -{}; - -HttpResponse::HttpResponse(Stream& buffer, unsigned int code, const char * reason): - content(&buffer), - code(code), - reason(), - httpver() -{ - setReason(reason); -} - HttpResponse::HttpResponse(): content(), code(200), @@ -33,21 +17,6 @@ HttpResponse::HttpResponse(): httpver() {}; -HttpResponse::HttpResponse(unsigned int code): - content(), - code(code), - reason(), - httpver() -{} - -HttpResponse::HttpResponse(unsigned int code, const char * reason): - content(), - code(code), - reason(), - httpver() -{ - setReason(reason); -} const char * HttpResponse::setReason(const char * reason) { diff --git a/src/HttpResponse.h b/src/HttpResponse.h index 079e44e..ed6dd52 100644 --- a/src/HttpResponse.h +++ b/src/HttpResponse.h @@ -16,11 +16,7 @@ class HttpResponse { public: HttpResponse(Stream&); // TODO: only cnstr needed by WebKernel, add httpver param - HttpResponse(Stream&, unsigned int); // TODO: remove unnecessary constructors - HttpResponse(Stream&, unsigned int, const char *); HttpResponse(); - HttpResponse(unsigned int code); - HttpResponse(unsigned int code, const char *); ~HttpResponse(); unsigned int code; HttpHeaders headers; From 6b1dedc9a51bf1e84fdd8419c9027ca615e60e2e Mon Sep 17 00:00:00 2001 From: Kenneth Barbour Date: Sun, 8 Apr 2018 10:52:03 -0400 Subject: [PATCH 2/3] WebKernel::dispatch accepts a HttpResponse --- src/HttpResponse.cpp | 3 +-- src/WebKernel.cpp | 22 ++++++++++++++++++---- src/WebKernel.h | 2 +- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/HttpResponse.cpp b/src/HttpResponse.cpp index 83fc945..3afbe25 100644 --- a/src/HttpResponse.cpp +++ b/src/HttpResponse.cpp @@ -10,8 +10,7 @@ HttpResponse::HttpResponse(Stream& buffer): httpver() {}; -HttpResponse::HttpResponse(): - content(), +HttpResponse::HttpResponse(): code(200), reason(), httpver() diff --git a/src/WebKernel.cpp b/src/WebKernel.cpp index 5829cde..bfee54a 100644 --- a/src/WebKernel.cpp +++ b/src/WebKernel.cpp @@ -40,7 +40,11 @@ void WebKernel::handleClients() keepClient = false; break; } - dispatchRequest(); +#ifdef DEBUG + Serial.println("Dispatching"); +#endif + HttpResponse response; + dispatchRequest(response); keepClient = false; break; } @@ -61,12 +65,22 @@ void WebKernel::handleClients() } -void WebKernel::dispatchRequest() +void WebKernel::dispatchRequest(HttpResponse& response) { - HttpResponse response; response.setHttpVersion(_request.getHttpVersion()); - if (_initHandler != nullptr) +#ifdef DEBUG + Serial.print("Is init handler set? "); +#endif + if (_initHandler != nullptr) { +#ifdef DEBUG + Serial.println("Yes"); +#endif _initHandler(_request, response); + } else { +#ifdef DEBUG + Serial.println("No"); +#endif + } _dispatcher.handle(_request, response); response.send(_client); if (_terminateHandler != nullptr) diff --git a/src/WebKernel.h b/src/WebKernel.h index 73bf172..711dcd5 100644 --- a/src/WebKernel.h +++ b/src/WebKernel.h @@ -69,7 +69,7 @@ class WebKernel WebKernelState _state; unsigned long int _stateChange; - void dispatchRequest(); + void dispatchRequest(HttpResponse& response); void (*_initHandler)(HttpRequest&, HttpResponse&); void (*_terminateHandler)(const HttpRequest&, const HttpResponse&); From ce0b154921b3b3c4764be41ec7f2a3c68fb703c1 Mon Sep 17 00:00:00 2001 From: Kenneth Barbour Date: Mon, 16 Apr 2018 21:01:19 -0400 Subject: [PATCH 3/3] some basic examples that toggle the builtin ESP8266 LED --- examples/basic-webapp/basic-webapp.ino | 125 ++++++++++++++++ examples/filesystem-webapp/data/index.html | 13 ++ examples/filesystem-webapp/data/led.txt | 1 + examples/filesystem-webapp/data/scripts.js | 28 ++++ examples/filesystem-webapp/data/styles.css | 0 .../filesystem-webapp/filesystem-webapp.ino | 138 ++++++++++++++++++ 6 files changed, 305 insertions(+) create mode 100644 examples/basic-webapp/basic-webapp.ino create mode 100644 examples/filesystem-webapp/data/index.html create mode 100644 examples/filesystem-webapp/data/led.txt create mode 100644 examples/filesystem-webapp/data/scripts.js create mode 100644 examples/filesystem-webapp/data/styles.css create mode 100644 examples/filesystem-webapp/filesystem-webapp.ino diff --git a/examples/basic-webapp/basic-webapp.ino b/examples/basic-webapp/basic-webapp.ino new file mode 100644 index 0000000..cb9c94c --- /dev/null +++ b/examples/basic-webapp/basic-webapp.ino @@ -0,0 +1,125 @@ +/** + * Basic WebKernel Web Application Example + * In this example, the ESP8266 acts as a standalone access point and allows + * the user to control the builtin LED pin over the web. + * + * To see this example working: + * 1. Open the Serial Monitor + * 2. Upload to the ESP8266 + * 3. Note the WiFi SSID, password, and IP Address in the serial monitor + * 4. Connect to the newly created WiFi Access Point + * 5. Using the web browser or curl, control the LED pin with the following + * URLs: (assuming the default configuration) + * + * * Show the LED status: http://192.168.4.1/led + * * Toggle the LED; http://192.168.4.1/led/toggle + */ +#include +#include + +const char* network_ssid = "ESPThing"; // Name of the WiFi Access Point to create +const char* network_password = ""; // Password for WiFi (empty is no password) + +const uint8_t ledPin = LED_BUILTIN; // Arduino Pin controlling the LED + +uint8_t content_buffer[64] = {}; // global buffer to contain response data +Buffer content(content_buffer, // print response data to this variable + sizeof(content_buffer)/sizeof(content_buffer[0])); + + +/** + * Handle requests for the site index (URL: /) + */ +void index(HttpRequest& request, HttpResponse& response) +{ + response.headers.set("Content-Type","text/plain"); + content.println("Site Index"); +} + +/** + * Handle requests to show the LED status + * This function should print the status of the LED pin to content + */ +void show_led(HttpRequest& request, HttpResponse& response) +{ + response.headers.set("Content-Type","text/plain"); + if (digitalRead(ledPin)) + content.println("off"); + else content.println("on"); +} + +/** + * Handle requests to toggle the LED state + * Afterwards, send the user the LED's status + */ +void toggle_led(HttpRequest& request, HttpResponse& response) +{ + digitalWrite(ledPin, !(digitalRead(ledPin))); + show_led(request, response); +} + +/** + * Before any request is handled, clear the content buffer from the last + * request and setup the buffer for the new request. + */ +void init_webkernel(HttpRequest& request, HttpResponse& response) +{ + content.clear(); // clear previously printed content + response.content = &content; // attach content buffer to the response +} + +/** + * Setup a WiFi connection + * Called during setup routine + */ +void setup_wifi() +{ + // Setup WiFi - Use the ESP as a standalone access point + WiFi.mode(WIFI_AP); + WiFi.softAP(network_ssid, network_password); + Serial.println("WiFi SSID: "); + Serial.print(network_ssid); + Serial.print("WiFi password: "); + Serial.println(network_password); + Serial.print("AP IP Address: "); + Serial.println(WiFi.softAPIP()); +} + +/** + * Define an array of Routes for the WebKernel + * Each route contains: + * * one or more methods (ex: (GET | POST) handles both GET and POST requests), + * * a URL pattern (* matches anything until a slash '/', # matches until the end) + * * the name of a function accepting arguments: HttpRequest&, HttpResponse& + */ +Route routes[] = { + {GET, "/", index}, + {GET, "/led", show_led}, + {GET, "/led/toggle", toggle_led} +}; + +WebKernel webKernel(80, routes, sizeof(routes)/sizeof(routes[0])); // Global WebKernel + +void setup() { + + // Initialize Pins + pinMode(ledPin, OUTPUT); + digitalWrite(ledPin, HIGH); + + // Start Serial + Serial.begin(115200); + delay(10); + + // Setup wifi + setup_wifi(); + + // Initialize webKernel + webKernel.begin(); + webKernel.setInitHandler(init_webkernel); // important! tells the webkernel how to initialize each connection + +} + +void loop() { + webKernel.handleClients(); +} + diff --git a/examples/filesystem-webapp/data/index.html b/examples/filesystem-webapp/data/index.html new file mode 100644 index 0000000..ed10f25 --- /dev/null +++ b/examples/filesystem-webapp/data/index.html @@ -0,0 +1,13 @@ + + + + Site Index + + + +

Site Index

+
loading
+ + + + diff --git a/examples/filesystem-webapp/data/led.txt b/examples/filesystem-webapp/data/led.txt new file mode 100644 index 0000000..b3d8640 --- /dev/null +++ b/examples/filesystem-webapp/data/led.txt @@ -0,0 +1 @@ +on diff --git a/examples/filesystem-webapp/data/scripts.js b/examples/filesystem-webapp/data/scripts.js new file mode 100644 index 0000000..6cc721e --- /dev/null +++ b/examples/filesystem-webapp/data/scripts.js @@ -0,0 +1,28 @@ +const stateUrl = '/led'; +const toggleUrl = '/led/toggle'; + +var stateElem = document.getElementById("led-state"); +var toggleBtn = document.getElementById("toggle-btn"); + +function loadState() { + var xhr = new XMLHttpRequest(); + xhr.open('GET', stateUrl, true); + xhr.onreadystatechange = stateLoaded; + xhr.send(); +} + +function toggle() { + var xhr = new XMLHttpRequest(); + xhr.open('GET', toggleUrl, true); + xhr.onreadystatechange = stateLoaded; + xhr.send(); +} + +function stateLoaded(e) { + var state = e.target.response; + stateElem.innerHTML = state; +} + +toggleBtn.addEventListener('click',toggle); + +loadState(); diff --git a/examples/filesystem-webapp/data/styles.css b/examples/filesystem-webapp/data/styles.css new file mode 100644 index 0000000..e69de29 diff --git a/examples/filesystem-webapp/filesystem-webapp.ino b/examples/filesystem-webapp/filesystem-webapp.ino new file mode 100644 index 0000000..1e97f43 --- /dev/null +++ b/examples/filesystem-webapp/filesystem-webapp.ino @@ -0,0 +1,138 @@ +/** + * WebKernel Application using the SPIFFS filesystem + */ +#include +#include +#include + +const char* network_ssid = "ESPThing"; // Name of the WiFi Access Point to create +const char* network_password = ""; // Password for WiFi (empty is no password) + +const uint8_t ledPin = LED_BUILTIN; // Arduino Pin controlling the LED + +uint8_t content_buffer[64] = {}; // global buffer to contain response data +Buffer content(content_buffer, // print response data to this variable + sizeof(content_buffer)/sizeof(content_buffer[0])); +File contentFile; // global file pointer to contain a response file + + +/** + * Handle requests for the site index (URL: /) + */ +void index(HttpRequest& request, HttpResponse& response) +{ + request.setUrl("/index.html"); + serve_static(request, response); +} + +/** + * Handle requests to show the LED status + * This function should print the status of the LED pin to content + */ +void show_led(HttpRequest& request, HttpResponse& response) +{ + response.headers.set("Content-Type","text/plain"); + if (digitalRead(ledPin)) + content.println("off"); + else content.println("on"); +} + +/** + * Handle requests to toggle the LED state + * Afterwards, send the user the LED's status + */ +void toggle_led(HttpRequest& request, HttpResponse& response) +{ + digitalWrite(ledPin, !(digitalRead(ledPin))); + show_led(request, response); +} + +void serve_static(HttpRequest& request, HttpResponse& response) +{ + Serial.println("Serving static content:"); + Serial.println(request.getUrl()); + // TODO: handle query string (after ?) + contentFile = SPIFFS.open(request.getUrl(),"r"); + if (!contentFile) { + handle_not_found(request, response); + return; + } + response.content = &contentFile; +} + +/** + * Before any request is handled, clear the content buffer from the last + * request and setup the buffer for the new request. + */ +void init_webkernel(HttpRequest& request, HttpResponse& response) +{ + if (contentFile) contentFile.close(); + content.clear(); // clear previously printed content + response.content = &content; // attach content buffer to the response +} + +void handle_not_found(HttpRequest& request, HttpResponse& response) +{ + content.println("File Not Found"); + response.code = 404; +} + +/** + * Setup a WiFi connection + * Called during setup routine + */ +void setup_wifi() +{ + // Setup WiFi - Use the ESP as a standalone access point + WiFi.mode(WIFI_AP); + WiFi.softAP(network_ssid, network_password); + Serial.println("WiFi SSID: "); + Serial.print(network_ssid); + Serial.print("WiFi password: "); + Serial.println(network_password); + Serial.print("AP IP Address: "); + Serial.println(WiFi.softAPIP()); +} + +/** + * Define an array of Routes for the WebKernel + * Each route contains: + * * one or more methods (ex: (GET | POST) handles both GET and POST requests), + * * a URL pattern (* matches anything until a slash '/', # matches until the end) + * * the name of a function accepting arguments: HttpRequest&, HttpResponse& + */ +Route routes[] = { + {GET, "/", index}, + {GET, "/led", show_led}, + {GET, "/led/toggle", toggle_led}, + {GET, "/#", serve_static} +}; + +WebKernel webKernel(80, routes, sizeof(routes)/sizeof(routes[0])); // Global WebKernel + +void setup() { + + // Initialize Pins + pinMode(ledPin, OUTPUT); + digitalWrite(ledPin, HIGH); + + // Start Serial + Serial.begin(115200); + delay(10); + + // Setup wifi + setup_wifi(); + + // Initialize webKernel + webKernel.begin(); + webKernel.setInitHandler(init_webkernel); // important! tells the webkernel how to initialize each connection + + // Initialize Filesystem + SPIFFS.begin(); + +} + +void loop() { + webKernel.handleClients(); +} +