Files
Omino/memory/MEMORY.md
T

88 lines
4.2 KiB
Markdown

# ESP32-C6 Desk Buddy — Project Memory
## Hardware
- Board: Seeed XIAO ESP32-C6
- Display: SSD1306 128x64 OLED
- Gesture sensor: CJMCU-7620 (PAJ7620U2, I2C addr 0x73)
## I2C — CRITICAL
- ESP32-C6 has two I2C buses:
- I2C0 (HP, `Wire`): configurable to any GPIO — USE THIS
- I2C1 (LP, `Wire1`): hardware-locked to SDA=GPIO6, SCL=GPIO7 — NOT usable on XIAO (pins not exposed)
- **Both SSD1306 and PAJ7620 share one bus**: `Wire.begin(22, 23)` — SDA=GPIO22(D4), SCL=GPIO23(D5)
- SSD1306=0x3C, PAJ7620=0x73 — different addresses, coexist fine
- SW I2C (U8g2 bit-bang) blocks CPU ~40ms/frame — kills WiFi on single-core ESP32-C6. Always use HW I2C.
- U8g2 HW I2C constructor: `U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, 23, 22)`
## PAJ7620 API
- Library: `acrandal/RevEng PAJ7620`
- Gesture type: `Gesture` (NOT `Gesture_t`)
- begin() takes pointer: `sensor.begin(&Wire)` (NOT reference)
- `Wire1` pre-defined in framework — do not redeclare `TwoWire Wire1(1)`
- Gesture index: `(int)g - 1` maps GES_UP(1)..GES_WAVE(9) to 0..8
## HTTP Server — CRITICAL
- `WebServer.h` does NOT work reliably on ESP32-C6 with IDF 5.x (pioarduino platform)
- Use `AsyncWebServer` from `mathieucarbou/ESPAsyncWebServer@^3.3.12`
- No `handleClient()` needed — callback-based, works on interrupts
- JSON POST body handler: use the 3-arg `on()` with body callback (4th param)
## NVS / Preferences
- Always open with `prefs.begin("namespace", false)``true` (read-only) fails with NOT_FOUND if namespace doesn't exist yet
- NVS key max 15 chars — use short prefixes: u/d/l/r/f/b/cw/ccw/w for gestures
## platformio.ini
```ini
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
board = seeed_xiao_esp32c6
framework = arduino
lib_deps =
olikraus/U8g2
acrandal/RevEng PAJ7620
bblanchon/ArduinoJson@^7.2.1
mathieucarbou/ESPAsyncWebServer@^3.3.12
build_flags =
-std=gnu++17
board_build.partitions = default # no Zigbee partitions needed
```
## Desk Buddy — Architecture
- Moods (0-11): NORMAL, HAPPY, SLEEPY, SURPRISED, ANGRY, SAD, EXCITED, WINK_L, WINK_R, HUNGRY, PLAYFUL, DIRTY
- Actions (0-6): NONE, DATETIME, WIFI, FEED, PLAY, CLEAN, STATUS
- Eyes: `drawFilledEllipse` + mood-specific shapes; wink = `drawCircle` UPPER arc
- Wink: WINK_L closes screen-RIGHT eye (!isLeft = buddy's left), WINK_R closes screen-LEFT
- Blink: state machine OPEN→CLOSING→CLOSED→OPENING, 4px/tick
- Pupil: smooth drift toward random target every 1.5-4s (NORMAL only)
- Sleepy: triggered after 5 min idle (`lastEvent` tracking), ZZZ bubbles
- Auto-revert: `revertAt` timestamp, 0 = permanent mood
- Display refresh: 50ms (~20fps) — OK with HW I2C
## Webhook System
- Per-gesture config: URL (128 chars) + mood (0=none, 1-6) + enabled
- Fired async via FreeRTOS `xTaskCreate` — non-blocking
- POST `{"gesture":"wave"}` to configured URL, 3s timeout
- Config persisted in NVS, served/edited via HTTP at GET/POST /
- JSON API: GET /api/config, POST /api/config
## Key Files
- `src/main.cpp` — hardware + drawing + HTTP server + WiFi + FreeRTOS (~400 lines)
- `lib/BuddyDomain/` — BuddyTypes.h, BuddyLogic.h/.cpp (mood, blink, pupil — no Arduino)
- `lib/TamaLogic/` — TamaLogic.h/.cpp (hunger/happiness/hygiene ticks — no Arduino)
- `lib/GestureConfig/` — GestureConfig.h/.cpp (struct, enums, string tables)
- `platformio.ini` — [env:esp32-c6] + [env:native]
- `test/native/test_buddy/` + `test/native/test_tama/` — Unity tests (40 total)
- Reference project: `../handsensor/` — webhook/config patterns
## Testing
- `pio test -e native` or `task test` — runs 40 unit tests on Mac (no device needed)
- `pio run -e esp32-c6` — firmware build
- Domain libs have zero Arduino deps: `<algorithm>` only; RngFn injection for determinism
## Domain API
- `initBuddy(BuddyState &b, uint32_t now)` — pass millis()
- `setBuddyMood(BuddyState &b, Mood m, uint32_t now, uint32_t durationMs=0)`
- `updateBuddyAnim(BuddyState &b, uint32_t now, RngFn rng)` — RngFn = int32_t(*)(lo,hi)
- `initTama(TamaState &t, uint32_t now)`
- `updateTama(TamaState &t, const BuddyState &b, uint32_t now)` → returns Mood override
- `tamaFeed/Play/Clean(TamaState &t)` — direct state mutations