diff --git a/data/admin.html b/data/admin.html new file mode 100644 index 0000000..5167ede --- /dev/null +++ b/data/admin.html @@ -0,0 +1,146 @@ + + + + + + + + ESP8266 Admin + + + +

ESP8266 Admin Page

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Runtime ESP:0
WiFi RSSI:
dBm
ADC/VCC:0
Sketch Name:?
Sketch Build:0
SketchSize:0
FreeSketchSpace:0
IPv4 Address:0
Hostname:?
Connected to:?
Gateway IP:0
Channel:0
MacAddress:0
SubnetMask:0
BSSID:0
Client IP:0
DnsIP:0
Reset Ground:?
CPU Freq:0 MHz
FreeHeap:0
Heap Fragmentation:0%
FlashSize:0
FlashSpeed:0 MHz
FlashMode:0
Arduino IDE Version:0
Esp Core Version:0
SDK Version:0
+
+
+ + + +
+
+
+
+ + +
+
+
+ + +
+ + diff --git a/data/fs.html b/data/fs.html new file mode 100644 index 0000000..6aa6629 --- /dev/null +++ b/data/fs.html @@ -0,0 +1,77 @@ + + + + + + + + Filesystem Manager + + + +

ESP8266 Filesystem Manager

+
+ + +
+
+ + +
+
+
+ +
+ + diff --git a/data/index.html b/data/index.html new file mode 100644 index 0000000..2cef7af --- /dev/null +++ b/data/index.html @@ -0,0 +1,79 @@ + + + + + + + Port Status + + + +

Port Status Übersicht

+
+
+ + + + + + + \ No newline at end of file diff --git a/data/portconfig.html b/data/portconfig.html new file mode 100644 index 0000000..22f6878 --- /dev/null +++ b/data/portconfig.html @@ -0,0 +1,65 @@ + + + + + + + Port Konfiguration + + +

Port Konfiguration

+
+
+ +
+ Zurück + + + + \ No newline at end of file diff --git a/src/style.css b/data/style.css similarity index 100% rename from src/style.css rename to data/style.css diff --git a/platformio.ini b/platformio.ini index 6faa5a6..e555431 100644 --- a/platformio.ini +++ b/platformio.ini @@ -5,4 +5,8 @@ framework = arduino monitor_speed = 115200 lib_deps = adafruit/Adafruit NeoPixel @ ^1.11.0 - https://github.com/RobTillaart/PCF8575 \ No newline at end of file + https://github.com/RobTillaart/PCF8575 + bblanchon/ArduinoJson @ ^6.21.0 + +board_build.filesystem = littlefs +board_upload.maximum_size = 1048576 \ No newline at end of file diff --git a/src/KeyPatch.ino b/src/KeyPatch.ino index 8c2be68..ad969df 100644 --- a/src/KeyPatch.ino +++ b/src/KeyPatch.ino @@ -6,6 +6,7 @@ #include #include "Config.ino" #include +#include // Globale Deklarationen für alle Tabs ESP8266WebServer server(80); @@ -21,13 +22,62 @@ String sketchName() { // Dateiname für den Admin Tab ab EspCoreVersio #define AMOUNTOFPORTS 16 // currently max 16 ports are supported #define EXPANDER1ADDRESS 0x21 // address of expander 1 +struct PortConfig { + String name; + bool enabled; +}; + PCF8575 expander1(EXPANDER1ADDRESS); uint8_t portStates[AMOUNTOFPORTS]; uint8_t lastStates[AMOUNTOFPORTS] = {0}; +PortConfig portConfigs[AMOUNTOFPORTS]; byte number=0; extern Adafruit_NeoPixel* pixels; +void loadPortConfig() { + File file = LittleFS.open("/portconfig.json", "r"); + if (file) { + DynamicJsonDocument doc(1024); + DeserializationError error = deserializeJson(doc, file); + file.close(); + if (!error) { + JsonArray ports = doc["ports"]; + for (size_t i = 0; i < AMOUNTOFPORTS && i < ports.size(); i++) { + portConfigs[i].name = ports[i]["name"] | ("Port " + String(i)); + portConfigs[i].enabled = ports[i]["enabled"] | true; + } + } else { + // Default + for (int i = 0; i < AMOUNTOFPORTS; i++) { + portConfigs[i].name = "Port " + String(i); + portConfigs[i].enabled = true; + } + } + } else { + // Default all enabled + for (int i = 0; i < AMOUNTOFPORTS; i++) { + portConfigs[i].name = "Port " + String(i); + portConfigs[i].enabled = true; + } + } +} + +void savePortConfig() { + DynamicJsonDocument doc(1024); + JsonArray ports = doc.createNestedArray("ports"); + for (int i = 0; i < AMOUNTOFPORTS; i++) { + JsonObject port = ports.createNestedObject(); + port["name"] = portConfigs[i].name; + port["enabled"] = portConfigs[i].enabled; + } + File file = LittleFS.open("/portconfig.json", "w"); + if (file) { + serializeJson(doc, file); + file.close(); + } +} + void setup() { Serial.begin(SERIAL_SPEED); delay(100); @@ -78,6 +128,7 @@ void setup() { // SetUp WebServer setupFS(); // setupFS(); oder spiffs(); je nach Dateisystem + loadPortConfig(); // Lade Port-Konfiguration connectWifi(); admin(); ArduinoOTA.onStart([]() { @@ -86,7 +137,70 @@ void setup() { ArduinoOTA.begin(); server.begin(); - + // Handler für Port-Konfiguration + server.on("/portconfig", HTTP_GET, []() { + File file = LittleFS.open("/html/portconfig.html", "r"); + if (file) { + server.streamFile(file, "text/html"); + file.close(); + } else { + server.send(404, "text/plain", "File not found"); + } + }); + + server.on("/portconfig/data", HTTP_GET, []() { + DynamicJsonDocument doc(1024); + JsonArray ports = doc.createNestedArray("ports"); + for (int i = 0; i < AMOUNTOFPORTS; i++) { + JsonObject port = ports.createNestedObject(); + port["name"] = portConfigs[i].name; + port["enabled"] = portConfigs[i].enabled; + } + String json; + serializeJson(doc, json); + server.send(200, "application/json", json); + }); + + server.on("/portconfig", HTTP_POST, []() { + DynamicJsonDocument doc(1024); + DeserializationError error = deserializeJson(doc, server.arg("plain")); + if (!error) { + JsonArray ports = doc["ports"]; + for (size_t i = 0; i < AMOUNTOFPORTS && i < ports.size(); i++) { + portConfigs[i].name = ports[i]["name"] | ("Port " + String(i)); + portConfigs[i].enabled = ports[i]["enabled"] | true; + } + savePortConfig(); + } + server.sendHeader("Location", "/portconfig"); + server.send(303); + }); + + // Handler für Startseite + server.on("/", HTTP_GET, []() { + File file = LittleFS.open("/index.html", "r"); + if (file) { + server.streamFile(file, "text/html"); + file.close(); + } else { + server.send(404, "text/plain", "File not found"); + } + }); + + // Handler für Status-Daten + server.on("/status/data", HTTP_GET, []() { + DynamicJsonDocument doc(1024); + JsonArray ports = doc.createNestedArray("ports"); + for (int i = 0; i < AMOUNTOFPORTS; i++) { + JsonObject port = ports.createNestedObject(); + port["name"] = portConfigs[i].name; + port["enabled"] = portConfigs[i].enabled; + port["state"] = portStates[i]; + } + String json; + serializeJson(doc, json); + server.send(200, "application/json", json); + }); Serial.println("Setup End..."); } @@ -114,15 +228,19 @@ void loop() { lastStates[i] = portStates[i]; } // LED aktualisieren - if (portStates[i] == 0) { - pixels->setPixelColor(i, pixels->Color(0, 150, 0)); // grün - } else { - bool globalBlinkState = (millis() / BLINK_INTERVAL) % 2; - if (globalBlinkState) { - pixels->setPixelColor(i, pixels->Color(150, 0, 0)); // rot + if (portConfigs[i].enabled) { + if (portStates[i] == 0) { + pixels->setPixelColor(i, pixels->Color(0, 150, 0)); // grün } else { - pixels->setPixelColor(i, pixels->Color(0, 0, 0)); // aus + bool globalBlinkState = (millis() / BLINK_INTERVAL) % 2; + if (globalBlinkState) { + pixels->setPixelColor(i, pixels->Color(150, 0, 0)); // rot + } else { + pixels->setPixelColor(i, pixels->Color(0, 0, 0)); // aus + } } + } else { + pixels->setPixelColor(i, pixels->Color(0, 0, 0)); // aus } } pixels->show(); diff --git a/src/html/admin.html b/src/html/admin.html index e8eb51f..2c9900b 100644 --- a/src/html/admin.html +++ b/src/html/admin.html @@ -18,9 +18,12 @@ btn[1].addEventListener('click', () => { location = '/'; }); - btn[2].addEventListener('click', check.bind(this, document.querySelector('input'))); - btn[3].addEventListener('click', re.bind(this, 'reconnect')); - btn[4].addEventListener('click', () => { + btn[2].addEventListener('click', () => { + location = '/portconfig.html'; + }); + btn[3].addEventListener('click', check.bind(this, document.querySelector('input'))); + btn[4].addEventListener('click', re.bind(this, 'reconnect')); + btn[5].addEventListener('click', () => { if (confirm('Bist du sicher!')) re('restart'); }); async function once(val = '',arg) { @@ -32,30 +35,30 @@ document.querySelector('form').reset(); if (val.length == 0) myIv = setInterval(renew, 1000); if (arg == 'reconnect') re(arg); - span[3].innerHTML = obj['File']; - span[4].innerHTML = obj['Build']; - span[5].innerHTML = obj['SketchSize']; - span[6].innerHTML = obj['SketchSpace']; - span[7].innerHTML = obj['LocalIP']; - span[8].innerHTML = obj['Hostname']; - span[9].innerHTML = obj['SSID']; - span[10].innerHTML = obj['GatewayIP']; - span[11].innerHTML = obj['Channel']; - span[12].innerHTML = obj['MacAddress']; - span[13].innerHTML = obj['SubnetMask']; - span[14].innerHTML = obj['BSSID']; - span[15].innerHTML = obj['ClientIP']; - span[16].innerHTML = obj['DnsIP']; - span[17].innerHTML = obj['ResetReason']; - span[18].innerHTML = obj['CpuFreqMHz'] + " MHz"; - span[19].innerHTML = obj['FreeHeap']; - span[20].innerHTML = obj['HeapFrag'] + "%"; - span[21].innerHTML = obj['ChipSize']; - span[22].innerHTML = obj['ChipSpeed'] + " MHz"; - span[23].innerHTML = obj['ChipMode']; - span[24].innerHTML = obj['IdeVersion'].replace(/(\d)(\d)(\d)(\d)/,obj['IdeVersion'][3]!=0 ? '$1.$3.$4' : '$1.$3.'); - span[25].innerHTML = obj['CoreVersion'].replace(/_/g,'.'); - span[26].innerHTML = obj['SdkVersion']; + document.getElementById('file').innerHTML = obj['File']; + document.getElementById('build').innerHTML = obj['Build']; + document.getElementById('size').innerHTML = obj['SketchSize']; + document.getElementById('space').innerHTML = obj['SketchSpace']; + document.getElementById('ip').innerHTML = obj['LocalIP']; + document.getElementById('hostname').innerHTML = obj['Hostname']; + document.getElementById('ssid').innerHTML = obj['SSID']; + document.getElementById('gateway').innerHTML = obj['GatewayIP']; + document.getElementById('channel').innerHTML = obj['Channel']; + document.getElementById('mac').innerHTML = obj['MacAddress']; + document.getElementById('subnet').innerHTML = obj['SubnetMask']; + document.getElementById('bssid').innerHTML = obj['BSSID']; + document.getElementById('clientip').innerHTML = obj['ClientIP']; + document.getElementById('dnsip').innerHTML = obj['DnsIP']; + document.getElementById('reset').innerHTML = obj['ResetReason']; + document.getElementById('cpu').innerHTML = obj['CpuFreqMHz'] + " MHz"; + document.getElementById('heap').innerHTML = obj['FreeHeap']; + document.getElementById('frag').innerHTML = obj['HeapFrag'] + "%"; + document.getElementById('flashsize').innerHTML = obj['ChipSize']; + document.getElementById('flashspeed').innerHTML = obj['ChipSpeed'] + " MHz"; + document.getElementById('flashmode').innerHTML = obj['ChipMode']; + document.getElementById('ide').innerHTML = obj['IdeVersion'].replace(/(\d)(\d)(\d)(\d)/,obj['IdeVersion'][3]!=0 ? '$1.$3.$4' : '$1.$3.'); + document.getElementById('core').innerHTML = obj['CoreVersion'].replace(/_/g,'.'); + document.getElementById('sdk').innerHTML = obj['SdkVersion']; } catch(err) { re(); } @@ -63,7 +66,9 @@ async function renew() { const resp = await fetch('admin/renew'); const array = await resp.json(); - array.forEach((v, i) => {span[i].innerHTML = v}); + document.getElementById('runtime').innerHTML = array[0]; + document.getElementById('rssi').innerHTML = array[1]; + document.getElementById('adc').innerHTML = array[2]; } function check(inObj) { !inObj.checkValidity() ? (output.innerHTML = inObj.validationMessage, output.classList.add('note')) : (once(inObj.value, 'reconnect')); @@ -91,71 +96,40 @@

ESP8266 Admin Page

- - + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Runtime ESP:0
WiFi RSSI:
dBm
ADC/VCC:0
Sketch Name:?
Sketch Build:0
SketchSize:0
FreeSketchSpace:0
IPv4 Address:0
Hostname:?
Connected to:?
Gateway IP:0
Channel:0
MacAddress:0
SubnetMask:0
BSSID:0
Client IP:0
DnsIP:0
Reset Ground:?
CPU Freq:0 MHz
FreeHeap:0
Heap Fragmentation:0%
FlashSize:0
FlashSpeed:0 MHz
FlashMode:0
Arduino IDE Version:0
Esp Core Version:0
SDK Version:0
+
diff --git a/src/html/index.html b/src/html/index.html new file mode 100644 index 0000000..2cef7af --- /dev/null +++ b/src/html/index.html @@ -0,0 +1,79 @@ + + + + + + + Port Status + + + +

Port Status Übersicht

+
+
+ + + + + + + \ No newline at end of file diff --git a/src/html/portconfig.html b/src/html/portconfig.html new file mode 100644 index 0000000..d39115d --- /dev/null +++ b/src/html/portconfig.html @@ -0,0 +1,66 @@ + + + + + + + Port Konfiguration + + +

Port Konfiguration

+
+
+ +
+ + + + + + \ No newline at end of file diff --git a/src/html/style.css b/src/html/style.css new file mode 100644 index 0000000..45838e8 --- /dev/null +++ b/src/html/style.css @@ -0,0 +1,120 @@ + +/* For more information visit:https://fipsok.de */ +body { + font-family: sans-serif; + background-color: #87cefa; + display: flex; + flex-flow: column; + align-items: center; +} +h1,h2 { + color: #e1e1e1; + text-shadow: 2px 2px 2px black; +} +li { + background-color: #feb1e2; + list-style-type: none; + margin-bottom: 10px; + padding: 2px 5px 1px 0; + box-shadow: 5px 5px 5px rgba(0,0,0,0.7); +} +li a:first-child, li b { + background-color: #8f05a5; + font-weight: bold; + color: white; + text-decoration:none; + padding: 2px 5px; + text-shadow: 2px 2px 1px black; + cursor:pointer; +} +li strong { + color: red; +} +input { + height:35px; + font-size:14px; + padding-left: .3em; +} +label + a { + text-decoration: none; +} +h1 + main { + display: flex; +} +aside { + display: flex; + flex-direction: column; + padding: 0.2em; +} +button { + height:40px; + width:130px; + font-size:16px; + margin-top: 1em; + box-shadow: 5px 5px 5px rgba(0,0,0,0.7); +} +div button { + background-color: #7bff97; +} +nav { + display: flex; + align-items: baseline; + justify-content: space-between; +} +#left { + align-items:flex-end; + text-shadow: 0.5px 0.5px 1px #757474; +} +#cr { + font-weight: bold; + cursor:pointer; + font-size: 1.5em; +} +#up { + width: auto; +} +.note { + background-color: #fecdee; + padding: 0.5em; + margin-top: 1em; + text-align: center; + max-width: 320px; + border-radius: 0.5em; +} +.no { + display: none; +} +form [title] { + background-color: skyblue; + font-size: 1em; + width: 120px; +} +form:nth-of-type(2) { + margin-bottom: 1em; +} +[value*=Format] { + margin-top: 1em; + box-shadow: 5px 5px 5px rgba(0,0,0,0.7); +} +[name="group"] { + display: none; +} +[name="group"] + label { + font-size: 1.5em; + margin-right: 5px; +} +[name="group"] + label::before { + content: "\002610"; +} +[name="group"]:checked + label::before { + content: '\002611\0027A5'; +} +@media only screen and (max-width: 500px) { + .ip { + right: 6em; + position: relative; + } + aside { + max-width: 50vw; + } +}