Files
Omino/README.md
T
2026-06-05 01:03:27 +02:00

259 lines
6.7 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 i wyświetla informacje na żądanie. 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 7 nastrojów z różnymi kształtami oczu:
| Nastrój | Wygląd | Opis |
|---------|--------|------|
| `normal` | pełne oczy, dryfujące źrenice | domyślny |
| `happy` | łukowe oczy `^_^` | górna połowa oka zasłonięta |
| `sleepy` | opuszczona powieka `zZz` | bąbelki ZZZ, wolne mruganie |
| `surprised` | wielkie oczy `o_O` | uniesione brwi |
| `angry` | zmrużone `>_<` | skośne brwi do środka |
| `sad` | smutne `T_T` | odwrócone brwi, łzy |
| `excited` | gwiazdy `*_*` | wzór × zamiast źrenic |
Dodatkowe animacje:
- **Mruganie** — co 2,56 s (szybsze w trybie `sleepy`)
- **Ruch źrenic** — płynny drift do losowej pozycji co 1,54 s (tylko `normal`)
- **Auto-uśpienie** — po 5 minutach bez gestu przechodzi w `sleepy`
### 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
- Nastrój po wysłaniu webhooka konfigurowalny niezależnie
### Akcje
Akcje wyświetlają informacje na OLED przez 8 sekund, potem wracają do twarzy:
| Akcja | Co pokazuje |
|-------|------------|
| **Data i godzina** | `HH:MM:SS` (duża czcionka), `DD.MM.RRRR`, dzień tygodnia |
| **Status WiFi** | SSID, adres IP, RSSI w dBm, słupki siły sygnału |
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/webhooku
- **Akcja** — co wyświetlić na OLED
- **ON** — czy gest jest aktywny
### JSON API
```bash
# Odczyt konfiguracji
GET http://<IP>/api/config
# Zapis konfiguracji
POST http://<IP>/api/config
Content-Type: application/json
{
"wave": {
"url": "http://homeassistant.local:8123/api/webhook/my_hook",
"mood": 6,
"action": 0,
"enabled": true
},
"up": {
"url": "",
"mood": 0,
"action": 1,
"enabled": true
}
}
```
#### Wartości `mood`
| Wartość | Nastrój |
|---------|---------|
| 0 | bez zmiany |
| 1 | happy |
| 2 | sleepy |
| 3 | surprised |
| 4 | angry |
| 5 | sad |
| 6 | excited |
#### Wartości `action`
| Wartość | Akcja |
|---------|-------|
| 0 | brak |
| 1 | data i godzina |
| 2 | status WiFi |
### Konfiguracja WiFi
Dane sieci są na razie hardcoded w `src/main.cpp`:
```cpp
const char *WIFI_SSID = "twoja_siec";
const char *WIFI_PASS = "twoje_haslo";
```
---
## Instalacja i build
### Wymagania
- [PlatformIO](https://platformio.org/) (CLI lub VS Code extension)
### Build i upload
```bash
cd esp32-c6
pio run --target upload
pio device monitor
```
### Zależności (pobierane automatycznie)
```ini
olikraus/U8g2
acrandal/RevEng PAJ7620
bblanchon/ArduinoJson@^7.2.1
mathieucarbou/ESPAsyncWebServer@^3.3.12
```
### platformio.ini
```ini
[env:esp32-c6]
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
```
---
## Architektura kodu
Cały projekt mieści się w jednym pliku `src/main.cpp` (~650 linii).
```
setup()
Wire.begin(22, 23) ← I2C0 HP dla obu urządzeń
u8g2.begin() ← SSD1306 HW I2C
sensor.begin(&Wire) ← PAJ7620 HW I2C
connectWiFi()
configTzTime(...) ← NTP sync
setupHttpServer() ← AsyncWebServer port 80
loop() ← ~200 fps bez rysowania
sensor.readGesture() ← polling co 500 ms
handleGesture()
executeAction() ← overlay na OLED
fireWebhook() ← FreeRTOS task (async)
setBuddyMood()
updateBuddyAnim() ← tick co 50 ms (stan)
showBuddyScreen() ← rysowanie co 50 ms (~20 fps)
showDateTimeScreen() ← overlay jeśli aktywny
showWiFiStatusScreen() ← overlay jeśli aktywny
```
### 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 |
| NVS klucze max 15 znaków | Skrócone prefiksy gestów: `u/d/l/r/f/b/cw/ccw/w` |
| `Preferences.begin("ns", true)` crash gdy namespace nie istnieje | Otwierać zawsze z `false` |
---
## 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)