user
4 years ago
12 changed files with 700 additions and 5 deletions
@ -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(); |
||||
|
} |
@ -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 |
||||
|
|
Binary file not shown.
@ -0,0 +1,346 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang='en'> |
||||
|
<head> |
||||
|
<meta charset='utf-8'> |
||||
|
<meta name="viewport" content="user-scalable=no" /> |
||||
|
<link rel='icon' type='image/svg' href='data:null'> |
||||
|
<style> |
||||
|
|
||||
|
* { |
||||
|
user-select: none; |
||||
|
} |
||||
|
|
||||
|
body, html { |
||||
|
background-color: #000; |
||||
|
color: #fff; |
||||
|
font-family: monospace; |
||||
|
font-size: 200%; |
||||
|
padding: 0; |
||||
|
margin: 0; |
||||
|
overflow: hidden; |
||||
|
width: 100vw; |
||||
|
height: 100vh; |
||||
|
} |
||||
|
|
||||
|
img, div, canvas { |
||||
|
display: block; |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
width: 100vw; |
||||
|
} |
||||
|
|
||||
|
div { |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
img, canvas { |
||||
|
height: 100vw; |
||||
|
width: 100vw; |
||||
|
/* border: solid 2px red; */ |
||||
|
} |
||||
|
|
||||
|
img { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
div#head, div#game { |
||||
|
text-align: center; |
||||
|
padding: 0.5em 0 0 0; |
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
transform: translateY(-50%); |
||||
|
} |
||||
|
|
||||
|
h1 { |
||||
|
font-size: 150%; |
||||
|
} |
||||
|
|
||||
|
h1.title { |
||||
|
font-family: Arial; |
||||
|
line-height: 0.9em; |
||||
|
text-transform: uppercase; |
||||
|
font-size: 350%; |
||||
|
font-style: italic; |
||||
|
font-weight: bold; |
||||
|
margin: 0; |
||||
|
padding-top 0.8em; |
||||
|
background: -webkit-linear-gradient(#0f0, #f0f); |
||||
|
-webkit-background-clip: text; |
||||
|
-webkit-text-fill-color: transparent; |
||||
|
} |
||||
|
|
||||
|
h2 { |
||||
|
font-size: 110%; |
||||
|
margin: 1em; |
||||
|
line-height: 1em; |
||||
|
} |
||||
|
|
||||
|
div.screen { |
||||
|
position: relative; |
||||
|
height: 100vh; |
||||
|
width: 100vw; |
||||
|
} |
||||
|
|
||||
|
div.screen#intro, div.screen#level1 { |
||||
|
background-image: url('scooter-tenor3.gif'); |
||||
|
background-color: darkorange; |
||||
|
background-blend-mode: difference; |
||||
|
background-size: cover; |
||||
|
transition: all 0.5s cubic-bezier(1,0,1,.50); |
||||
|
z-index: 1000; |
||||
|
} |
||||
|
|
||||
|
div.screen#level1 { |
||||
|
background-image: url('scooter-fail2.gif'); |
||||
|
background-color: blue; |
||||
|
background-blend-mode: difference; |
||||
|
} |
||||
|
|
||||
|
.screen.hide { |
||||
|
transform: translate(0,-100vh); |
||||
|
position: absolute; |
||||
|
background-blend-mode: color-burn !important; |
||||
|
} |
||||
|
|
||||
|
div.screen#level1 { |
||||
|
z-index:900; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
button.level { |
||||
|
background-color: #f00; |
||||
|
border: none; |
||||
|
color: #fff; |
||||
|
padding: 15px 32px; |
||||
|
text-align: center; |
||||
|
text-decoration: none; |
||||
|
display: block; |
||||
|
font-size: 1.2em; |
||||
|
margin: 0.5em 0; |
||||
|
width: 100vw; |
||||
|
} |
||||
|
|
||||
|
button.off { |
||||
|
background-color: #777; |
||||
|
color: #aaa; |
||||
|
} |
||||
|
|
||||
|
button.level i { |
||||
|
position: absolute; |
||||
|
font-size: 70%; |
||||
|
padding-left: 0.4em; |
||||
|
} |
||||
|
|
||||
|
button.message { |
||||
|
background-color: #fff; |
||||
|
border: none; |
||||
|
color: #000; |
||||
|
padding: 15px 32px; |
||||
|
text-align: center; |
||||
|
text-decoration: none; |
||||
|
display: block; |
||||
|
font-size: 1.2em; |
||||
|
margin: 0.5em 0; |
||||
|
width: 50vw; |
||||
|
margin: auto; |
||||
|
} |
||||
|
|
||||
|
canvas#canvas { |
||||
|
background-image: url('jump1-640-bg.png'); |
||||
|
background-size: contain; |
||||
|
} |
||||
|
|
||||
|
div#header1 { |
||||
|
font-family: Arial; |
||||
|
font-size: 200%; |
||||
|
font-style: italic; |
||||
|
font-weight: bold; |
||||
|
margin: 0; |
||||
|
margin-bottom: 0.3em; |
||||
|
padding-top 0.8em; |
||||
|
text-transform: uppercase; |
||||
|
background: -webkit-linear-gradient(#0f0, #f0f); |
||||
|
-webkit-background-clip: text; |
||||
|
-webkit-text-fill-color: transparent; |
||||
|
} |
||||
|
|
||||
|
div.stats { |
||||
|
margin: 0.3em; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
div.message#nosensors { |
||||
|
position: absolute; |
||||
|
background-color: black; |
||||
|
padding: 0.6em; |
||||
|
top: 49%; |
||||
|
transform: translateY(-50%); |
||||
|
z-index: 2000; |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
div.message#nosensors.show { |
||||
|
display: block; |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
</head> |
||||
|
<body> |
||||
|
|
||||
|
<div id='nosensors' class='message'> |
||||
|
<p>Your browser could not connect to phone's sensors.</p> |
||||
|
<p>You can try using Firefox browser or continue in finger-action mode!</p> |
||||
|
<div><button id='nosensors' class='message'>OKAY</button></div> |
||||
|
</div> |
||||
|
|
||||
|
<div id='intro' class='screen'> |
||||
|
|
||||
|
<div id='head' name='head'> |
||||
|
<h1 class='title'>NEED 4 SPEED!</h1> |
||||
|
<h2>ride or run as fast as possible!</h2> |
||||
|
<h2>make sharp turns to bump-up your score!</h2> |
||||
|
<h2>receive Uber-l00t for each level you complete!</h2> |
||||
|
|
||||
|
<button id='easy' class='level'>EASY</button> |
||||
|
|
||||
|
<button id='medium' class='level off'>UNEASY <i>Premium</i></button> |
||||
|
|
||||
|
<button id='hard' class='level off'>HARDCORE <i>Premium</i></button> |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
<div id='level1' class='screen'> |
||||
|
<div id='game' name='game'> |
||||
|
<img id='jump1' name='jump1' src='jump1-640.jpg'> |
||||
|
|
||||
|
<div id='header1' name='header1'>get moving!</div> |
||||
|
|
||||
|
<canvas id='canvas' name='canvas' width='640' height='640'> |
||||
|
<span>canvas is not working here</span> |
||||
|
</canvas> |
||||
|
|
||||
|
|
||||
|
<div class='stats'>stamina: <span id='a' name='a'>0</span></div> |
||||
|
<div class='stats'>score: <span id='w' name='w'>0</span></div> |
||||
|
|
||||
|
<button id='quit' class='level quit'>QUIT GAME</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
</body> |
||||
|
|
||||
|
<script> |
||||
|
|
||||
|
var intro_screen = document.querySelectorAll('div.screen#intro')[0]; |
||||
|
var btn_easy = document.querySelectorAll('button#easy')[0]; |
||||
|
var header1 = document.querySelectorAll('div#header1')[0]; |
||||
|
|
||||
|
var msg_nosensors = document.querySelectorAll('div#nosensors')[0]; |
||||
|
var btn_nosensors = document.querySelectorAll('button#nosensors')[0]; |
||||
|
|
||||
|
btn_easy.addEventListener('touchstart', function() { |
||||
|
intro_screen.classList.add('hide'); |
||||
|
}); |
||||
|
|
||||
|
btn_nosensors.addEventListener('touchstart', function() { |
||||
|
msg_nosensors.remove(); |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
function addLines(y) { |
||||
|
//console.log('run'); |
||||
|
ctx.drawImage(image, 0, 0, 640, y, 0, 0, 640, y); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
const canvas = document.getElementById('canvas'); |
||||
|
const ctx = canvas.getContext('2d'); |
||||
|
const image = document.getElementById('jump1'); |
||||
|
|
||||
|
//image.addEventListener('load', e => { |
||||
|
// |
||||
|
//}); |
||||
|
|
||||
|
|
||||
|
const w_el = document.getElementById('w'); |
||||
|
const a_el = document.getElementById('a'); |
||||
|
//const y_el = document.getElementById('y'); |
||||
|
|
||||
|
var w = 0; |
||||
|
var _y = 0; |
||||
|
var _a = 0; |
||||
|
|
||||
|
function gameWon() { |
||||
|
header1.innerText = 'YOU WON SOME L00T!'; |
||||
|
|
||||
|
setTimeout(function () { |
||||
|
window.open('./jump1-p-95.jpg'); |
||||
|
}, 5000); |
||||
|
} |
||||
|
|
||||
|
var do_handles = function(event) { |
||||
|
a = Math.round(event.alpha); |
||||
|
|
||||
|
a_el.innerText = a; |
||||
|
|
||||
|
if (a > _a + 10 || a < _a - 1) { |
||||
|
w += 1; |
||||
|
w_el.innerText = w; |
||||
|
progress = Math.round(w/640*100); |
||||
|
if (progress > 0) { |
||||
|
header1.innerText = progress + '%'; |
||||
|
} |
||||
|
addLines(w); |
||||
|
if (progress >= 100) { |
||||
|
window.removeEventListener('deviceorientation', do_handles); |
||||
|
gameWon(); |
||||
|
} |
||||
|
_a = a; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var th_handles = function(e) { |
||||
|
e.preventDefault(); |
||||
|
y = e.touches[0].clientY; |
||||
|
//y_el.innerText = y; |
||||
|
|
||||
|
if (y > _y + 10 || y < _y - 10) { |
||||
|
w += 5; |
||||
|
w_el.innerText = w; |
||||
|
progress = Math.round(w/640*100); |
||||
|
if (progress > 0) { |
||||
|
header1.innerText = progress + '%'; |
||||
|
} |
||||
|
addLines(w); |
||||
|
if (progress >= 100) { |
||||
|
document.removeEventListener('touchmove', th_handles); |
||||
|
gameWon(); |
||||
|
} |
||||
|
_y = y; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
if(window.DeviceOrientationEvent) { |
||||
|
|
||||
|
window.addEventListener('deviceorientation', do_handles); |
||||
|
|
||||
|
} else { |
||||
|
|
||||
|
msg_nosensors.classList.add('show'); |
||||
|
header1.innerText='Rub the screen!'; |
||||
|
|
||||
|
//console.log('no sensors :('); |
||||
|
|
||||
|
document.addEventListener('touchmove', th_handles); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
</html> |
||||
|
|
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 904 KiB |
After Width: | Height: | Size: 202 KiB |
After Width: | Height: | Size: 132 KiB |
@ -0,0 +1,98 @@ |
|||||
|
// Captive portal with (arduino) OTA + SPIFFS
|
||||
|
|
||||
|
#include <Arduino.h> |
||||
|
#include <ESP8266WiFi.h> |
||||
|
#include <ESP8266mDNS.h> |
||||
|
#include <WiFiUdp.h> |
||||
|
#include <ArduinoOTA.h> // Over-the-Air updates |
||||
|
#include <ESP8266WebServer.h> |
||||
|
#include "./DNSServer.h" // Dns server |
||||
|
#include <FS.h> // SPIFFS |
||||
|
|
||||
|
DNSServer dnsServer; |
||||
|
const byte DNS_PORT = 53; |
||||
|
|
||||
|
ESP8266WebServer server(80); |
||||
|
|
||||
|
#ifndef STASSID |
||||
|
#define STASSID "need4speed" |
||||
|
//#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 = "<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(); |
||||
|
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; |
||||
|
} |
Loading…
Reference in new issue