keypatch/src/KeyPatch.ino

248 lines
7.1 KiB
C++

#include <Wire.h>
#include <PCF8575.h>
#include <Adafruit_NeoPixel.h>
#include <ESP8266WebServer.h>
#include <ArduinoOTA.h>
#include <LittleFS.h>
#include "Config.ino"
#include <DNSServer.h>
#include <ArduinoJson.h>
// Globale Deklarationen für alle Tabs
ESP8266WebServer server(80);
const byte DNS_PORT = 53;
DNSServer dnsServer;
String sketchName() { // Dateiname für den Admin Tab ab EspCoreVersion 2.6.0
char file[sizeof(__FILE__)] = __FILE__;
char * pos = strrchr(file, '.'); *pos = '\0';
return file;
}
#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);
Serial.printf("\nSketchname: %s\nBuild: %s\t\tIDE: %d.%d.%d\n%s\n\n",
(__FILE__), (__TIMESTAMP__), ARDUINO / 10000, ARDUINO % 10000 / 100, ARDUINO % 100 / 10 ? ARDUINO % 100 : ARDUINO % 10, ESP.getFullVersion().c_str());
Serial.printf("\nKeyPatch ESP8266 WebServer\n");
//Set UP 12C Communication
Wire.begin();
for (byte adress=8; adress<120; adress++)
{
Wire.beginTransmission(adress);
if (Wire.endTransmission()==0)
{
Serial.print(number+1);
Serial.print(". Device: 0x");
Serial.println(adress, HEX);
number++;
}
}
Serial.print (number, DEC);
Serial.println (" device(s) found.");
//Setup CPF Expander
Serial.print("Expander 1 at ");
Serial.print(EXPANDER1ADDRESS,HEX);
if (!expander1.begin())
{
Serial.println(" could not initialize...");
}
if (!expander1.isConnected())
{
Serial.println(" => not connected");
}
else
{
Serial.println(" => connected!!");
}
//Setup NeoPixel
NeoPixel_init(AMOUNTOFPORTS); // Initialisierung der NeoPixel
for (uint8_t i = 0; i < AMOUNTOFPORTS; i++) {
portStates[i] = 0;
lastStates[i] = 0;
pixels->setPixelColor(i, pixels->Color(0, 0, 0)); // aus
};
pixels->show();
// SetUp WebServer
setupFS(); // setupFS(); oder spiffs(); je nach Dateisystem
loadPortConfig(); // Lade Port-Konfiguration
connectWifi();
admin();
ArduinoOTA.onStart([]() {
//save(); // Wenn Werte vor dem Neustart gespeichert werden sollen
});
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...");
}
void loop() {
ArduinoOTA.handle();
server.handleClient();
if (millis() < 0x2FFF || millis() > 0xFFFFF0FF) { // Die Funktion "runtime()" wird nur für den Admin Tab gebraucht.
runtime(); // Auskommentieren falls du den Admin Tab nicht nutzen möchtest.
}
dnsServer.processNextRequest();
reStation();
// Erst 16 Ports aus PCF8575 einlesen
for (uint8_t i = 0; i < AMOUNTOFPORTS; i++) {
portStates[i] = expander1.read(i);
}
for (uint8_t i = 0; i < AMOUNTOFPORTS; i++) {
if (portStates[i] != lastStates[i]) {
// Ausgabe: geänderte Ports melden
Serial.print("Pin ");
Serial.print(i);
Serial.print(" hat sich geändert -> ");
Serial.println(portStates[i]);
lastStates[i] = portStates[i];
}
// LED aktualisieren
if (portConfigs[i].enabled) {
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
} else {
pixels->setPixelColor(i, pixels->Color(0, 0, 0)); // aus
}
}
} else {
pixels->setPixelColor(i, pixels->Color(0, 0, 0)); // aus
}
}
pixels->show();
delay(50);
}