From 520bcf7d7f1e9e3f22b90439d0c3a0ab7352d7f7 Mon Sep 17 00:00:00 2001 From: "XPS\\Micro" Date: Tue, 10 Feb 2026 19:51:57 +0100 Subject: [PATCH] CharGraphs added --- include/WordFinder.h | 134 +++++++++++++++++++++ include/charPattern.inc | 24 +++- include/defines.inc | 3 +- include/vars.inc | 4 +- lib/CharGraphTimeLogic/src/MinuteRules.cpp | 6 +- lib/CharGraphTimeLogic/src/WordMatcher.cpp | 6 +- lib/rgbPanel/rgbPanel.cpp | 40 +++--- lib/rgbPanel/rgbPanel.h | 6 +- platformio.ini | 5 +- src/main.cpp | 66 +++++----- 10 files changed, 227 insertions(+), 67 deletions(-) create mode 100644 include/WordFinder.h diff --git a/include/WordFinder.h b/include/WordFinder.h new file mode 100644 index 0000000..fb0be4b --- /dev/null +++ b/include/WordFinder.h @@ -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 + +// ============================================================================ +// 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 diff --git a/include/charPattern.inc b/include/charPattern.inc index 3b40563..19b4c20 100644 --- a/include/charPattern.inc +++ b/include/charPattern.inc @@ -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" diff --git a/include/defines.inc b/include/defines.inc index 0d78c4b..cef3d4c 100644 --- a/include/defines.inc +++ b/include/defines.inc @@ -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 \ No newline at end of file diff --git a/include/vars.inc b/include/vars.inc index afde676..8d9c0f1 100644 --- a/include/vars.inc +++ b/include/vars.inc @@ -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; diff --git a/lib/CharGraphTimeLogic/src/MinuteRules.cpp b/lib/CharGraphTimeLogic/src/MinuteRules.cpp index c44cc01..792caac 100644 --- a/lib/CharGraphTimeLogic/src/MinuteRules.cpp +++ b/lib/CharGraphTimeLogic/src/MinuteRules.cpp @@ -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; } diff --git a/lib/CharGraphTimeLogic/src/WordMatcher.cpp b/lib/CharGraphTimeLogic/src/WordMatcher.cpp index d7c5979..4cadd5d 100644 --- a/lib/CharGraphTimeLogic/src/WordMatcher.cpp +++ b/lib/CharGraphTimeLogic/src/WordMatcher.cpp @@ -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; } diff --git a/lib/rgbPanel/rgbPanel.cpp b/lib/rgbPanel/rgbPanel.cpp index 84e133b..0129264 100644 --- a/lib/rgbPanel/rgbPanel.cpp +++ b/lib/rgbPanel/rgbPanel.cpp @@ -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; - currentMinute = 0; + 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); diff --git a/lib/rgbPanel/rgbPanel.h b/lib/rgbPanel/rgbPanel.h index 5497554..e81f1a2 100644 --- a/lib/rgbPanel/rgbPanel.h +++ b/lib/rgbPanel/rgbPanel.h @@ -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); diff --git a/platformio.ini b/platformio.ini index 6fb0d3d..dddbad9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -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 diff --git a/src/main.cpp b/src/main.cpp index cb925f0..638642f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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();