Initial Setup

This commit is contained in:
XPS\Micro 2026-01-25 16:53:17 +01:00
commit 7cdb899e01
6 changed files with 4402 additions and 0 deletions

59
.gitignore vendored Normal file
View File

@ -0,0 +1,59 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
# Build-Artefakte und kompilierte Dateien
*.elf
*.bin
*.hex
*.map
*.o
*.a
*.zip
# Eagle Backup-Dateien
*.s#?
*.b#?
*.l#?
# PlatformIO
.pio/
.pioenvs/
.piolibdeps/
firmware/build/
# KiCad temporaere Dateien
*.000
*.bak
*.bck
*-bak
*-cache.lib
*.kicad_pcb-bak
*.kicad_sch-bak
fp-info-cache
*-save.pro
*-save.kicad_pcb
# Generierte Fertigungsdateien (optional, je nach Workflow)
hardware/gerber/*.gbr
hardware/gerber/*.drl
# CAD temporaere Dateien
*.stl.autosave
*.step.autosave
# Halte .gitkeep Dateien
!.gitkeep
# IDE-Einstellungen
.vscode/
.idea/
*.code-workspace
# Betriebssystem-Dateien
.DS_Store
Thumbs.db
desktop.ini

2601
html/index.html Normal file

File diff suppressed because it is too large Load Diff

64
html_to_header.py Normal file
View File

@ -0,0 +1,64 @@
import os
import gzip
html_path = "html/index.html"
header_path = "include/html.h"
# HTML-Datei lesen
with open(html_path, 'r', encoding='utf-8') as f:
html_content = f.read()
# HTML-Inhalt mit GZIP komprimieren
html_bytes = html_content.encode('utf-8')
compressed = gzip.compress(html_bytes, compresslevel=9)
# Original- und komprimierte Größe ausgeben
original_size = len(html_bytes)
compressed_size = len(compressed)
ratio = (1 - compressed_size / original_size) * 100
print(f"Original-Größe: {original_size:6d} Bytes")
print(f"Komprimierte Größe: {compressed_size:6d} Bytes")
print(f"Kompression: {ratio:5.1f}%")
# Header-Datei erstellen mit komprimierten Daten
header_content = f"""// Auto-generiert von html_to_header.py
// Original-Größe: {original_size} Bytes
// Komprimierte Größe: {compressed_size} Bytes
// Kompression: {ratio:.1f}%
#ifndef HTML_H
#define HTML_H
#include <Arduino.h>
// GZIP-komprimierte HTML-Seite
const uint8_t HTML_PAGE_GZIP[] PROGMEM = {{
"""
# Komprimierte Bytes als Hex-Array schreiben
for i, byte in enumerate(compressed):
if i % 16 == 0:
header_content += " "
header_content += f"0x{byte:02x}"
if i < len(compressed) - 1:
header_content += ","
if (i + 1) % 16 == 0:
header_content += "\n"
else:
header_content += " "
header_content += f"""
}};
const size_t HTML_PAGE_GZIP_LEN = {compressed_size};
#endif // HTML_H
"""
# Header-Datei schreiben
os.makedirs(os.path.dirname(header_path), exist_ok=True)
with open(header_path, 'w', encoding='utf-8') as f:
f.write(header_content)
print(f"\nHeader-Datei erstellt: {header_path}")

1320
include/html.h Normal file

File diff suppressed because it is too large Load Diff

79
platformio.ini Normal file
View File

@ -0,0 +1,79 @@
[platformio]
default_envs = esp8266clone
[common]
lib_ldf_mode = deep
; Monitor-Einstellungen
monitor_speed = 115200
; RTS/DTR für Auto-Reset (wichtig für Clones!)
monitor_dtr = 1
monitor_rts = 1
monitor_filters =
default
time
esp8266_exception_decoder
[env]
framework = arduino
platform = espressif8266
; Bibliotheken
lib_deps =
; Build-Flags für Debug
build_flags =
-I include
-DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
-DDEBUG_ESP_PORT=Serial
-DDEBUG_ESP_CORE=false
-DDEBUG_MODE=false ;true = Debug, false = Produktiv
-DNDEBUG
-DDEBUG_SOURCE=true
-DDEBUG_ESP_WIFI=0
-DUSE_RTC=false
-DTEST_RGB_ONLY=false
-DTEST_RGB=false
-DPOWERLOSSDETECT=false
; Monitor-Einstellungen
monitor_filters =
default
time
esp8266_exception_decoder
extra_scripts = pre:html_to_header.py
[env:esp8266clone]
board = esp12e ; ESP8266MOD = ESP-12E/F kompatibel
framework = ${env.framework}
platform = ${env.platform}
; Serial-Einstellungen
monitor_speed = ${common.monitor_speed}
upload_speed = 115200 ; Langsam für Clone-Stabilität
upload_port = COM13
; Flash-Einstellungen (sicher für alle Clones)
board_build.flash_mode = dio ; Statt qio - kompatibler!
# CPU-Frequenz auf 160MHz erhöhen
board_build.f_cpu = 160000000L
; Flash-Einstellungen (4MB Flash für Standard D1 Mini)
board_build.f_flash = 40000000L
board_build.ldscript = eagle.flash.4m2m.ld ;2 MB Sketch, 2 MB OTA kein SPIFFS
; Build-Flags für Debug
build_flags =
${env.build_flags}
;-DBRIDGE_LEDS_POS77
; Bibliotheken
lib_deps = ${env.lib_deps}
; Monitor-Einstellungen
monitor_filters = ${common.monitor_filters}
; RTS/DTR für Auto-Reset (wichtig für Clones!)
monitor_dtr = 1
monitor_rts = 1

279
src/main.cpp Normal file
View File

@ -0,0 +1,279 @@
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <DNSServer.h>
#include <EEPROM.h>
#include <Wire.h>
#include <ESP8266httpUpdate.h>
#include <Updater.h>
#include <html.h>
// Auto-generated version from build date/time
#define BUILD_DATE __DATE__
#define BUILD_TIME __TIME__
#define AP_SSID_LENGHT 14
#define AP_PASSWORD_LENGTH 17
// WiFi Zugangsdaten
const char* ssid = "DEINW_WLAN_SSID";
const char* password = "DEIN_WLAN_PASSWORT";
char AP_SSID[AP_SSID_LENGHT] = "CharGraph-01\0";
char AP_PASSWORD[AP_PASSWORD_LENGTH] = "MeinPasswort123\0";
// ════════════════════════════════════════════════════════════════
// OTA UPDATE ADRESSEN (346 bytes frei: 166-511)
// ════════════════════════════════════════════════════════════════
#define ADDR_OTA_VERSION 166 // Firmware-Version (32 bytes)
#define ADDR_OTA_BUILD_DATE 198 // Build-Datum/Zeit (20 bytes)
#define ADDR_OTA_FLAGS 218 // OTA-Status-Flags (1 byte)
#define OTA_FLAG_UPDATE_SUCCESS 0x01
#define OTA_FLAG_UPDATE_FAILED 0x02
// ════════════════════════════════════════════════════════════════
// OTA UPDATE VARIABLEN
// ════════════════════════════════════════════════════════════════
char firmwareVersion[32] = "BASIC";
bool otaInProgress = false;
int otaProgress = 0;
String otaError = "";
unsigned long otaStartTime = 0;
IPAddress apIP(192, 168, 4, 1);
// ════════════════════════════════════════════════════════════════
// NETZWERK
// ════════════════════════════════════════════════════════════════
ESP8266WebServer server(80);
DNSServer dnsServer;
const byte DNS_PORT = 53;
// ════════════════════════════════════════════════════════════════
// WEBSERVER HANDLER
// ════════════════════════════════════════════════════════════════
void handleRoot() {
// GZIP-komprimierte HTML-Seite senden
server.sendHeader("Content-Encoding", "gzip");
server.send_P(200, "text/html", (const char*)HTML_PAGE_GZIP, HTML_PAGE_GZIP_LEN);
}
void showOTAProgress(int progress, bool isError = false, bool isSuccess = false) {
}
void otaProgressCallback(size_t current, size_t total) {
if (total > 0) {
int progress = (current * 100) / total;
otaProgress = progress;
static int lastProgress = -1;
if (progress != lastProgress) {
showOTAProgress(progress);
lastProgress = progress;
}
}
yield();
}
// ════════════════════════════════════════════════════════════════
// OTA UPDATE HANDLER
// ════════════════════════════════════════════════════════════════
void handleOTAInfo() {
String json = "{";
json += "\"version\":\"" + String(firmwareVersion) + "\",";
json += "\"buildDate\":\"" + String(BUILD_DATE) + "\",";
json += "\"buildTime\":\"" + String(BUILD_TIME) + "\",";
json += "\"freeSpace\":" + String(ESP.getFreeSketchSpace()) + ",";
json += "\"sketchSize\":" + String(ESP.getSketchSize()) + ",";
json += "\"chipId\":\"" + String(ESP.getChipId(), HEX) + "\",";
json += "\"flashSize\":" + String(ESP.getFlashChipRealSize()) + ",";
json += "\"otaReady\":" + String(otaInProgress ? "false" : "true");
json += "}";
server.send(200, "application/json", json);
}
void handleOTAUpload() {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
otaInProgress = true;
otaProgress = 0;
otaError = "";
otaStartTime = millis();
WiFi.setSleepMode(WIFI_NONE_SLEEP);
showOTAProgress(0);
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if (!Update.begin(maxSketchSpace)) {
Update.printError(Serial);
otaError = "Begin failed";
}
}
else if (upload.status == UPLOAD_FILE_WRITE) {
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
Update.printError(Serial);
otaError = "Write failed";
} else {
size_t progress = Update.progress();
size_t total = Update.size();
if (total > 0) {
otaProgressCallback(progress, total);
}
}
}
else if (upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) {
showOTAProgress(100, false, true);
EEPROM.write(ADDR_OTA_FLAGS, OTA_FLAG_UPDATE_SUCCESS);
EEPROM.commit();
otaInProgress = false;
delay(3000);
ESP.restart();
} else {
Update.printError(Serial);
otaError = String(Update.getError());
showOTAProgress(100, true, false);
otaInProgress = false;
}
}
}
void handleOTAUploadDone() {
if (otaError.length() > 0) {
String json = "{\"success\":false,\"message\":\"" + otaError + "\"}";
server.send(500, "application/json", json);
} else {
String json = "{\"success\":true,\"message\":\"Update erfolgreich\"}";
server.send(200, "application/json", json);
}
}
void handleOTAFromURL() {
if (!server.hasArg("url")) {
server.send(400, "application/json",
"{\"success\":false,\"message\":\"URL fehlt\"}");
return;
}
String firmwareURL = server.arg("url");
if (firmwareURL.length() == 0) {
server.send(400, "application/json",
"{\"success\":false,\"message\":\"URL leer\"}");
return;
}
server.send(200, "application/json",
"{\"success\":true,\"message\":\"Update gestartet\"}");
delay(500);
otaInProgress = true;
otaProgress = 0;
otaError = "";
otaStartTime = millis();
WiFi.setSleepMode(WIFI_NONE_SLEEP);
showOTAProgress(0);
ESPhttpUpdate.onProgress([](int current, int total) {
otaProgressCallback(current, total);
});
ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW);
WiFiClient client;
t_httpUpdate_return ret = ESPhttpUpdate.update(client, firmwareURL);
switch (ret) {
case HTTP_UPDATE_FAILED:
otaError = ESPhttpUpdate.getLastErrorString();
showOTAProgress(100, true, false);
otaInProgress = false;
break;
case HTTP_UPDATE_NO_UPDATES:
otaError = "No updates available";
showOTAProgress(100, true, false);
otaInProgress = false;
break;
case HTTP_UPDATE_OK:
showOTAProgress(100, false, true);
EEPROM.write(ADDR_OTA_FLAGS, OTA_FLAG_UPDATE_SUCCESS);
EEPROM.commit();
delay(3000);
ESP.restart();
break;
}
}
void handleOTAStatus() {
String json = "{";
json += "\"inProgress\":" + String(otaInProgress ? "true" : "false") + ",";
json += "\"progress\":" + String(otaProgress) + ",";
json += "\"error\":\"" + otaError + "\",";
json += "\"elapsed\":" + String(millis() - otaStartTime);
json += "}";
server.send(200, "application/json", json);
}
void handleNotFound() {
Serial.println("→ handleNotFound aufgerufen");
server.sendHeader("Location", "/", true);
server.send(302, "text/plain", "");
}
void setup()
{
Serial.begin(115200);
// WiFi verbinden
WiFi.mode(WIFI_AP_STA); // Dual-Mode: AP + Station
WiFi.softAP(AP_SSID, AP_PASSWORD);
delay(5000);
yield();
IPAddress apIP(192, 168, 4, 1);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
dnsServer.start(DNS_PORT, "*", apIP);
Serial.println("");
Serial.println("");
Serial.print("Verbunden mit: ");
Serial.println(ssid);
Serial.print("IP-Adresse: ");
Serial.println(WiFi.localIP());
// Root-Endpoint
server.on("/", handleRoot);
server.on("/ota/info", handleOTAInfo);
server.on("/ota/upload", HTTP_POST, handleOTAUploadDone, handleOTAUpload);
server.on("/ota/url", handleOTAFromURL);
server.on("/ota/status", handleOTAStatus);
server.onNotFound(handleNotFound);
// Server starten
server.begin();
Serial.println("HTTP-Server gestartet");
Serial.print("OTA-Upload unter: http://");
Serial.print(WiFi.localIP());
Serial.println("/update");
}
void loop(){
dnsServer.processNextRequest();
server.handleClient();
yield();
}