VersaMCU/doc/03_action_engine.md

101 lines
3.8 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.

# Aktions-Engine
**Dateien:** `config/action.h`, `CButton.h/.cpp`, `CMainController.cpp` (`processEvents`, `execute_action_down`, `execute_action_up`)
## SAction-Struct
```cpp
struct __attribute__((packed)) SAction {
ActionType type; // 1 Byte
uint16_t data; // 2 Bytes (Keycode, Consumer-Code, Command-ID oder Slot-Index)
};
// Gesamt: 3 Bytes (packed! ohne packed wären es 4 durch Alignment)
```
`packed` ist zwingend damit `sizeof(SDeviceConfig) == 740` mit der C#-Serialisierung in VersaGUI übereinstimmt.
## ActionType
| Typ | Wert | Bedeutung | data-Inhalt |
|---|---|---|---|
| `NONE` | 0 | Keine Aktion | — |
| `HID_KEY` | 1 | Tastendruck via USB HID Keyboard | Low-Byte = HID Keycode, High-Byte = Modifier |
| `HID_CONSUMER` | 2 | Consumer Control (Volume, Media, …) | Consumer Usage ID |
| `HOST_COMMAND` | 3 | Event an VersaGUI senden, App führt aus | Command-ID (frei definiert) |
| `MACRO` | 4 | Makro-Sequenz aus NVM-Tabelle | Slot-Index 031 |
| `PROFILE_SWITCH` | 5 | Aktives Profil wechseln | 02 = Ziel-Profil, 0xFF = nächstes Profil (Zyklus 0→1→2→0) |
## execute_action_down() — Taste gedrückt (Hold-Start)
| ActionType | Verhalten |
|---|---|
| `HID_KEY` | `usb_hid_send_key(keycode, modifier)` — Taste bleibt gedrückt bis `execute_action_up()` |
| `HID_CONSUMER` | `usb_hid_send_consumer(usage_id)` — bleibt aktiv bis `execute_action_up()` |
| `HOST_COMMAND` | `usb_serial_send(USB_EVT_KEY_DOWN, key_id)` |
| `MACRO` | Volle Sequenz ausführen (Steps[slot], keycode==0 = Ende, delay 10+20 ms) |
| `PROFILE_SWITCH` | NVM laden → `active_profile` setzen → CRC neu berechnen → NVM speichern → `init_buttons()` |
| `NONE` | nop |
## execute_action_up() — Taste losgelassen (Hold-Ende)
| ActionType | Verhalten |
|---|---|
| `HID_KEY` | `usb_hid_release_key()` |
| `HID_CONSUMER` | `usb_hid_release_consumer()` |
| `HOST_COMMAND` | — (optional: könnte `USB_EVT_KEY_UP` senden) |
| `MACRO`/`PROFILE_SWITCH`/`NONE` | nop |
## PROFILE_SWITCH — Ablauf
```cpp
SDeviceConfig cfg;
nvm_config_load(cfg); // komplette Config aus NVM
uint8_t target = (uint8_t)action.data;
if (target == 0xFF)
target = (cfg.active_profile + 1) % 3; // Zyklus
cfg.active_profile = target;
cfg.crc = nvm_config_crc(cfg); // CRC MUSS nach Änderung neu berechnet werden!
if (nvm_config_save(cfg)) // bool: false = NVM-Timeout
init_buttons();
```
> **Wichtig:** `active_profile` liegt im CRC-geschützten Bereich (ab Byte 7). Wird die CRC nicht aktualisiert, findet das nächste `nvm_config_load()` einen CRC-Fehler und lädt die Defaults (alle Aktionen NONE, alle LEDs Regenbogen).
## Hold-Modell (HID-Keys und Consumer Controls)
Normale Tasten- und Media-Aktionen folgen dem **Hold-Modell**:
```
KEY_DOWN-Event vom Board → execute_action_down() → HID Key-Down senden
[Taste bleibt physisch gedrückt...]
KEY_UP-Event vom Board → execute_action_up() → HID Key-Up senden
```
Das OS erkennt die gedrückte Taste und startet sein eigenes Key-Repeat nach ~500 ms — wie auf einer normalen Tastatur.
## Tap-Modell (Encoder CW/CCW)
Encoder-Bewegungen sind diskret (kein Halten möglich) und verwenden das **Tap-Modell**:
```
ENC_CW/ENC_CCW-Event → execute_action_down() + delay(10) + execute_action_up()
```
(Atomare Sequenz für jeden Encoder-Schritt.)
## Work-Loop-Reihenfolge
```cpp
void work() {
matrix_scan(); // → Events in Queue (KEY_DOWN, KEY_UP, ENC_CW, ENC_CCW)
poll_vendor(); // Serial-Pakete verarbeiten (PC↔Board Kommandos)
processEvents(); // → execute_action_down/up() aufrufen
updateLEDs(); // Dirty-LEDs aktualisieren
}
```
**processEvents() verarbeitet:**
- `KEY_DOWN``execute_action_down()`
- `KEY_UP``execute_action_up()`
- `ENC_CW` / `ENC_CCW``execute_action_down()` + `delay(10)` + `execute_action_up()`