diff --git a/works/Dennis de Bel & Anton Jehle/adaptor-lasercut-template-snap.svg b/works/Dennis de Bel & Anton Jehle/adaptor-lasercut-template-snap.svg
deleted file mode 100644
index 087733b..0000000
--- a/works/Dennis de Bel & Anton Jehle/adaptor-lasercut-template-snap.svg
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
diff --git a/works/Dennis de Bel & Anton Jehle/esp-files/DNSServer.cpp b/works/Dennis de Bel & Anton Jehle/esp-files/DNSServer.cpp
new file mode 100644
index 0000000..6552bec
--- /dev/null
+++ b/works/Dennis de Bel & Anton Jehle/esp-files/DNSServer.cpp
@@ -0,0 +1,166 @@
+#include "./DNSServer.h"
+#include
+#include
+
+#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();
+}
diff --git a/works/Dennis de Bel & Anton Jehle/esp-files/DNSServer.h b/works/Dennis de Bel & Anton Jehle/esp-files/DNSServer.h
new file mode 100755
index 0000000..2bade4b
--- /dev/null
+++ b/works/Dennis de Bel & Anton Jehle/esp-files/DNSServer.h
@@ -0,0 +1,72 @@
+#ifndef DNSServer_h
+#define DNSServer_h
+#include
+
+#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
+
diff --git a/works/Dennis de Bel & Anton Jehle/esp-files/data/.DS_Store b/works/Dennis de Bel & Anton Jehle/esp-files/data/.DS_Store
new file mode 100644
index 0000000..4df1045
Binary files /dev/null and b/works/Dennis de Bel & Anton Jehle/esp-files/data/.DS_Store differ
diff --git a/works/Dennis de Bel & Anton Jehle/index.html b/works/Dennis de Bel & Anton Jehle/esp-files/data/index.html
similarity index 100%
rename from works/Dennis de Bel & Anton Jehle/index.html
rename to works/Dennis de Bel & Anton Jehle/esp-files/data/index.html
diff --git a/works/Dennis de Bel & Anton Jehle/esp-files/data/paracity_gallery_v041.zip b/works/Dennis de Bel & Anton Jehle/esp-files/data/paracity_gallery_v041.zip
new file mode 100644
index 0000000..ecda115
Binary files /dev/null and b/works/Dennis de Bel & Anton Jehle/esp-files/data/paracity_gallery_v041.zip differ
diff --git a/works/Dennis de Bel & Anton Jehle/esp-files/esp-files.ino b/works/Dennis de Bel & Anton Jehle/esp-files/esp-files.ino
new file mode 100644
index 0000000..b25d857
--- /dev/null
+++ b/works/Dennis de Bel & Anton Jehle/esp-files/esp-files.ino
@@ -0,0 +1,98 @@
+ // Captive portal with (arduino) OTA + SPIFFS
+
+ #include
+ #include
+ #include
+ #include
+ #include // Over-the-Air updates
+ #include
+ #include "./DNSServer.h" // Dns server
+ #include // SPIFFS
+
+ DNSServer dnsServer;
+ const byte DNS_PORT = 53;
+
+ ESP8266WebServer server(80);
+
+ #ifndef STASSID
+ #define STASSID "Paracity Gallery v041"
+ //#define STASPSK "mypassword"
+ #endif
+
+ IPAddress apIP(192, 168, 4, 1);
+ const char* ssid = STASSID;
+ //const char* password = STAPSK;
+
+ 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("opencoil", WiFi.softAPIP());
+ Serial.println("Ready");
+ Serial.print("IP address: ");
+ Serial.println(WiFi.softAPIP());
+
+ //Over-the-Air updates
+ ArduinoOTA.setHostname("opencoil");
+ //ArduinoOTA.setPassword("change-me"); //disabled to allow data uploads
+ ArduinoOTA.begin();
+ SPIFFS.begin();
+
+ //redirect all traffic to index.html
+ server.onNotFound([]() {
+ if(!handleFileRead(server.uri())){
+ const char *metaRefreshStr = "redirecting...
";
+ server.send(200, "text/html", metaRefreshStr);
+ }
+ });
+
+ server.begin();
+
+ }
+
+ void loop() {
+ dnsServer.processNextRequest();
+ ArduinoOTA.handle();
+ 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 SPIFFS 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(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)){
+ if(SPIFFS.exists(pathWithGz))
+ path += ".gz";
+ File file = SPIFFS.open(path, "r");
+ size_t sent = server.streamFile(file, contentType);
+ file.close();
+ return true;
+ }
+ return false;
+}