# VersaGUI Windows-Tray-App zur Konfiguration und Steuerung des VersaPad v2. Geschrieben in **C# / .NET 7 / WinForms**. ## Voraussetzungen - Windows 10/11 - [.NET 7 SDK](https://dotnet.microsoft.com/download/dotnet/7.0) (zum Bauen) - VersaMCU-Firmware auf dem Board geflasht - Board per USB verbunden (erscheint als CDC COM-Port, kein Treiber nötig) ## Starten ```bash dotnet run ``` Oder als Release-Build: ```bash dotnet publish -c Release -r win-x64 --self-contained ``` Die App erscheint als Icon in der Windows-Taskleiste (System Tray). Kein Hauptfenster. ## Bedienung 1. **Board verbinden** – App erkennt das Board automatisch per VID/PID (`0x239A / 0x0042`) via WMI 2. **Rechtsklick** auf das Tray-Icon → **Konfiguration...** 3. Taste/Encoder anklicken → Aktion auswählen: - **HID Tastatur**: Großen Button klicken, dann gewünschte Taste drücken (Strg+C, F5, …) - **HID Consumer**: Dropdown – Play/Pause, Lautstärke, etc. - **Host Command**: Numerische Command-ID (zukünftig: URL/Programm) - **LED-Farbe**: Farbpicker für die Idle-LED des Buttons 4. **Auf Board speichern** – überträgt Config in den NVM des Boards 5. Beim nächsten Verbinden wird die Config automatisch vom Board geladen ## Projekt-Struktur ``` VersaGUI/ ├── VersaGUI.csproj – .NET 7 WinForms Projekt ├── Program.cs – Einstiegspunkt, startet TrayApp ├── TrayApp.cs – ApplicationContext: Tray-Icon, Menü, Paket-Routing ├── ConfigForm.cs – Konfigurations-Fenster (4×5 Grid + Encoder-Panel) ├── ActionDialog.cs – Dialog: Taste erfassen / Consumer auswählen / Host-Command ├── DeviceConfig.cs – C#-Spiegel von SDeviceConfig (163B Serialisierung, CRC16) ├── Protocol.cs – Protokoll-Konstanten (Commands + Events) └── SerialManager.cs – COM-Port-Erkennung, Read-Loop, Config senden/empfangen ``` ## Architektur ``` TrayApp ├── SerialManager – Hintergrund-Thread: COM-Port-Verbindung + Leseloop │ ├── Connected-Event → Config vom Board anfordern (CMD_CONFIG_READ) │ └── PacketReceived → TrayApp.OnPacket() ├── DeviceConfig – In-Memory-Modell der Board-Config └── ConfigForm (optional) – Öffnet sich auf Benutzeranfrage └── ActionDialog – Modaler Dialog pro Taste/Encoder ``` ### SerialManager - Erkennt das Board per **WMI** (`Win32_PnPEntity`, VID/PID-Filter) → COM-Port-Name - Lese-Loop im Hintergrund-Thread: sammelt 8-Byte-Pakete, feuert `PacketReceived`-Event auf dem UI-Thread - **Reconnect-Timer**: versucht alle 3 Sekunden neu zu verbinden; nach Disconnect 5 Sekunden Backoff - Wichtig: `DtrEnable = true` muss **vor** `Open()` gesetzt werden – der SAMD21 prüft die DTR-Leitung in `if (!SerialUSB)` und verwirft sonst alle ausgehenden Pakete ### DeviceConfig / Serialisierung `DeviceConfig.ToBytes()` erzeugt den exakt 163-Byte-Puffer der `SDeviceConfig`-Struct in der Firmware entspricht (little-endian, packed). `CRC16-CCITT` (Poly 0x1021, Init 0xFFFF) über Bytes 7–162 – identisch zur Firmware-Implementierung. Config-Übertragung Board→PC (Lesen): ``` PC sendet: CMD_CONFIG_READ (0x13) Board sendet: EVT_CONFIG_BEGIN (Chunk-Anzahl) EVT_CONFIG_DATA × 28 (je 6 Nutzbytes) EVT_CONFIG_END ``` Config-Übertragung PC→Board (Schreiben): ``` PC sendet: CMD_CONFIG_BEGIN (Chunk-Anzahl) CMD_CONFIG_DATA × 28 CMD_CONFIG_COMMIT Board sendet: EVT_CONFIG_ACK oder EVT_CONFIG_NACK ``` ### Bekannte .NET-Eigenheiten | Problem | Lösung | |---|---| | `.NET 7 SerialPort.ReadByte()` wirft `IOException` statt `TimeoutException` auf Timeout | `catch (IOException)` → nur als Fehler behandeln wenn Port nicht mehr offen ist | | `DtrEnable = false` (Standard) → Board sendet nichts | `DtrEnable = true` im SerialPort-Konstruktor setzen, **vor** `Open()` | | DTR-Zustandswechsel nach `Open()` löst CDC-Disconnect auf dem Board aus | DTR vor `Open()` setzen → kein Zustandswechsel |