diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..149fd0f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,173 @@ +# AGENTS.md + +## Project Overview + +ESP8266 PlatformIO firmware for a German word clock (Wortuhr). An 11×11 WS2812B LED matrix (121 LEDs) displays time as illuminated German words. WiFi-controllable with AP mode for configuration and Station mode for NTP time sync. + +## Build Commands + +```bash +# Build (default environment: esp8266clone) +pio run + +# Build for specific environment +pio run -e esp8266clone +pio run -e d1_mini + +# Build and upload +pio run --target upload + +# Serial monitor (115200 baud) +pio device monitor + +# Clean build +pio run --target clean + +# Update libraries +pio lib update +``` + +### Pre-build Script + +`html_to_header.py` runs automatically before each build. It converts `html/index.html` into gzip-compressed C header `include/html.h`. Do not edit `html.h` directly — edit `html/index.html` instead. + +## Testing + +There is no unit test framework. Testing is done on-device: + +```bash +# Run black box test (upload to device, output on serial) +# Edit lib/CharGraphTimeLogic/blackboxtest/config.h to configure: +# - testpatterns[]: 110-char grid patterns +# - hourStart/hourEnd, minuteStart/minuteEnd, hops +# Then upload the blackboxtest.ino sketch + +# Enable word test mode via build flag in platformio.ini: +# -DTEST_ALLTHETIME=true +``` + +### Debug Flags (platformio.ini build_flags) + +- `DEBUG_SOURCE=true` — Enable serial debug output +- `USE_RTC=false` — Disable RTC (DS1307) functionality +- `TEST_RGB_ONLY=false` — Run only RGB LED test on boot +- `TEST_ALLTHETIME=false` — Run word-finding test for all times +- `POWERLOSSDETECT=false` — Enable power loss detection +- `CHARGRAPH_DEBUG` — Enable CharGraphTimeLogic debug output + +## Architecture + +### File Organization + +- `src/main.cpp` — All application logic (~2660 lines): setup/loop, web handlers, EEPROM, NTP, OTA, WiFi, charsoap management +- `include/common.inc` — Central include hub; imports all dependencies +- `include/defines.inc` — Hardware defines, debug macros, EEPROM address map +- `include/vars.inc` — Global variable definitions (also includes `html.h` at bottom) +- `include/extern.inc` — `extern` declarations for cross-module globals +- `include/charPattern.inc` — Default character pattern (PROGMEM), test words, MINUTE_LEDS +- `include/WordFinder.h` — Unified word search (PROGMEM + string, forward/backward) +- `lib/rgbPanel/` — LED matrix control, word finding/highlighting, fade effects +- `lib/CharGraphTimeLogic/` — Time-to-words conversion engine with validation +- `lib/PowerOnDetector/` — Boot reason analysis, power loss detection +- `lib/info/` — Serial connection status display + +### Include System + +`main.cpp` includes `common.inc`, which chains: `extern.inc` → `defines.inc` → `version.inc` → `vars.inc` → library headers. `vars.inc` includes `html.h` at bottom. `charPattern.inc` is included only by `rgbPanel.cpp`. + +### EEPROM Layout + +512 bytes total. All addresses are `#define`d in `defines.inc` with `ADDR_` prefix. Magic bytes (e.g., `0xAA`, `0xBB`) are used as "configured" flags. Always call `EEPROM.commit()` after writes. + +## Code Style Guidelines + +### Indentation and Formatting + +- **Indentation**: Mixed — mostly 4 spaces; some files use 2-space indent inside `#ifndef` guards. Use 4 spaces for new code. +- **Brace style**: Allman style (opening brace on its own line) for function definitions; K&R style (opening brace on same line) for `if`/`for`/`while`/`switch`. Both appear in the codebase — follow the style of the surrounding code. +- **Line length**: No strict limit; keep reasonable (~120 chars). +- **Section headers**: Use decorated comment blocks: + ```cpp + // ════════════════════════════════════════════════════════════════ + // SECTION NAME + // ════════════════════════════════════════════════════════════════ + ``` + +### Naming Conventions + +- **Functions**: `camelCase` — `loadCharsoap()`, `setRunningFlag()`, `findWord()`, `displayTime()` +- **Global variables**: `camelCase` — `bootCounter`, `ntpEnabled`, `lastSyncTime`, `autoBrightnessMin` +- **Constants**: `UPPER_SNAKE_CASE` — `NUM_LEDS`, `MAX_BRIGHTNESS`, `ADDR_CHARSOAP` +- **Macros/defines**: `UPPER_SNAKE_CASE` — `DEBUG_PRINTLN`, `RUNNING_FLAG_MAGIC` +- **Structs**: `PascalCase` — `ValidationResult`, `RuleContext`, `CharGraphTimeWords`, `TestStats` +- **Struct members**: `camelCase` — `wordCount`, `ledHex`, `fallbackLevel` +- **Header guards**: `_NAME_H_` for legacy files, `NAME_H` for newer CharGraphTimeLogic files + +### Types + +- Prefer Arduino/fixed-width types: `uint8_t`, `uint16_t`, `uint32_t`, `int8_t`, `int16_t` +- Use `bool` for flags +- Use `CRGB` for LED colors (FastLED) +- Use `char[]` / `char*` for strings — `String` class is used sparingly (only `otaError`) +- Use `const char*` with `PROGMEM` for flash-stored constant strings +- Use `unsigned char` when doing byte-level operations (UTF-8 handling) + +### Comments + +- Comments are a mix of German and English. German is predominant in older code (`main.cpp`, `rgbPanel`, `PowerOnDetector`). English is used in newer `CharGraphTimeLogic` library. +- Use `//` for inline comments; `/** ... */` Javadoc-style for function documentation in CharGraphTimeLogic +- Commented-out code is common (debug prints, alternative patterns) — leave existing commented code in place + +### Error Handling + +- Functions return `-1` or `false` on failure, valid values on success +- `getCharGraphWords()` returns `int8_t`: positive = success, 0 or negative = error +- Pattern validation returns `ValidationResult` struct with `bool valid` and `const char* reason` +- Debug output via macros: `DEBUG_PRINT(x)`, `DEBUG_PRINTLN(x)`, `DEBUG_PRINTF(...)` — these compile to no-ops when `DEBUG_SOURCE` is false +- Use `yield()` in long loops to prevent watchdog resets +- Always check `EEPROM.commit()` after EEPROM writes + +### PROGMEM Usage + +- Store constant strings and large data in flash with `PROGMEM` +- Read PROGMEM strings with `pgm_read_byte()`, `strcpy_P()`, `pgm_read_ptr()` +- Pattern strings are 110 characters (11 cols × 10 rows, row 11 is minute LEDs) + +### Include Patterns + +Library headers include their own dependencies: +```cpp +#include // Always first +#include // Hardware libraries +#include +#include // Project includes use angle brackets +#include +#include "Types.h" // Intra-library includes use quotes +``` + +### Web Server Handlers + +Handlers in `main.cpp` follow this pattern: +```cpp +server.on("/endpoint", []() { + // Parse server.arg("paramName") + // Process request + // EEPROM.write(...) + EEPROM.commit() for persistence + server.send(200, "application/json", "{\"status\":\"ok\"}"); +}); +``` + +### Hardware Constraints + +- ESP8266 has ~40KB usable RAM — avoid dynamic allocation, prefer stack buffers +- LED updates require interrupt protection: call `noInterrupts()` before `FastLED.show()`, `interrupts()` after +- LED index 2 is always off (photoresistor position) +- Zigzag LED mapping: even rows left-to-right, odd rows right-to-left +- Call `yield()` or `delay()` in loops to feed the watchdog and allow WiFi processing + +### Key Domain Knowledge + +- "charsoap" = the 110-character letter grid that defines which words can be displayed +- Umlauts Ä→a, Ö→o, Ü→u in charsoap (stored as lowercase: `FuNF` = FÜNF, `ZWoLF` = ZWÖLF) +- Words list format: dash-separated string `"ES-IST-HALB-UHR-..."` +- Time display advances hour at minute >= 20 (German convention: "zehn vor drei" at 2:50) diff --git a/include/charPattern.inc b/include/charPattern.inc index 19b4c20..44554a0 100644 --- a/include/charPattern.inc +++ b/include/charPattern.inc @@ -3,7 +3,8 @@ //Hier Umlaute wie Ö und Ü bereits durch o und u wegen UTF8 ersetzen!!!! -const char DEFAULT_CHARSOAP[] PROGMEM = "ESPISTGZEHNFuNFVIERTELVORNACHBALDHALBGZWEINSWCDLZIAXMTVZEHNEUNACHTELFuNFZWoLFSECHSIEBENBGDREIVIERZECRPFACWLUHR";//DLZIAX +const char DEFAULT_CHARSOAP[] PROGMEM = "ESMISTMZEHNFuNFVIERTELVORNACHBALDHALBQZWEINSBDREINVIERONAJAMHNAVINELFuNFZWoLFZEHNEUNACHTWSECHSIEBENDPAUSEBDUHR"; //NAVIN +//"ESPISTGZEHNFuNFVIERTELVORNACHBALDHALBGZWEINSWCDLZIAXMTVZEHNEUNACHTELFuNFZWoLFSECHSIEBENBGDREIVIERZECRPFACWLUHR";//DLZIAX //"ESYISTHZEHNFuNFVIERTELVORNACHBALDHALBUZWEINSDG6MSMARTINZEHNEUNACHTELFuNFZWoLFSECHSIEBENBXDREIVIERLUWRNFACWFUHR";//DG6MS //"ESYISTMZEHNFuNFVIERTELVORNACHBALDHALBDZWEINSNZUXHAUSEMZZEHNEUNACHTELFuNFZWoLFSECHSIEBENHXDREIVIERKNOTPAUSECUHR";//Böcker //"ESQISTFZEHNFuNFVIERTELVORNACHBALDHALBFYZWEITJEGARTNERVDELFuNFZWoLFZEHNEUNACHTXDREINSVIERSECHSIEBENCRWDSIMONUHR";//GARTNERSIMON diff --git a/include/vars.inc b/include/vars.inc index 8d9c0f1..6f41ea2 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] = "CharGraph\0"; -char AP_PASSWORD[AP_PASSWORD_LENGTH] = "CharGraph\0"; +char AP_SSID[AP_SSID_LENGHT] = "Wortuhr2026\0"; +char AP_PASSWORD[AP_PASSWORD_LENGTH] = "Wortuhr2026\0"; uint16_t bootCounter = 0; diff --git a/lib/rgbPanel/rgbPanel.cpp b/lib/rgbPanel/rgbPanel.cpp index 0129264..94b8514 100644 --- a/lib/rgbPanel/rgbPanel.cpp +++ b/lib/rgbPanel/rgbPanel.cpp @@ -16,8 +16,8 @@ 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][13] = {"DLZIAX\0", //max 11 Zeichen, \0 wird automatisch angefügt! - "FACW\0", +char SPECIAL_WORD[MAXWORDS][13] = {"NAJAMHNAVIN\0", //max 11 Zeichen, \0 wird automatisch angefügt! + "\0", "\0" }; //Text der ein- bzw. ausgeblendet werden soll const uint16_t SPECIAL_HOLD_MS = 5000; // Anzeigedauer des Spezialworts