CharGraphs added

This commit is contained in:
XPS\Micro 2026-02-10 19:51:57 +01:00
parent 679cfab9e8
commit 520bcf7d7f
10 changed files with 227 additions and 67 deletions

134
include/WordFinder.h Normal file
View File

@ -0,0 +1,134 @@
/**
* CharGraph - Unified Word Finder
*
* Central component for finding words in patterns
* Handles both PROGMEM patterns (for validation) and regular strings (for display)
* Shared between CharGraphTimeLogic and rgbPanel
*/
#ifndef WORD_FINDER_H
#define WORD_FINDER_H
#include <Arduino.h>
// ============================================================================
// PROGMEM WORD SEARCH (for validation)
// ============================================================================
/**
* Find word in PROGMEM pattern starting from position
* Both pattern and word are in PROGMEM
*
* @param pattern_progmem Pattern string in PROGMEM
* @param word_progmem Word pointer in PROGMEM
* @param searchStart Start position for search (default 0)
* @return Position of word, or -1 if not found
*/
inline int16_t findWordProgmem(const char* pattern_progmem, const char* word_progmem, uint16_t searchStart = 0) {
if (!pattern_progmem || !word_progmem) return -1;
// Load word from PROGMEM into buffer (max 11 chars: DREIVIERTEL)
char wordBuf[12]; // 11 chars + null terminator
strcpy_P(wordBuf, word_progmem);
uint8_t wordLen = strlen(wordBuf);
// Search through pattern_progmem byte by byte
uint16_t pos = searchStart;
while (true) {
char patByte = pgm_read_byte(pattern_progmem + pos);
if (patByte == '\0') break; // End of pattern
// Check if word matches at this position
bool match = true;
for (uint8_t i = 0; i < wordLen; i++) {
char p = pgm_read_byte(pattern_progmem + pos + i);
if (p != wordBuf[i]) {
match = false;
break;
}
}
if (match) return pos;
pos++;
}
return -1;
}
// ============================================================================
// STRING WORD SEARCH (for display)
// ============================================================================
/**
* Find word in regular string with occurrence support
*
* @param text String to search in
* @param word Word to find
* @param occurrence Which occurrence to find (0 = first, 1 = second, etc.)
* @param searchBackward Search backward from end
* @return Position of word, or -1 if not found
*/
inline int findWordString(const char* text, const char* word, int occurrence = 0, bool searchBackward = false) {
if (!text || !word) return -1;
const int wordLen = strlen(word);
const int textLen = strlen(text);
if (searchBackward) {
// Backward search: from end to beginning
int foundCount = 0;
for (int pos = textLen - wordLen; pos >= 0; --pos) {
bool match = true;
for (int j = 0; j < wordLen; ++j) {
unsigned char c1 = text[pos + j];
unsigned char c2 = word[j];
// Lowercase comparison (case-insensitive)
if (c1 >= 'A' && c1 <= 'Z') c1 += ('a' - 'A');
if (c2 >= 'A' && c2 <= 'Z') c2 += ('a' - 'A');
if (c1 != c2) {
match = false;
break;
}
}
if (match) {
if (foundCount == occurrence) {
return pos;
}
foundCount++;
}
}
} else {
// Forward search: from beginning to end
int foundCount = 0;
for (int pos = 0; pos <= textLen - wordLen; ++pos) {
bool match = true;
for (int j = 0; j < wordLen; ++j) {
unsigned char c1 = text[pos + j];
unsigned char c2 = word[j];
// Lowercase comparison (case-insensitive)
if (c1 >= 'A' && c1 <= 'Z') c1 += ('a' - 'A');
if (c2 >= 'A' && c2 <= 'Z') c2 += ('a' - 'A');
if (c1 != c2) {
match = false;
break;
}
}
if (match) {
if (foundCount == occurrence) {
return pos;
}
foundCount++;
}
}
}
return -1;
}
#endif // WORD_FINDER_H

View File

@ -3,10 +3,30 @@
//Hier Umlaute wie Ö und Ü bereits durch o und u wegen UTF8 ersetzen!!!!
const char DEFAULT_CHARSOAP[] = "ESHISTRFASTKURZVIERTELFuNFZWANZIGAZEHNJNACHRVORWHALBKLBZWEINSECHSEELFVIERACHTFuNFSIEBENITDREIZWoLFZZEHNEUNLUHR"; //119
const char DEFAULT_CHARSOAP[] PROGMEM = "ESPISTGZEHNFuNFVIERTELVORNACHBALDHALBGZWEINSWCDLZIAXMTVZEHNEUNACHTELFuNFZWoLFSECHSIEBENBGDREIVIERZECRPFACWLUHR";//DLZIAX
//"ESYISTHZEHNFuNFVIERTELVORNACHBALDHALBUZWEINSDG6MSMARTINZEHNEUNACHTELFuNFZWoLFSECHSIEBENBXDREIVIERLUWRNFACWFUHR";//DG6MS
//"ESYISTMZEHNFuNFVIERTELVORNACHBALDHALBDZWEINSNZUXHAUSEMZZEHNEUNACHTELFuNFZWoLFSECHSIEBENHXDREIVIERKNOTPAUSECUHR";//Böcker
//"ESQISTFZEHNFuNFVIERTELVORNACHBALDHALBFYZWEITJEGARTNERVDELFuNFZWoLFZEHNEUNACHTXDREINSVIERSECHSIEBENCRWDSIMONUHR";//GARTNERSIMON
//"ESQISTOZEHNFuNFVIERTLEVORNACHBALDHALBPJZWEIOVQGARTNERCAELFuNFZWoLFZEHNEUNACHTYDREINSVIERSECHSIEBENLRWDTZEITUHR";//GÄRTNER
//"ESHISTQBALDVIERTELFuNFKURZEHNUVORVNACHTAHALBACHTZEHNEUNLIMONCIELLOXVIERIYZWEIELFZWoLFuNFDREINSIEBENSECHSRWDUHR";//LIMONCIELLO
//"ESHISTQBALDVIERTELFuNFKURZEHNUVORVNACHTAHALBACHTZEHNEUNLIMONCIELLOXVIERIYZWEIELFZWoLFuNFDREINSIEBENSECHSRWDUHR";//Limonciello
//"ESJISTSBALDVIERTELFuNFKURZEHNIVORINACHZOHALBGRDNEUSSOBHACHTZEHNEUNHVIERTBZWEIELFZWoLFuNFDREINSIEBENSECHSWCFUHR";//NEUSS
//"ESHISTGFuNFZEHNVIERTELVORFASTNACHHALBALKZWEIFCAROLAZEITDREINSIEBENELFZWoLFuNFZEHNEUNACHTOSECHSVIERGFURWDUWEUHR";//Carola
//"ESMISTKBALDFuNFASTZEHNVIERTELMVORKNACHPHALBDHANNESKBRAUZWoLFuNFHWADREIELFVIERSECHSIEBENARZWEINSJXLUZEHNEUNACHT";//Uwe Kleinhans
//"ESOISTNZEHNFuNFVIERTELVORNACHBALDHALBEZWEINSONEAFAMILYAZEHNEUNACHTELFuNFZWoLFSECHSIEBENKFREIVIERTLYAMPAUSEIUHR"; //Breunig
//"ESXISTXFASTBALDKURZEHNFuNFVIERTELVOROBNACHGEBGZPAULHALBDREINSZWEIJELFVIERFuNFZEHNEUNACHTOSECHSIEBENZWoLFRDWUHR";// LASKAI
//"ESHISTCKURZSIVOTXZEHNMFuNFVIERTELVORNACHBALDHALBXEINSMEZEHNEUNZWEIELFuNFZWoLFSECHSIEBENXMDREIVIERVILRACHTSYUHR";//Harald Mück
//"ESKISTHZEHNFuNFVIERTELVORNACHBALDHALBPZWEINSAXISIHTHEMZWATERMELONSELFuNFZWoLFSECHSIEBENTXDREIVIERECZEHNEUNACHT"; //ISI THE WATERMELON
//"ESHISTPZEHNFuNFVIERTELVORNACHBALDHALBWZWEINSELFuNFZWoLFZEHNEUNACHTDREIVIERJIEJASMIN&LEVIRSECHSIEBENGPAUSERDUHR";//JASMIN&LEVI
//"ESGISTKZEHNFuNFVIERTELVORNACHBALDHALBDZWEINSDREIVIERWLZZEHNEUNACHTPSECHSIEBENELFuNFZWoLFMAIKETOCVXIUHRU&FABIAN";//Maike&Fabian
//"ESHISTPZEHNFuNFVIERTELVORNACHBALDHALBWZWEINSELFuNFZWoLFZEHNEUNACHTDREIVIERJIEJASMINKLEVIRSECHSIEBENGPAUSERDUHR";//JASMIN&LEVI
//"ESHISTJZEHNFuNFVIERTELNACHFASTVORYHALBPBALDSQXBMICHLFPYDREINSIEBENELFZWoLFuNFPSECHSZVIERACHTZEHNEUNZWEIRWDUHRK";//Rouven Öttinger 12BM02
//"ESOISTBZEHNFuNFVIERTELVORNACHBALDHALBCZWEINSULINAKLUCADIDREIVIEREFZEHNEUNACHTELFuNFZWoLFSECHSIEBENBPAUSEMBYUHR"; //Spiller
//"ESHISTRFASTKURZVIERTELFuNFZWANZIGAZEHNJNACHRVORWHALBKLBZWEINSECHSEELFVIERACHTFuNFSIEBENITDREIZWoLFZZEHNEUNLUHR";
//"ESHISTRFASTKURZVIERTELFuNFZWANZIGAZEHNJNACHRVORWHALBKLBZWEINSECHSEELFVIERACHTFuNFSIEBENITDREIZWoLFZZEHNEUNLUHR"; //119
//"ESXISTEFuNFVIERTELZEHNKURZWXPNACHSVORTHALBTVEINSXJZWEIUDREIVIERYPHFuNFSECHSWYSIEBENACHTUNEUNZEHNELFZWoLFXUHRJT";
//115"ESHISTCKURZSIVORXZEHNMFuNFVIERTELVORNACHBALDHALBXEINSMEZEHNEUNZWEIELFuNFZWoLFSECHSIEBENXMDREIVIERVILRACHTSYUHR";
char testPattern[]="ES-IST-WIR-HABEN-BALD-FAST-KURZ-FuNF-ZEHN-DREIVIERTEL-VIERTEL-ZWANZIG-VOR-NACH-EIN-EINS-ZWEI-DREI-VIER-FuNF-SECHS-SIEBEN-ACHT-NEUN-ZEHN-ELF-ZWoLF-UHR-NACHT-PAUSE-ALARM-ZEIT-RWD-KKS-KARLKUEBEL-MINT\0";
char testPattern[]="ES-IST-WIR-HABEN-BALD-FAST-KURZ-FuNF-ZEHN-DREIVIERTEL-VIERTEL-ZWANZIG-VOR-NACH-EIN-EINS-ZWEI-DREI-VIER-FuNF-SECHS-SIEBEN-ACHT-NEUN-ZEHN-ELF-ZWoLF-UHR-NACHT-PAUSE-ALARM-ZEIT-RWD-KKS-KARLKUEBEL-MINT-UWE\0";
/*
"ES_IST_ZEHNFuNFVIERTELVORNACHBALDHALB_ZWEINS___________ZEHNEUNACHTELFuNFZWoLFSECHSIEBEN__DREIVIER____PAUSE_UHR"

View File

@ -25,7 +25,6 @@
#define AP_SSID_LENGHT 13
#define AP_PASSWORD_LENGTH 17
#define MAXWORDS 3 // Anzahl Spezialwörter
// ════════════════════════════════════════════════════════════════
// EEPROM ADRESSEN
@ -104,6 +103,6 @@
#define MINUTE_LEDS_MAGIC 0xEE
#define RUNNING_FLAG_MAGIC 0x42 // Magic Byte
#define MAGIC_BYTE_INIT 0xAA
#define MAGIC_BYTE_INIT 0x55
#endif

View File

@ -17,8 +17,8 @@ int8_t isConfigured = 0xFF;
const unsigned long AP_TIMEOUT = 300000;
bool powerLossDetected = false; //Flag für Stromausfall
char AP_SSID[AP_SSID_LENGHT] = "CharGrap101\0";
char AP_PASSWORD[AP_PASSWORD_LENGTH] = "MeinPasswort101\0";
char AP_SSID[AP_SSID_LENGHT] = "CharGraph\0";
char AP_PASSWORD[AP_PASSWORD_LENGTH] = "CharGraph\0";
uint16_t bootCounter = 0;

View File

@ -129,17 +129,17 @@ static uint8_t rule_15_19(const RuleContext& ctx, const char** outWords) {
// :20-:24 - ZWANZIG NACH or ZEHN VOR HALB (with fallback support)
static uint8_t rule_20_24(const RuleContext& ctx, const char** outWords) {
if (ctx.fallbackLevel == 0 && ctx.hasZwanzig) {
// Primary: ZWANZIG NACH [h]
// Primary: ZWANZIG NACH [h] - uses current hour
outWords[0] = ZWANZIG;
outWords[1] = NACH;
outWords[2] = ctx.hourWord;
return 3;
}
// Fallback: ZEHN VOR HALB [h+1]
// Fallback: ZEHN VOR HALB [h+1] - must use NEXT hour
outWords[0] = ZEHN;
outWords[1] = VOR;
outWords[2] = HALB;
outWords[3] = ctx.hourWord;
outWords[3] = getHourWord((ctx.h12 + 1) % 12);
return 4;
}

View File

@ -88,9 +88,11 @@ uint8_t getWordsForTime(
hasUhrAtEnd = afterUhrEmpty;
}
// ========== STEP 4: Calculate display hour (advance at mm >= 20) ==========
// ========== STEP 4: Calculate display hour (advance at mm >= 25) ==========
// :20-:24 ZWANZIG NACH uses current hour
// :25-:59 other rules use next hour
uint8_t h12 = hour % 12;
if (minute >= 20) {
if (minute >= 25) {
h12 = (h12 + 1) % 12;
}

View File

@ -10,15 +10,15 @@ CRGB specialColor = CRGB::Green;
CRGB leds[NUM_LEDS];
uint8_t brightness = 80;
char charsoap[COLS * ROWS * 2]; //Char * 2 to fit all in, cause UTF (maybe ÄÖÜ)
char charsoap[(COLS * ROWS * 2) + 1] = {'\0'}; //Char * 2 to fit all in, cause UTF (maybe ÄÖÜ)
bool customCharsoap = false;
// ── Konfiguration für Spezialanzeige ──
// MAXWORDS ist in defines.inc definiert
// Platzhalter - werden dynamisch aus EEPROM oder Defaults geladen (siehe loadSpecialWords() in main.cpp)
char SPECIAL_WORD[MAXWORDS][12] = {"", //max 11 Zeichen, \0 wird automatisch angefügt!
"",
"" }; //Text der ein- bzw. ausgeblendet werden soll
char SPECIAL_WORD[MAXWORDS][13] = {"DLZIAX\0", //max 11 Zeichen, \0 wird automatisch angefügt!
"FACW\0",
"\0" }; //Text der ein- bzw. ausgeblendet werden soll
const uint16_t SPECIAL_HOLD_MS = 5000; // Anzeigedauer des Spezialworts
const int hourpattern[][2] = {
@ -87,7 +87,7 @@ void fadeInCurrentFrame(uint8_t targetBrightness, uint8_t steps = MAX_STEPS, uin
// Zeigt ein Wort (falls vorhanden) mit sanftem Fade-In, hält es und blendet es wieder aus
// OPTIMIERT: Schnellere Animation für bessere WiFi-Performance
bool showSpecialWordSequence(const char words[][12], CRGB color, uint8_t steps = 20, uint16_t stepDelayMs = 15)
bool showSpecialWordSequence(const char words[][SPECIAL_WORD_LENGTH], CRGB color, uint8_t steps = 20, uint16_t stepDelayMs = 15)
{
int pos = findWord(words[0], 0);
@ -404,24 +404,27 @@ int findWord(const char* word, int occurrence, bool searchBackward)
uint8_t testWords()
{
//return 0;
#if (defined(TEST_ALLTHETIME) && TEST_ALLTHETIME == false)
return 0;
#endif
DEBUG_PRINT("╔════════════════════════╗\n");
DEBUG_PRINT("║ Teste Wörter ... ║\n");
DEBUG_PRINT("╚════════════════════════╝\n")
DEBUG_PRINTLN(DEFAULT_CHARSOAP);
if(strlen(DEFAULT_CHARSOAP) != (COLS * (ROWS-1)))
DEBUG_PRINTLN(charsoap);
if(strlen(charsoap) != (COLS * (ROWS-1)))
{
//return -1;
DEBUG_PRINT("\nLänge stimmt nicht: ");
DEBUG_PRINTLN(strlen(DEFAULT_CHARSOAP));
DEBUG_PRINTLN(strlen(charsoap));
}
else
{
DEBUG_PRINT("\nLänge stimmt: ");
DEBUG_PRINTLN(strlen(DEFAULT_CHARSOAP));
DEBUG_PRINTLN(strlen(charsoap));
}
uint8_t currentHour = 12;
uint8_t currentMinute = 29;
uint8_t currentHour = 0;
uint8_t currentMinute = 0;
//CharGraphTimeWords result;
while(true)
{
@ -429,10 +432,10 @@ uint8_t testWords()
currentMinute++;
if(currentMinute == 60)
{
currentHour = 1;
currentHour += 1;
currentMinute = 0;
}
if(currentHour == 1 && currentMinute == 31)
if(currentHour == 23 && currentMinute == 59)
{
DEBUG_PRINT ("\n╔════════════════════════╗\n");
DEBUG_PRINT ( "║ Teste Wörter fertig. ║\n");
@ -449,9 +452,6 @@ uint8_t testWords()
DEBUG_PRINT(currentMinute);
DEBUG_PRINT("' -> ");
//delay(1000);
//int8_t resultval = getCharGraphWords(DEFAULT_CHARSOAP, currentHour, currentMinute, result);
//if (resultval == 0)
{
// ===== Display current time =====
displayTime(currentHour, currentMinute);
@ -461,13 +461,13 @@ uint8_t testWords()
// DEBUG_PRINT("ERROR: "); DEBUG_PRINT(resultval); DEBUG_PRINTLN(" Pattern validation failed!");
// delay(1000);
//}
delay(500);
delay(50);
}
}
void checkPattern()
{
//testWords();
testWords();
FastLED.clear();
uint8_t lengthPattern = strlen(testPattern);

View File

@ -11,6 +11,8 @@
#define COLS 11
#define ROWS 11
#define MAXWORDS 3 //Anzahl Spezialwörter
#define SPECIAL_WORD_LENGTH (COLS+2)
extern CRGB normalColor;
extern CRGB specialColor;
extern CRGB leds[NUM_LEDS];
@ -20,9 +22,9 @@
extern int MINUTE_LEDS[4];
extern char testPattern[];
extern bool customCharsoap;
extern char charsoap[COLS * ROWS * 2];
extern char charsoap[(COLS * ROWS * 2)+1];
extern const char DEFAULT_CHARSOAP[];
extern char SPECIAL_WORD[3][12]; // MAXWORDS = 3
extern char SPECIAL_WORD[MAXWORDS][SPECIAL_WORD_LENGTH]; // MAXWORDS = 3
extern void showLEDs();
extern int bridgeLED(int pos);

View File

@ -43,6 +43,7 @@ build_flags =
-DUSE_RTC=false
-DTEST_RGB_ONLY=false
-DTEST_RGB=false
-DTEST_ALLTHETIME=false
-DPOWERLOSSDETECT=false
-DCHARGRAPH_DEBUG
@ -63,7 +64,7 @@ platform = ${env.platform}
monitor_speed = ${common.monitor_speed}
upload_speed = 115200 ; Langsam für Clone-Stabilität
upload_port = COM13
;upload_port = COM13
; Flash-Einstellungen (sicher für alle Clones)
board_build.flash_mode = dio ; Statt qio - kompatibler!
@ -100,7 +101,7 @@ monitor_speed = ${common.monitor_speed}
;upload_speed = 921600
upload_speed = 115200
;upload_port = COM13 ; Automatische Erkennung oder manuell anpassen
upload_port = COM5
;upload_port = COM5
board_build.flash_mode = dio
# CPU-Frequenz auf 160MHz erhöht

View File

@ -15,37 +15,42 @@ const byte DNS_PORT = 53;
// ════════════════════════════════════════════════════════════════
// CHARSOAP / ZEICHENSATZ
// ════════════════════════════════════════════════════════════════
void initCharsoap() {
void initCharsoap(char *_charsoap) {
// Konvertiert UTF-8 Umlaute in charsoap zu ASCII in-place
// charsoap muss bereits gefüllt sein (von strcpy_P)
char temp[COLS * ROWS * 2]; // Temporärer Buffer für Konvertierung
int writePos = 0;
int readPos = 0;
int sourceLen = strlen(DEFAULT_CHARSOAP);
int sourceLen = strlen(_charsoap);
while (readPos < sourceLen && writePos < (ROWS * COLS)) {
unsigned char c = DEFAULT_CHARSOAP[readPos];
unsigned char c = _charsoap[readPos];
// UTF-8 Umlaute erkennen und zu lowercase konvertieren
if (c == 0xC3 && readPos + 1 < sourceLen) {
unsigned char next = DEFAULT_CHARSOAP[readPos + 1];
unsigned char next = _charsoap[readPos + 1];
switch(next) {
case 0x84: charsoap[writePos++] = 'a'; break; // Ä → a
case 0x96: charsoap[writePos++] = 'o'; break; // Ö → o
case 0x9C: charsoap[writePos++] = 'u'; break; // Ü → u
case 0x84: temp[writePos++] = 'a'; break; // Ä → a
case 0x96: temp[writePos++] = 'o'; break; // Ö → o
case 0x9C: temp[writePos++] = 'u'; break; // Ü → u
default:
charsoap[writePos++] = c;
if (writePos < (ROWS * COLS)) charsoap[writePos++] = next;
temp[writePos++] = c;
if (writePos < (ROWS * COLS)) temp[writePos++] = next;
break;
}
readPos += 2;
} else {
charsoap[writePos++] = c;
temp[writePos++] = c;
readPos++;
}
}
charsoap[writePos] = '\0';
temp[writePos] = '\0';
strcpy(_charsoap, temp); // Konvertiertes Ergebnis zurück nach charsoap
DEBUG_PRINTLN("Default charsoap initialisiert (Umlaute → lowercase)");
DEBUG_PRINTLN(charsoap);
DEBUG_PRINTLN("charsoap initialisiert (Umlaute → lowercase)");
DEBUG_PRINTLN(_charsoap);
}
void loadCharsoap() {
@ -119,24 +124,20 @@ void loadCharsoap() {
}
} else {
DEBUG_PRINTF("❌ Falsche Länge: %d\n", writePos);
strcpy(charsoap, DEFAULT_CHARSOAP);
initCharsoap(); // UTF-8 zu ASCII konvertieren!
strcpy_P(charsoap, (const char*)DEFAULT_CHARSOAP);
initCharsoap(charsoap); // UTF-8 zu ASCII konvertieren!
customCharsoap = false;
DEBUG_PRINTLN("→ Verwende Default");
}
} else {
strcpy(charsoap, DEFAULT_CHARSOAP);
initCharsoap(); // UTF-8 zu ASCII konvertieren!
strcpy_P(charsoap, (const char*)DEFAULT_CHARSOAP);
initCharsoap(charsoap); // UTF-8 zu ASCII konvertieren!
DEBUG_PRINTLN("✓ Standard charsoap");
}
}
void saveCharsoap(const char* newCharsoap) {
if (strlen(newCharsoap) != (ROWS * COLS)) {
DEBUG_PRINTF("❌ Länge: %d\n", strlen(newCharsoap));
return;
}
void saveCharsoap(const char* newCharsoap)
{
char cleaned[uint8_t (ROWS * COLS)+1];
uint16_t writePos = 0;
uint16_t readPos = 0;
@ -184,8 +185,8 @@ void saveCharsoap(const char* newCharsoap) {
}
void resetCharsoap() {
strcpy(charsoap, DEFAULT_CHARSOAP);
initCharsoap(); // UTF-8 zu ASCII konvertieren!
strcpy_P(charsoap, (const char*)DEFAULT_CHARSOAP);
initCharsoap(charsoap); // UTF-8 zu ASCII konvertieren!
customCharsoap = false;
EEPROM.write(ADDR_CHARSOAP_SET, 0);
EEPROM.commit();
@ -220,8 +221,8 @@ void loadSpecialWords() {
// Erste Initialisierung: Default-Werte setzen
DEBUG_PRINTLN("Setze Standard SPECIAL_WORD...");
const char DEFAULT_SPECIAL_WORD[MAXWORDS][12] = {
"RWD",
"",
"DLZIAX",
"FACW",
""
};
@ -439,8 +440,8 @@ void loadConfig() {
DEBUG_PRINTLN("╚════════════════════════════════════════════════════╝");
// DEFAULT_CHARSOAP vorbereiten (UTF-8 → ASCII)
strcpy(charsoap, DEFAULT_CHARSOAP);
initCharsoap();
strcpy_P(charsoap, (const char*)DEFAULT_CHARSOAP);
initCharsoap(charsoap);
DEBUG_PRINTLN("→ Speichere DEFAULT_CHARSOAP ins EEPROM...");
@ -827,7 +828,7 @@ void displayTime(int hours, int minutes)
yield();
CharGraphTimeWords result;
int8_t resultval = getCharGraphWords(DEFAULT_CHARSOAP, testPattern, hours, minutes, result);
int8_t resultval = getCharGraphWords(charsoap, testPattern, hours, minutes, result);
if (resultval == 0)
{
@ -1361,6 +1362,7 @@ void handleSave() {
server.send(400, "text/plain", "Fehler");
}
}
// ════════════════════════════════════════════════════════════════
// LED TEST HANDLER
// ════════════════════════════════════════════════════════════════
@ -2395,7 +2397,7 @@ void setup()
Serial.begin(115200);
while (!Serial) { }
Serial.setDebugOutput(true);
delay(500);
delay(5000);
Serial.setDebugOutput(false);
Serial.printf("Flash Chip ID: %08X\n", ESP.getFlashChipId());
@ -2450,7 +2452,7 @@ void setup()
EEPROM.begin(EEPROM_SIZE);
// Zuerst Default charsoap initialisieren
initCharsoap();
initCharsoap(charsoap);
loadCharsoap();
loadSpecialWords();