Files
Omino/README.md
T

319 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ESP32-C6 Desk Buddy
Animowany "biurkowy przyjaciel" na Seeed XIAO ESP32-C6 z czujnikiem gestów PAJ7620 i wyświetlaczem OLED 128×64. Reaguje na gesty emocjami, wykonuje webhooki HTTP, wyświetla informacje na żądanie i symuluje potrzeby w stylu Tamagotchi. Konfigurowany przez przeglądarkę.
---
## Sprzęt
| Komponent | Model | Adres I2C |
|-----------|-------|-----------|
| Mikrokontroler | Seeed XIAO ESP32-C6 | — |
| Wyświetlacz | SSD1306 OLED 128×64 | 0x3C |
| Czujnik gestów | CJMCU-7620 (PAJ7620U2) | 0x73 |
### Podłączenie
Oba urządzenia dzielą **jedną magistralę I2C** (hardware, HP I2C bus 0):
```
SSD1306 / PAJ7620 XIAO ESP32-C6
──────────────────────────────────────
VCC → 3.3V
GND → GND
SDA → D4 (GPIO22)
SCL → D5 (GPIO23)
```
> **Ważne:** ESP32-C6 ma dwa kontrolery I2C:
> - **I2C0 (HP, `Wire`)** — dowolne piny GPIO ✓
> - **I2C1 (LP, `Wire1`)** — zablokowany sprzętowo na GPIO6/GPIO7, niedostępne na XIAO ✗
>
> Nie używaj `Wire1` ani SW I2C (bit-bang blokuje WiFi na single-core CPU ~40 ms/frame).
---
## Funkcje
### Animacja oczu (Buddy)
Ciągła animacja na OLED ~20 fps. Buddy ma 12 nastrojów z różnymi kształtami oczu i ust:
| Nastrój | Wygląd | Opis |
|---------|--------|------|
| `normal` | pełne oczy, dryfujące źrenice | domyślny |
| `happy` | pełne oczy, uśmiech | wesołe policzki |
| `sleepy` | poziome kreski `zZz` | bąbelki ZZZ, wolne mruganie |
| `surprised` | wielkie oczy `o_O` | szeroko otwarte |
| `angry` | klinowe oczy `>_<` | usta w dół |
| `sad` | dolna połowa oka `T_T` | łzy, usta w górę |
| `excited` | gwiazdy `*_*` | wzór × zamiast źrenic |
| `wink L` | ;) | prawe oko otwarte, lewe zamknięte |
| `wink R` | (; | lewe oko otwarte, prawe zamknięte |
| `hungry` | zmniejszone oczy, usta otwarte | aktywowany przez głód |
| `playful` | iskrzące oczy | aktywowany przez nudę |
| `dirty` | rozognione źrenice | aktywowany przez potrzebę mycia |
Dodatkowe animacje:
- **Mruganie** — co 2,56 s (szybsze w trybie `sleepy`)
- **Ruch źrenic** — saccady do losowej pozycji co 1,54,5 s we wszystkich nastrojach; podczas snu wolny drift co 49 s
- **Micro-tremor** — drobne drżenie podczas fiksacji (tylko `normal`/`happy`)
- **Auto-uśpienie** — po 5 minutach bez gestu przechodzi w `sleepy`
- **Nocne wygaszenie** — między 00:00 a 05:00, po 5 minutach braku aktywności wyświetlacz gaśnie; wraca przy geście
### Tamagotchi — potrzeby
Buddy ma trzy potrzeby narastające z czasem:
| Potrzeba | Kierunek | Tempo | Próg alarmu | Nastrój |
|----------|----------|-------|-------------|---------|
| Głód | 0=syt → 100=głodny | +1/2 min | ≥ 80 | `hungry` |
| Szczęście | 100=wesoły → 0=znudzony | 1/3 min | ≤ 20 | `playful` |
| Higiena | 100=czysty → 0=brudny | 1/4 min | ≤ 20 | `dirty` |
Gdy potrzeba przekroczy próg, Buddy automatycznie zmienia nastrój. W lewym dolnym rogu ekranu pojawiają się litery: **G** (głód), **Z** (zabawa), **M** (mycie).
### Gesty (PAJ7620)
Czujnik rozpoznaje 9 gestów ręką:
| Gest | Domyślna reakcja |
|------|-----------------|
| `up` | happy |
| `down` | sad |
| `left` | surprised |
| `right` | surprised |
| `forward` | sleepy (trwały) |
| `backward` | angry |
| `clockwise` | excited |
| `anticlockwise` | normal |
| `wave` | excited |
Każdemu gestowi można przypisać własny nastrój, webhook i akcję przez panel konfiguracyjny.
### Webhooki
Każdy gest może wywoływać webhook HTTP POST na skonfigurowany URL:
```
POST http://twoj-serwer/endpoint
Content-Type: application/json
{"gesture": "wave"}
```
- Wywołanie asynchroniczne (FreeRTOS task) — nie blokuje animacji
- Timeout 3 sekundy
### Akcje
Akcje wyświetlają informacje lub reagują na potrzeby Buddy'ego:
| Akcja | Czas | Co robi |
|-------|------|---------|
| **Data i godzina** | 8 s | `HH:MM:SS`, `DD.MM.RRRR`, dzień tygodnia |
| **Status WiFi** | 8 s | SSID, IP, RSSI, słupki siły sygnału |
| **Nakarm** | 3 s | głód 30, nastrój `happy` |
| **Pobaw się** | 3 s | szczęście +25, nastrój `excited` |
| **Umyj** | 3 s | higiena +40, nastrój `surprised` |
| **Status tamagotchi** | 8 s | paski postępu: głód / szczęście / higiena |
Czas synchronizowany przez NTP (`pool.ntp.org`) — strefa CET/CEST (Polska).
---
## Konfiguracja
### Panel webowy
Po uruchomieniu wejdź przeglądarką na adres IP wypisany w Serial (lub na OLED przy starcie):
```
http://<IP>/
```
Dla każdego gestu można ustawić:
- **URL webhoka** — endpoint HTTP POST (pusty = wyłączony)
- **Nastrój** — jaki nastrój ustawić po geście (0 = bez zmiany)
- **Akcja** — co zrobić/wyświetlić na OLED
- **ON** — czy gest jest aktywny
### JSON API
```bash
# Odczyt konfiguracji gestów
GET http://<IP>/api/config
# Zapis konfiguracji gestów
POST http://<IP>/api/config
Content-Type: application/json
{
"wave": {
"url": "http://homeassistant.local:8123/api/webhook/my_hook",
"mood": 6,
"action": 4,
"enabled": true
},
"up": {
"url": "",
"mood": 0,
"action": 3,
"enabled": true
}
}
```
#### Wartości `mood`
| Wartość | Nastrój |
|---------|---------|
| 0 | bez zmiany (domyślna reakcja gestu) |
| 1 | happy |
| 2 | sleepy |
| 3 | surprised |
| 4 | angry |
| 5 | sad |
| 6 | excited |
| 7 | wink L |
| 8 | wink R |
| 9 | hungry |
| 10 | playful |
| 11 | dirty |
#### Wartości `action`
| Wartość | Akcja |
|---------|-------|
| 0 | brak |
| 1 | data i godzina |
| 2 | status WiFi |
| 3 | nakarm (głód 30) |
| 4 | pobaw się (szczęście +25) |
| 5 | umyj (higiena +40) |
| 6 | status tamagotchi |
### Konfiguracja WiFi
Dane sieci przechowywane są w pliku `data/wifi.json` na LittleFS. Plik **nie jest commitowany** i **nigdy nie jest serwowany przez HTTP** — hasło WiFi pozostaje tylko na urządzeniu.
```json
{
"ssid": "twoja_siec",
"password": "twoje_haslo"
}
```
### Konfiguracja gestów
Plik `data/config.json` zawiera wyłącznie konfigurację gestów (bez danych WiFi). Jest dostępny przez `GET /config.json` i pobieralny komendą `task config-download`.
```json
{
"up": { "url": "", "mood": 0, "action": 3, "enabled": true },
"wave": { "url": "http://ha.local/webhook/x", "mood": 6, "action": 0, "enabled": true }
}
```
---
## Instalacja i build
### Wymagania
- [PlatformIO](https://platformio.org/) (CLI lub VS Code extension)
- [Task](https://taskfile.dev/) (`brew install go-task`)
### Pierwsze uruchomienie
```bash
# Konfiguracja WiFi
cp data/wifi.json.example data/wifi.json
nano data/wifi.json # uzupełnij ssid i password
# Konfiguracja gestów (opcjonalnie — domyślne wartości działają od razu)
cp data/config.json.example data/config.json
task flash-monitor # uploadfs + upload + monitor
```
### Typowe komendy
| Komenda | Opis |
|---------|------|
| `task build` | Tylko kompilacja |
| `task flash` | Wgraj filesystem + firmware |
| `task upload` | Wgraj tylko firmware |
| `task uploadfs` | Wgraj `data/wifi.json` + `data/config.json` |
| `task monitor` | Monitor portu szeregowego |
| `task flash-monitor` | Pełne wgranie + monitor |
| `task wifi` | Edytuj WiFi (`data/wifi.json`) + wgraj FS |
| `task gestures` | Edytuj gesty (`data/config.json`) + wgraj FS |
| `task config-download` | Pobierz konfigurację gestów z urządzenia |
| `task erase-flash` | Skasuj flash i wgraj od nowa |
| `task` | Lista wszystkich zadań |
### Zmiana WiFi bez rekompilacji
```bash
task wifi # otwiera edytor + automatycznie uploadfs
```
### Zależności (pobierane automatycznie)
```ini
olikraus/U8g2
acrandal/RevEng PAJ7620
bblanchon/ArduinoJson@^7.2.1
mathieucarbou/ESPAsyncWebServer@^3.3.12
```
---
## Architektura kodu
Cały projekt mieści się w jednym pliku `src/main.cpp`.
```
setup()
loadAllConfig()
/wifi.json → WIFI_SSID/PASS (nigdy nie serwowane)
/config.json → gConfig[] (gesty)
initBuddy() + initTama()
Wire.begin(22, 23) ← I2C0 HP dla obu urządzeń
u8g2.begin() ← SSD1306 HW I2C
sensor.begin(&Wire) ← PAJ7620 HW I2C
connectWiFi() → NTP sync
setupHttpServer() ← AsyncWebServer port 80
loop() ← polling
sensor.readGesture() ← co 500 ms
handleGesture()
executeAction() ← tama efekt + overlay OLED
setBuddyMood() ← skonfigurowany lub domyślny
fireWebhook() ← FreeRTOS task (async)
updateTama() ← co 10 s (potrzeby + mood override)
updateBuddyAnim() ← co 50 ms (mruganie, saccady, ZZZ)
showBuddyScreen() ← co 50 ms (~20 fps)
```
### Kluczowe decyzje techniczne
| Problem | Rozwiązanie |
|---------|-------------|
| SW I2C blokuje WiFi 40ms/frame na single-core ESP32-C6 | HW I2C (Wire) dla obu urządzeń na wspólnej magistrali |
| `WebServer.h` nie działa na ESP32-C6 z IDF 5.x | `ESPAsyncWebServer` (callback-based, brak `handleClient()`) |
| `Wire1` (LP I2C) zablokowany na GPIO6/7 (niedostępne na XIAO) | Jeden `Wire` (HP I2C) dla SSD1306 + PAJ7620 |
| `setContrast()` ma zbyt wąski zakres wizualny na SSD1306 | `setPowerSave(1/0)` — komenda 0xAE/0xAF wyłącza panel |
| Hasło WiFi dostępne przez HTTP | Oddzielny `/wifi.json` — nigdy nie serwowany; `/config.json` zawiera tylko gesty |
---
## Dodawanie nowych akcji
1. Dodaj wartość do `enum Action` w `main.cpp`
2. Zaktualizuj `NUM_ACTIONS` i `ACTION_LABELS[]`
3. Napisz funkcję `showXxxScreen()`
4. Dodaj `case ACTION_XXX:` w `showBuddyScreen()` (sekcja overlay)
5. Opcjonalnie: dodaj efekt tama w `executeAction()` (np. zmiana potrzeby)