Esp8266 Admin als Arduino Tab.
Die Spiffs.ino ist zum ausführen der Admin.ino erforderlich.
Admin.ino
// ****************************************************************
// Sketch Esp8266 Admin Modular(Tab)
// created: Jens Fleischer, 2018-05-09
// last mod: Jens Fleischer, 2021-06-09
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp8266
// Software: Esp8266 Arduino Core 2.4.2 / 2.5.0 / 2.5.2
// Geprüft: von 1MB bis 16MB Flash
// Getestet auf: Nodemcu, Wemos D1 Mini Pro, Sonoff Switch, Sonoff Dual
/******************************************************************
Copyright (c) 2018 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
*******************************************************************/
// Diese Version von Admin sollte als Tab eingebunden werden.
// #include <FS.h> #include <ESP8266WebServer.h> müssen im Haupttab aufgerufen werden
// Die Funktionalität des ESP8266 Webservers ist erforderlich.
// Die Spiffs.ino muss im ESP8266 Webserver enthalten sein
// Funktion "admin();" muss im setup() nach spiffs() aber vor dem Verbindungsaufbau aufgerufen werden.
// Die Funktion "runtime();" muss mindestens zweimal innerhalb 49 Tage aufgerufen werden.
// Entweder durch den Client(Webseite) oder zur Sicherheit im "loop();"
/**************************************************************************************/
const char* const PROGMEM flashChipMode[] = {"QIO", "QOUT", "DIO", "DOUT", "Unbekannt"};
void admin() { // Funktionsaufruf "admin();" muss im Setup eingebunden werden
File file = SPIFFS.open("/config.json", "r");
if (file) {
String newhostname = file.readStringUntil('\n');
if (newhostname != "") {
WiFi.hostname(newhostname.substring(1, newhostname.length() - 1));
file.close();
ArduinoOTA.setHostname(WiFi.hostname().c_str());
}
}
server.on("/admin/renew", handlerenew);
server.on("/admin/once", handleonce);
server.on("/reconnect", []() {
server.send(304, "message/http");
WiFi.reconnect();
});
server.on("/restart", []() {
server.send(304, "message/http");
//speichern(); //Wenn Werte vor dem Neustart gespeichert werden sollen
ESP.restart();
});
}
//Es kann entweder die Spannung am ADC-Pin oder die Modulversorgungsspannung (VCC) ausgegeben werden.
void handlerenew() { // Um die am ADC-Pin anliegende externe Spannung zu lesen, verwende analogRead (A0)
server.send(200, "application/json", "[\"" + runtime() + "\",\"" + WiFi.RSSI() + "\",\"" + analogRead(A0) + "\"]"); // Json als Array
}
/*
ADC_MODE(ADC_VCC);
void handlerenew() { // Zum Lesen der Modulversorgungsspannung (VCC), verwende ESP.getVcc()
server.send(200, "application/json", "[\"" + runtime() + "\",\"" + WiFi.RSSI() + "\",\"" + ESP.getVcc() / 1024.0 + " V" + "\"]");
}
*/
void handleonce() {
if (server.arg(0) != "") {
WiFi.hostname(server.arg(0));
File f = SPIFFS.open("/config.json", "w"); // Datei zum schreiben öffnen
f.printf("\"%s\"\n", WiFi.hostname().c_str());
f.close();
}
char *pos = strrchr((__FILE__), strstr (__FILE__, "\\") ? '\\' : '/'); *pos = '\0';
String temp = "{\"File\":\"" + String(__FILE__).substring(String(__FILE__).lastIndexOf (strstr (__FILE__, "\\") ? '\\' : '/') + 1, String(__FILE__).length()) +
"\", \"Build\":\"" + String(__DATE__) + " " + String(__TIME__) + "\", \"SketchSize\":\"" + formatBytes(ESP.getSketchSize()) +
"\", \"SketchSpace\":\"" + formatBytes(ESP.getFreeSketchSpace()) + "\", \"LocalIP\":\"" + WiFi.localIP().toString() +
"\", \"Hostname\":\"" + WiFi.hostname() + "\", \"SSID\":\"" + WiFi.SSID() + "\", \"GatewayIP\":\"" + WiFi.gatewayIP().toString() +
"\", \"Channel\":\"" + WiFi.channel() + "\", \"MacAddress\":\"" + WiFi.macAddress() + "\", \"SubnetMask\":\"" + WiFi.subnetMask().toString() +
"\", \"BSSID\":\"" + WiFi.BSSIDstr() + "\", \"ClientIP\":\"" + server.client().remoteIP().toString() + "\", \"DnsIP\":\"" + WiFi.dnsIP().toString() +
"\", \"ResetReason\":\"" + ESP.getResetReason() + "\", \"CpuFreqMHz\":\"" + F_CPU / 1000000 + "\", \"FreeHeap\":\"" + formatBytes(ESP.getFreeHeap()) +
"\", \"ChipSize\":\"" + formatBytes(ESP.getFlashChipSize()) + "\", \"ChipSpeed\":\"" + ESP.getFlashChipSpeed() / 1000000 +
"\", \"ChipMode\":\"" + flashChipMode[ESP.getFlashChipMode()] + "\", \"IdeVersion\":\"" + ARDUINO +
"\", \"CoreVersion\":\"" + ESP.getCoreVersion() + "\", \"SdkVersion\":\"" + ESP.getSdkVersion()+ "\"}";
server.send(200, "application/json", temp); // Json als Objekt
}
String runtime() {
static uint8_t rolloverCounter = 0;
static uint32_t letzteMillis = 0;
uint32_t aktuelleMillis = millis();
if (aktuelleMillis < letzteMillis) { // prüft Millis Überlauf
rolloverCounter++;
}
letzteMillis = aktuelleMillis;
uint32_t sek = (0xFFFFFFFF / 1000 ) * rolloverCounter + (aktuelleMillis / 1000);
char buf[20];
snprintf(buf, sizeof(buf), sek < 86400 || sek > 172800 ? "%d Tage %02d:%02d:%02d" : "%d Tag %02d:%02d:%02d", sek / 86400, sek / 3600 % 24, sek / 60 % 60, sek % 60);
return buf;
}
Die Funktion "runtime();" muss mindestens zweimal innerhalb 49 Tage aufgerufen werden.
Entweder durch den Client(Webseite) oder besser im loop().
*************** Aufruf runtime() ******************
void loop() {
......
.........
if (millis() < 0x2FFF || millis() > 0xFFFFF0FF) {
runtime();
}
......
}
Das Webinterface zum Esp8266 Admin Tab.
admin.html
<!DOCTYPE HTML> <!-- For more information visit: https://fipsok.de -->
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<title>ESP8266 Admin</title>
<script>
window.addEventListener('load', () => {
renew(),once();
let output = document.querySelector('#note');
let button = document.querySelectorAll('button');
let span = document.querySelectorAll('#right span');
button[0].addEventListener('click', () => {
window.location = '/spiffs.html';
});
button[1].addEventListener('click', () => {
window.location = '/';
});
button[2].addEventListener('click', check.bind(this, document.querySelector('input')));
button[3].addEventListener('click', re.bind(this, 'reconnect'));
button[4].addEventListener('click', () => {
if (confirm('Bist du sicher!')) re('restart');
});
function once(arg1,arg2) {
fetch('/admin/once', {
method: 'POST',
body: arg1
}).then( (resp) => {
return resp.json();
}).then( (obj) => {
output.innerHTML = '';
output.classList.remove('note');
document.querySelector('form').reset();
if (arg1 == undefined) myIv = setInterval(renew, 1000);
if (arg2 == 'reconnect') re(arg2);
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['ChipSize'];
span[21].innerHTML = obj['ChipSpeed'] + " MHz";
span[22].innerHTML = obj['ChipMode'];
span[23].innerHTML = obj['IdeVersion'].replace(/(\d)(\d)(\d)(\d)/,obj['IdeVersion'][3]!=0 ? '$1.$3.$4' : '$1.$3.');
span[24].innerHTML = obj['CoreVersion'].replace(/_/g,'.');
span[25].innerHTML = obj['SdkVersion'];
}).catch(function(err) {
re();
});
}
function renew() {
fetch('admin/renew').then( (resp) => {
return resp.json();
}).then( (array) => {
array.forEach((x, i) => {
span[i].innerHTML = x
});
});
}
function check(inObj) {
!inObj.checkValidity() ? (output.innerHTML = inObj.validationMessage, output.classList.add('note')) : (once(inObj.value, 'reconnect'));
}
function re(arg) {
clearInterval(myIv);
fetch(arg);
output.classList.add('note');
if (arg == 'restart') {
output.innerHTML = 'Der Server wird neu gestartet. Die Daten werden in 15 Sekunden neu geladen.';
setTimeout(once, 15000);
}
else if (arg == 'reconnect'){
output.innerHTML = 'Die WiFi Verbindung wird neu gestartet. Daten werden in 10 Sekunden neu geladen.';
setTimeout(once, 10000);
}
else {
output.innerHTML = 'Es ist ein Verbindungfehler aufgetreten. Es wird versucht neu zu verbinden.';
setTimeout(once, 3000);
}
}
});
</script>
</head>
<body>
<h1>ESP8266 Admin Page</h1>
<main>
<aside id="left">
<span>Runtime ESP:</span>
<span>WiFi RSSI:</span>
<span>ADC/VCC:</span>
<span>Sketch Name:</span>
<span>Sketch Build:</span>
<span>SketchSize:</span>
<span>FreeSketchSpace:</span>
<span>IPv4 Address:</span>
<span>Hostname:</span>
<span>Connected to:</span>
<span>Gateway IP:</span>
<span>Channel:</span>
<span>MacAddress:</span>
<span>SubnetMask:</span>
<span>BSSID:</span>
<span>Client IP:</span>
<span>DnsIP:</span>
<span>Reset Ground:</span>
<span>CPU Freq:</span>
<span>FreeHeap:</span>
<span>FlashSize:</span>
<span>FlashSpeed:</span>
<span>FlashMode:</span>
<span>Arduino IDE Version:</span>
<span>Esp Core Version:</span>
<span>SDK Version:</span>
</aside>
<aside id="right">
<span>0</span>
<div>
<span></span>
dBm
</div>
<span>0</span>
<span>?</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>?</span>
<span>?</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>?</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
</aside>
</main>
<div>
<button>Spiffs</button>
<button>Startseite</button>
</div>
<div id="note"></div>
<div>
<form>
<input placeholder=" neuer Hostname" pattern="([A-Za-z0-9\-]{1,32})" title="Es dürfen nur Buchstaben
(a-z, A-Z), Ziffern (0-9) und Bindestriche (-) enthalten sein. Maximal 32 Zeichen" required>
<button type="button">Name Senden</button>
</form>
</div>
<div>
<button>WiFi Reconnect</button>
<button>ESP Restart</button>
</div>
</body>
</html>
Für die Darstellung ist die style.css analog Spiffs Verwaltung, erforderlich.
style.css
/* 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;
}
h1 + main {
display: flex;
}
aside {
display: flex;
flex-direction: column;
padding: 0.2em;
}
#left {
align-items:flex-end;
text-shadow: 0.5px 0.5px 1px #757474;
}
.note {
background-color: #fecdee;
padding: 0.5em;
margin-top: 1em;
text-align: center;
max-width: 320px;
border-radius: 0.5em;
}
[type=submit] {
height:40px;
font-size: 16px;
}
[value*=Format] {
margin-top: 1em;
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
}
button {
height:40px;
width:130px;
background-color: #7bff97;
font-size:16px;
margin-top: 1em;
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
}
form [title] {
background-color: skyblue;
font-size: 16px;
width: 125px;
}