6.3 KiB
CLAUDE.md — KVMote
Arquivo de contexto para o Claude Code. Leia antes de qualquer alteração no projeto.
O que é o KVMote
KVM (Keyboard/Video/Mouse) over Bluetooth. Controla um PC remoto (cliente) a partir de um PC host usando um Arduino Leonardo como dispositivo HID USB. O host captura teclado e mouse via hooks globais do Windows e retransmite os eventos ao Arduino por Bluetooth Serial (HC-06), que os injeta como HID no PC cliente.
Sem software no PC cliente. O Arduino aparece como teclado + mouse USB padrão.
Hardware
- Arduino Leonardo (ATmega32U4) — único modelo com HID USB nativo
- HC-06 módulo Bluetooth (Slave) conectado ao Serial1 do Leonardo
- LED RGB nos pinos: R=5, G=6, B=9 (PWM, analogWrite)
- Baud rate: 9600 (padrão HC-06 de fábrica)
Host PC ──BT SPP──► HC-06 ──Serial1──► Arduino Leonardo ──USB HID──► Cliente PC
(KVMote.exe) (COM virtual) (sem software)
Estrutura de arquivos
| Arquivo | Função |
|---|---|
KVMote.ino |
Firmware Arduino: máquina de estados, HID, LED |
Principal.cs |
Lógica principal C#: hooks, KVM, serial, clipboard |
Principal.Designer.cs |
UI WinForms: layout, controles |
Program.cs |
Entry point: DPI + ApplicationConfiguration |
Form1.cs / Form1.Designer.cs |
Stubs vazios (manter, não remover) |
KVMote.csproj |
.NET 8, ImplicitUsings=disable, System.IO.Ports |
Protocolo serial binário (Host → Arduino)
| Comando | Bytes | Descrição |
|---|---|---|
M dx dy |
3 | Mouse move (int8 com sinal) |
W delta |
2 | Mouse wheel / touchpad (int8) |
K char |
2 | Keyboard.write(char) — digita caractere |
C L|R |
2 | Mouse click esquerdo ou direito |
P keycode |
2 | Keyboard.press(keycode) — tecla segurada |
U keycode |
2 | Keyboard.release(keycode) |
A |
1 | Keyboard.releaseAll() |
D L|R |
2 | Mouse.press() — botão segurado |
E L|R |
2 | Mouse.release() |
O |
1 | LED magenta (entrou no cliente) |
H |
1 | LED azul (host conectado) |
G |
1 | LED verde (host desconectado) |
~ |
1 | Ping → Arduino responde [PONG] |
Arduino → Host: apenas [PONG] como string ASCII.
LED RGB — cores e significados
| Cor | Estado |
|---|---|
| 🟢 Verde (0,255,0) | Boot / host desconectado |
| 🔵 Azul (0,0,255) | Host conectado, mouse no host |
| 🟣 Magenta (255,0,255) | Mouse no PC cliente |
Sem flashes de tráfego — cor sólida estática.
Lógica KVM (Principal.cs)
Auto-detect de porta
AutoDetectAsync() → ProbePorts(): abre cada COM, envia ~, aguarda 500ms por [PONG].
Hooks globais
WH_MOUSE_LL(14) eWH_KEYBOARD_LL(13) viaSetWindowsHookEx- Instalados após conectar, removidos ao desconectar
Entrada no modo cliente
- Mouse atinge a borda configurada (Left/Right/Above/Below)
EnterClientMode(): esconde cursor, warp para centro da tela- Técnica FPS:
SetCursorPospara posição fixa, acumula deltas reais
Coordenadas virtuais
_virtualX/_virtualY: acumulam deltas enviados ao cliente- Retorno ao host quando virtual cruza
-ReturnThreshold(15px) na direção de entrada
Saída do modo cliente
ExitClientMode(): mostra cursor, reposiciona 40px dentro da borda, enviaA+H- Também chamado em
BeginReconnect()para não deixar cursor sumido
Throttle de mouse
SendMouse()usaMonitor.TryEnter(lossy) — descarta se canal ocupado- Throttle de 50ms (~20 pacotes/s) — seguro para BT 9600 baud
Wheel / touchpad
- Acumula
_wheelAccum += rawDelta - Envia ao Arduino quando acumula ±120 (1 notch de mouse wheel)
- Captura smooth scroll de touchpad (deltas pequenos ±3..±15)
Clipboard (Ctrl+V em modo cliente)
- Hook intercepta Ctrl+V e Shift+Ins em modo cliente
- Lê
Clipboard.GetText()do host - Limite:
MaxClipChars = 300 - Envia
A(releaseAll) + 100ms antes de digitar - Digita via
K+ byte a cada 20ms (~50 chars/s) - Layout PT-BR ABNT2:
PtBrMapremapeia chars via substituição de byte antes deKeyboard.write()
Reconexão
BeginReconnect(): chamado em timeout serial ou exceção de porta- Retenta a cada 2500ms
- Chama
ExitClientMode()imediatamente (cursor volta sempre) !_isReconnectingbloqueia entrada no modo cliente durante reconexão
Convenções C# obrigatórias
<ImplicitUsings>disable</ImplicitUsings>
Todo using deve ser explícito. Faltou um → CS0103 em runtime.
Designer file: todos os membros de Form precisam do prefixo this.:
this.btnConnect.Text = "Conectar"; // ✓
btnConnect.Text = "Conectar"; // ✗ CS0103 em ImplicitUsings=disable
AutoScaleMode: usar Dpi com AutoScaleDimensions = new SizeF(96F, 96F). Nunca None (quebra DPI alto) nem Font (escala diferente por máquina).
Form1.cs / Form1.Designer.cs: manter como stubs vazios (namespace KVMote { }). O VS inclui no .csproj automaticamente; remover do disco pode causar erro de build.
Como gerar o .exe portable
# Self-contained (~70MB) — sem dependência de .NET no destino
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true
Saída: bin\Release\net8.0-windows\win-x64\publish\KVMote.exe
Não usar o "Publicar" do Visual Studio — gera ClickOnce (setup.exe + arquivos separados).
Limitações conhecidas
- Canal único 9600 baud: mouse trava durante paste de clipboard (design proposital)
- Chars não-ASCII (
é,ã,ç...): não enviáveis via clipboard (filtrados) - PT-BR:
/,?,\,|,@não mapeáveis viaKeyboard.write()— ignorados no paste - Monitor único:
Screen.PrimaryScreen— multi-monitor não implementado - Um cliente por vez: arquitetura 1:1 (um HC-06, um Arduino)
- BT desconectado: se Arduino perder USB, BT cai,
BeginReconnect()age em ~3s
Roadmap (não implementado)
- Clipboard Bridge: app separado para compartilhar clipboard via OneDrive (pasta compartilhada) entre host e cliente corporativo
- Multi-monitor: detectar em qual monitor o cursor está ao cruzar a borda
- Layout US-International completo: mapeamento de dead keys para paste
- Baud rate configurável: AT commands no HC-06 para 115200