VersaGUI

Windows-Tray-App zur Konfiguration und Steuerung des VersaPad v2. Geschrieben in C# / .NET 7 / WinForms.

Voraussetzungen

  • Windows 10/11
  • .NET 7 SDK (zum Bauen)
  • VersaMCU-Firmware auf dem Board geflasht
  • Board per USB verbunden (erscheint als CDC COM-Port, kein Treiber nötig)

Starten

dotnet run

Oder als Release-Build:

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

Funktionsumfang (Anforderungskatalog)

1 Verbindung & Geräteerkennung

# Anforderung Status
1.1 App läuft als Windows Tray-Anwendung ohne sichtbares Hauptfenster
1.2 Automatische Board-Erkennung per WMI / VID+PID (0x239A / 0x0042)
1.3 Automatischer Reconnect alle 3 s; 5 s Backoff nach Verbindungsverlust
1.4 Verbindungsstatus im Tray-Icon (Symbol + Tooltip) sichtbar
1.5 Beim Verbinden wird die gespeicherte Config automatisch vom Board gelesen
1.6 Beim Verbinden wird die Makro-Tabelle automatisch vom Board gelesen

2 Tastenbelegung HID Tastatur

# Anforderung Status
2.1 Taste durch Drücken erfassen (kein manuelles HID-ID-Eingeben)
2.2 Modifier-Kombination: Strg / Shift / Alt / Win einzeln oder kombiniert
2.3 Pfeiltasten, Enter, Escape, F1F12, Numpad erfassbar
2.4 Layout-unabhängige Erfassung via PS/2-Scan-Code → HID-Usage; Ö/Ä/Ü auf QWERTZ korrekt
2.5 Taste-Name wird laut aktivem Windows-Layout angezeigt (GetKeyNameText)
2.6 Board führt Tastendruck als USB-HID-Tastatureingabe aus (funktioniert ohne laufende App)
2.7 Hold-Semantik: Taste bleibt gedrückt solange physisch gehalten (OS initiiert Repeat nach ~500ms)

3 Tastenbelegung HID Consumer / Medientasten

# Anforderung Status
3.1 Auswahl per Dropdown mit Klartext-Namen
3.2 Unterstützte Aktionen: Play/Pause, Nächster/Vorheriger Titel, Stop, Lauter/Leiser, Mute, Taschenrechner, Browser Zurück/Vor
3.3 Hold-Semantik: Media-Control bleibt aktiv solange Taste gehalten wird (z.B. Lautstärke-Wiederholung)

4 Tastenbelegung Makros

# Anforderung Status
4.1 Bis zu 4 Schritte pro Makro (je 1 Taste + Modifier)
4.2 Jeder Schritt per Taste drücken erfassen, inkl. Sondertasten
4.3 Leere Schritte werden übersprungen (kürzere Makros möglich)
4.4 32 Makro-Slots je ein Slot pro MX-Button (019) und Encoder-Aktion (2031)
4.5 Makro-Tabelle wird separat vom Board gelesen und geschrieben (NVM Row 1)
4.6 Board führt Makro-Schritte mit 10 ms Key-Down + 20 ms Pause ohne laufende App aus

5 Encoder-Belegung

# Anforderung Status
5.1 4 Encoder, je 3 Aktionen: SW (Drücken), CW (Rechts), CCW (Links)
5.2 Gleiche Aktionstypen wie Tasten (HID Key, Consumer, Makro, Host Command)
5.3 Encoder-SW: Hold-Semantik wie normale Tasten (Taste bleibt gedrückt)
5.4 Encoder-CW/CCW: Tap-Modell (diskrete Ereignisse, atomare down+delay+up Sequenzen, kein Hold möglich)

6 LED-Konfiguration

# Anforderung Status
6.1 Basis-LED-Farbe pro MX-Button via Farbpicker (RGB)
6.2 Animationsmodus pro Button wählbar: Statisch, Blinken, Pulsieren, Regenbogen
6.3 Animations-Tempo: Schnell (0,5 s) / Mittel (1 s) / Langsam (2 s) / Sehr langsam (4 s)
6.4 Regenbogen-Modus: Board berechnet Hue-Sweep lokal, gleichmäßige Phasenverteilung
6.5 LED-Config und Aktionstyp im gleichen Dialog bearbeitbar

7 Konfiguration speichern & laden

# Anforderung Status
7.1 Config und Makros werden gleichzeitig per „Auf Board speichern" übertragen
7.2 Board validiert Config mit CRC16-CCITT + Magic + Version; NACK bei Fehler
7.3 Config bleibt nach Stromverlust im NVM erhalten (kein Flash-Verschleiß bei nur Lesen)
7.4 JSON-Export der gesamten Config (Aktionen + LED) in Datei
7.5 JSON-Import mit Versionscheck; ungültige Dateien werden abgelehnt

8 Verbindungsprotokoll

# Anforderung Status
8.1 8-Byte-Festlängen-Pakete über CDC Serial (kein Treiber nötig)
8.2 Ping/Pong zur manuellen Verbindungsdiagnose im Konfigurations-Fenster
8.3 Config-Transfer: BEGIN → n×DATA(6 Byte) → COMMIT → ACK/NACK
8.4 Makro-Transfer: gleiche Struktur, 43 Chunks à 6 Byte (256 Byte Tabelle)
8.5 Debug-Logging aller RX-Pakete in %TEMP%\versapad_rx.txt

9 Nicht implementiert / Roadmap

# Anforderung Status
9.1 Host Command: URL/Programm öffnen wenn Board-Taste gedrückt 🔲 TODO
9.2 Eigenes Tray-Icon (aktuell: Windows-Standard-Icon) 🔲 TODO
9.3 Fader/Potentiometer-Unterstützung (3× ADC-Achsen auf Board vorhanden) 🔲 TODO
9.4 Profile: mehrere Tastenbelegungen speichern und wechseln 🔲 TODO

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 / Makro / LED-Animation
├── DeviceConfig.cs       C#-Spiegel von SDeviceConfig (223 B, Version 2) + MacroTable (256 B)
├── ConfigJson.cs         JSON-Export/Import der DeviceConfig
├── Protocol.cs           Protokoll-Konstanten (Commands + Events)
└── SerialManager.cs      COM-Port-Erkennung, Read-Loop, Config + Makros 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 223-Byte-Puffer der SDeviceConfig-Struct (Version 2, little-endian, packed). CRC16-CCITT (Poly 0x1021, Init 0xFFFF) über Bytes 7222 identisch zur Firmware-Implementierung.

MacroTable.ToBytes() erzeugt 256 Byte (32 Slots × 4 Steps × 2 Byte).

Config-Übertragung Board→PC (Lesen):

PC sendet:    CMD_CONFIG_READ  (0x13)
Board sendet: EVT_CONFIG_BEGIN (Chunk-Anzahl = 38)
              EVT_CONFIG_DATA  × 38 (je 6 Nutzbytes)
              EVT_CONFIG_END

Config-Übertragung PC→Board (Schreiben):

PC sendet:    CMD_CONFIG_BEGIN (0x10, Chunk-Anzahl)
              CMD_CONFIG_DATA  × 38
              CMD_CONFIG_COMMIT (0x12)
Board sendet: EVT_CONFIG_ACK (0x90) oder EVT_CONFIG_NACK (0x91)

Makro-Übertragung (analog, separate Kommandos 0x200x23):

PC sendet:    CMD_MACRO_BEGIN  (0x20, Chunk-Anzahl = 43)
              CMD_MACRO_DATA   × 43
              CMD_MACRO_COMMIT (0x22)
Board sendet: EVT_MACRO_ACK   (0x95)

Kommunikationsprotokoll

Das vollständige Protokoll (alle Command- und Event-IDs, NVM-Layout, Paketformat) ist in VersaMCU/README.md dokumentiert.

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
Description
No description provided
Readme 143 KiB
Languages
C# 100%