forked from iffybooks/pocket-wifi-portal
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Still a WIP
- Loading branch information
Showing
8 changed files
with
1,371 additions
and
0 deletions.
There are no files selected for viewing
95 changes: 95 additions & 0 deletions
95
Example Projects/3-Pocket_Portal--Colouring_Page/3-Pocket_Portal--Colouring_Page.ino
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Captive portal with (arduino) OTA + LittleFS | ||
// Adapted from https://git.vvvvvvaria.org/then/ESP8266-captive-ota-spiffs | ||
|
||
#include <Arduino.h> | ||
#include <ESP8266WiFi.h> | ||
#include <ESP8266mDNS.h> | ||
#include <WiFiUdp.h> | ||
#include <ESP8266WebServer.h> | ||
#include "./DNSServer.h" // Dns server | ||
// #include <FS.h> // SPIFFS -> deprecated. All instances of SPIFFS have been replaced with LITTLEFS | ||
#include <LittleFS.h> // LittleFS | ||
|
||
DNSServer dnsServer; | ||
const byte DNS_PORT = 53; | ||
|
||
ESP8266WebServer server(80); | ||
|
||
#ifndef STASSID | ||
#define STASSID "A Digital Colouring Book" // ✨ set your network name here ✨ | ||
#endif | ||
|
||
IPAddress apIP(192, 168, 4, 1); | ||
const char* ssid = STASSID; | ||
|
||
void setup() { | ||
Serial.begin(115200); | ||
Serial.println("Booting"); | ||
|
||
WiFi.mode(WIFI_AP); | ||
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); | ||
WiFi.softAP(ssid); | ||
dnsServer.start(DNS_PORT, "*", apIP); // redirect dns request to AP ip | ||
|
||
MDNS.begin("esp8266", WiFi.softAPIP()); | ||
Serial.println("Ready"); | ||
Serial.print("IP address: "); | ||
Serial.println(WiFi.softAPIP()); | ||
|
||
//File system begin | ||
// SPIFFS.begin(); -> deprecated | ||
LittleFS.begin(); | ||
|
||
|
||
//redirect all traffic to index.html | ||
server.onNotFound([]() { | ||
if(!handleFileRead(server.uri())){ | ||
const char *metaRefreshStr = "<head><meta http-equiv=\"refresh\" content=\"0; url=http://192.168.4.1/index.html\" /></head><body><p>redirecting...</p></body>"; | ||
server.send(200, "text/html", metaRefreshStr); | ||
} | ||
}); | ||
|
||
server.begin(); | ||
|
||
} | ||
|
||
void loop() { | ||
dnsServer.processNextRequest(); | ||
server.handleClient(); | ||
delay(50); | ||
} | ||
|
||
|
||
String getContentType(String filename){ | ||
if(server.hasArg("download")) return "application/octet-stream"; | ||
else if(filename.endsWith(".htm")) return "text/html"; | ||
else if(filename.endsWith(".html")) return "text/html"; | ||
else if(filename.endsWith(".css")) return "text/css"; | ||
else if(filename.endsWith(".js")) return "application/javascript"; | ||
else if(filename.endsWith(".png")) return "image/png"; | ||
else if(filename.endsWith(".gif")) return "image/gif"; | ||
else if(filename.endsWith(".jpg")) return "image/jpeg"; | ||
else if(filename.endsWith(".ico")) return "image/x-icon"; | ||
else if(filename.endsWith(".xml")) return "text/xml"; | ||
else if(filename.endsWith(".mp4")) return "video/mp4"; | ||
else if(filename.endsWith(".pdf")) return "application/x-pdf"; | ||
else if(filename.endsWith(".zip")) return "application/x-zip"; | ||
else if(filename.endsWith(".gz")) return "application/x-gzip"; | ||
return "text/plain"; | ||
} | ||
|
||
//Given a file path, look for it in the LittleFS file storage. Returns true if found, returns false if not found. | ||
bool handleFileRead(String path){ | ||
if(path.endsWith("/")) path += "index.html"; | ||
String contentType = getContentType(path); | ||
String pathWithGz = path + ".gz"; | ||
if(LittleFS.exists(pathWithGz) || LittleFS.exists(path)){ | ||
if(LittleFS.exists(pathWithGz)) | ||
path += ".gz"; | ||
File file = LittleFS.open(path, "r"); | ||
size_t sent = server.streamFile(file, contentType); | ||
file.close(); | ||
return true; | ||
} | ||
return false; | ||
} |
166 changes: 166 additions & 0 deletions
166
Example Projects/3-Pocket_Portal--Colouring_Page/DNSServer.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
#include "./DNSServer.h" | ||
#include <lwip/def.h> | ||
#include <Arduino.h> | ||
|
||
#define DEBUG | ||
#define DEBUG_OUTPUT Serial | ||
|
||
DNSServer::DNSServer() | ||
{ | ||
_ttl = htonl(60); | ||
_errorReplyCode = DNSReplyCode::NonExistentDomain; | ||
} | ||
|
||
bool DNSServer::start(const uint16_t &port, const String &domainName, | ||
const IPAddress &resolvedIP) | ||
{ | ||
_port = port; | ||
_domainName = domainName; | ||
_resolvedIP[0] = resolvedIP[0]; | ||
_resolvedIP[1] = resolvedIP[1]; | ||
_resolvedIP[2] = resolvedIP[2]; | ||
_resolvedIP[3] = resolvedIP[3]; | ||
downcaseAndRemoveWwwPrefix(_domainName); | ||
return _udp.begin(_port) == 1; | ||
} | ||
|
||
void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode) | ||
{ | ||
_errorReplyCode = replyCode; | ||
} | ||
|
||
void DNSServer::setTTL(const uint32_t &ttl) | ||
{ | ||
_ttl = htonl(ttl); | ||
} | ||
|
||
void DNSServer::stop() | ||
{ | ||
_udp.stop(); | ||
} | ||
|
||
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName) | ||
{ | ||
domainName.toLowerCase(); | ||
domainName.replace("www.", ""); | ||
domainName.replace("https://", ""); | ||
} | ||
|
||
void DNSServer::processNextRequest() | ||
{ | ||
_currentPacketSize = _udp.parsePacket(); | ||
if (_currentPacketSize) | ||
{ | ||
_buffer = (unsigned char*)malloc(_currentPacketSize * sizeof(char)); | ||
_udp.read(_buffer, _currentPacketSize); | ||
_dnsHeader = (DNSHeader*) _buffer; | ||
|
||
if (_dnsHeader->QR == DNS_QR_QUERY && | ||
_dnsHeader->OPCode == DNS_OPCODE_QUERY && | ||
requestIncludesOnlyOneQuestion() && | ||
(_domainName == "*" || getDomainNameWithoutWwwPrefix() == _domainName) | ||
) | ||
|
||
{ | ||
replyWithIP(); | ||
} | ||
else if (_dnsHeader->QR == DNS_QR_QUERY) | ||
{ | ||
replyWithCustomCode(); | ||
} | ||
|
||
free(_buffer); | ||
} | ||
} | ||
|
||
bool DNSServer::requestIncludesOnlyOneQuestion() | ||
{ | ||
return ntohs(_dnsHeader->QDCount) == 1 && | ||
_dnsHeader->ANCount == 0 && | ||
_dnsHeader->NSCount == 0 && | ||
_dnsHeader->ARCount == 0; | ||
} | ||
|
||
String DNSServer::getDomainNameWithoutWwwPrefix() | ||
{ | ||
String parsedDomainName = ""; | ||
unsigned char *start = _buffer + 12; | ||
if (*start == 0) | ||
{ | ||
return parsedDomainName; | ||
} | ||
int pos = 0; | ||
while(true) | ||
{ | ||
unsigned char labelLength = *(start + pos); | ||
for(int i = 0; i < labelLength; i++) | ||
{ | ||
pos++; | ||
parsedDomainName += (char)*(start + pos); | ||
} | ||
pos++; | ||
if (*(start + pos) == 0) | ||
{ | ||
downcaseAndRemoveWwwPrefix(parsedDomainName); | ||
return parsedDomainName; | ||
} | ||
else | ||
{ | ||
parsedDomainName += "."; | ||
} | ||
} | ||
} | ||
|
||
void DNSServer::replyWithIP() | ||
{ | ||
_dnsHeader->QR = DNS_QR_RESPONSE; | ||
_dnsHeader->ANCount = _dnsHeader->QDCount; | ||
_dnsHeader->QDCount = _dnsHeader->QDCount; | ||
//_dnsHeader->RA = 1; | ||
|
||
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); | ||
_udp.write(_buffer, _currentPacketSize); | ||
|
||
_udp.write((uint8_t)192); // answer name is a pointer | ||
_udp.write((uint8_t)12); // pointer to offset at 0x00c | ||
|
||
_udp.write((uint8_t)0); // 0x0001 answer is type A query (host address) | ||
_udp.write((uint8_t)1); | ||
|
||
_udp.write((uint8_t)0); //0x0001 answer is class IN (internet address) | ||
_udp.write((uint8_t)1); | ||
|
||
_udp.write((unsigned char*)&_ttl, 4); | ||
|
||
// Length of RData is 4 bytes (because, in this case, RData is IPv4) | ||
_udp.write((uint8_t)0); | ||
_udp.write((uint8_t)4); | ||
_udp.write(_resolvedIP, sizeof(_resolvedIP)); | ||
_udp.endPacket(); | ||
|
||
|
||
|
||
#ifdef DEBUG | ||
DEBUG_OUTPUT.print("DNS responds: "); | ||
DEBUG_OUTPUT.print(_resolvedIP[0]); | ||
DEBUG_OUTPUT.print("."); | ||
DEBUG_OUTPUT.print(_resolvedIP[1]); | ||
DEBUG_OUTPUT.print("."); | ||
DEBUG_OUTPUT.print(_resolvedIP[2]); | ||
DEBUG_OUTPUT.print("."); | ||
DEBUG_OUTPUT.print(_resolvedIP[3]); | ||
DEBUG_OUTPUT.print(" for "); | ||
DEBUG_OUTPUT.println(getDomainNameWithoutWwwPrefix()); | ||
#endif | ||
} | ||
|
||
void DNSServer::replyWithCustomCode() | ||
{ | ||
_dnsHeader->QR = DNS_QR_RESPONSE; | ||
_dnsHeader->RCode = (unsigned char)_errorReplyCode; | ||
_dnsHeader->QDCount = 0; | ||
|
||
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); | ||
_udp.write(_buffer, sizeof(DNSHeader)); | ||
_udp.endPacket(); | ||
} |
72 changes: 72 additions & 0 deletions
72
Example Projects/3-Pocket_Portal--Colouring_Page/DNSServer.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
#ifndef DNSServer_h | ||
#define DNSServer_h | ||
#include <WiFiUdp.h> | ||
|
||
#define DNS_QR_QUERY 0 | ||
#define DNS_QR_RESPONSE 1 | ||
#define DNS_OPCODE_QUERY 0 | ||
|
||
enum class DNSReplyCode | ||
{ | ||
NoError = 0, | ||
FormError = 1, | ||
ServerFailure = 2, | ||
NonExistentDomain = 3, | ||
NotImplemented = 4, | ||
Refused = 5, | ||
YXDomain = 6, | ||
YXRRSet = 7, | ||
NXRRSet = 8 | ||
}; | ||
|
||
struct DNSHeader | ||
{ | ||
uint16_t ID; // identification number | ||
unsigned char RD : 1; // recursion desired | ||
unsigned char TC : 1; // truncated message | ||
unsigned char AA : 1; // authoritive answer | ||
unsigned char OPCode : 4; // message_type | ||
unsigned char QR : 1; // query/response flag | ||
unsigned char RCode : 4; // response code | ||
unsigned char Z : 3; // its z! reserved | ||
unsigned char RA : 1; // recursion available | ||
uint16_t QDCount; // number of question entries | ||
uint16_t ANCount; // number of answer entries | ||
uint16_t NSCount; // number of authority entries | ||
uint16_t ARCount; // number of resource entries | ||
}; | ||
|
||
class DNSServer | ||
{ | ||
public: | ||
DNSServer(); | ||
void processNextRequest(); | ||
void setErrorReplyCode(const DNSReplyCode &replyCode); | ||
void setTTL(const uint32_t &ttl); | ||
|
||
// Returns true if successful, false if there are no sockets available | ||
bool start(const uint16_t &port, | ||
const String &domainName, | ||
const IPAddress &resolvedIP); | ||
// stops the DNS server | ||
void stop(); | ||
|
||
private: | ||
WiFiUDP _udp; | ||
uint16_t _port; | ||
String _domainName; | ||
unsigned char _resolvedIP[4]; | ||
int _currentPacketSize; | ||
unsigned char* _buffer; | ||
DNSHeader* _dnsHeader; | ||
uint32_t _ttl; | ||
DNSReplyCode _errorReplyCode; | ||
|
||
void downcaseAndRemoveWwwPrefix(String &domainName); | ||
String getDomainNameWithoutWwwPrefix(); | ||
bool requestIncludesOnlyOneQuestion(); | ||
void replyWithIP(); | ||
void replyWithCustomCode(); | ||
}; | ||
#endif | ||
|
53 changes: 53 additions & 0 deletions
53
Example Projects/3-Pocket_Portal--Colouring_Page/data/colouringbook.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
var svgID = "colour"; // 🔄 Replace 'colour' with the HTML ID of the illustration's SVG (if you want everything to be colour-able), or the ID of the layer or group to colour in | ||
|
||
//// ✨ COLOUR PICKER CUSTOM COLOR ✨ //// | ||
|
||
// Function to handle color picker selection | ||
function handleColorPickerSelection(event) { | ||
// Check if a color picker input element already exists | ||
if (!document.getElementById("colorPickerInput")) { | ||
// Create a color picker input element | ||
var input = document.createElement("input"); | ||
input.type = "color"; | ||
input.id = "colorPickerInput"; // Assign an id to the input element | ||
|
||
// Add event listener for color change | ||
input.addEventListener("change", function () { | ||
// Set the current fill to the selected color | ||
var selectedColor = input.value; | ||
_currentFill = "fill:" + selectedColor; | ||
|
||
// Remove the input element from the body | ||
document.body.removeChild(input); | ||
}); | ||
|
||
// Append the input element to the body | ||
document.body.appendChild(input); | ||
|
||
// Trigger click event on color picker input | ||
input.click(); | ||
} | ||
} | ||
|
||
// Add event listener to the custom swatch for color picker | ||
document | ||
.getElementById("colorPickerSwatch") | ||
.addEventListener("click", handleColorPickerSelection); | ||
|
||
//// ✨ CHANGING THE FILL COLOUR DYNAMICALLY ✨ //// | ||
|
||
// default selected colour when page is loaded | ||
var _currentFill = "fill:#fff"; | ||
|
||
// change the fill colour when clicking inside of your ilustration | ||
$("#" + svgID).click(function (event) { | ||
$(event.target).attr("style", _currentFill); | ||
}); | ||
var $swatches = $("#swatches"); | ||
$swatches.click(function (event) { | ||
$swatch = $(event.target); | ||
loc = [parseInt($swatch.attr("x"), 10), parseInt($swatch.attr("y"), 10)]; | ||
$("#selection", $swatches).attr("x", loc[0]); | ||
$("#selection", $swatches).attr("y", loc[1]); | ||
_currentFill = $swatch.attr("style"); | ||
}); |
Oops, something went wrong.