KVMote/KVMote.ino
Ricardo Carneiro c77a9cac0d Add KVMote source — C#, Arduino sketch and .gitignore
- Principal.cs/Designer.cs: WinForms KVM host app (.NET 8)
  Global mouse/keyboard hooks, edge detection, BT serial protocol,
  clipboard paste via Ctrl+V, PT-BR ABNT2 layout translation
- KVMote.ino: Arduino Leonardo sketch (HC-06 BT, USB HID)
  Non-blocking state machine, LED RGB feedback, full protocol support
- KVMote.csproj/sln: project files with System.IO.Ports NuGet
- .gitignore: C#/Visual Studio standard exclusions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 15:09:07 -03:00

184 lines
7.1 KiB
C++

/*
KVMote — Arduino Leonardo (Serial1 = HC-06)
Baud rate: 9600 (padrão de fábrica do HC-06).
Se você já configurou o HC-06 para outra velocidade via AT commands,
altere a constante abaixo.
Protocolo binário:
Mouse move → 'M' dx(int8) dy(int8) 3 bytes
Mouse wheel → 'W' delta(int8) 2 bytes
Tecla write → 'K' char 2 bytes
Clique → 'C' 'L'|'R' 2 bytes
Tecla press → 'P' keycode 2 bytes
Tecla release → 'U' keycode 2 bytes
ReleaseAll → 'A' 1 byte
Mouse press → 'D' 'L'|'R' 2 bytes
Mouse release → 'E' 'L'|'R' 2 bytes
LED cliente → 'O' (laranja — entrou no cliente)
LED host → 'H' (flash verde + volta azul — voltou ao host)
Ping/Pong → '~' → responde [PONG] 1 byte
*/
#include <Keyboard.h>
#include <Mouse.h>
// ── Pinos do LED RGB ──────────────────────────────────────────────────────────
#define PIN_R 5
#define PIN_G 6
#define PIN_B 9
#define BAUD_HC06 9600 // ← altere se necessário
// ── Cor base do LED (muda conforme o modo) ────────────────────────────────────
uint8_t basR = 0, basG = 0, basB = 255; // azul = idle
// ── Máquina de estados ────────────────────────────────────────────────────────
enum Estado : uint8_t {
AGUARDA_CMD,
AGUARDA_MOUSE_DX,
AGUARDA_MOUSE_DY,
AGUARDA_MOUSE_WHEEL,
AGUARDA_TECLA,
AGUARDA_CLIQUE,
AGUARDA_PRESS_KEY,
AGUARDA_RELEASE_KEY,
AGUARDA_MOUSE_PRESS,
AGUARDA_MOUSE_RELEASE
};
Estado estado = AGUARDA_CMD;
int8_t pendingDX = 0;
// ── Helpers de LED ────────────────────────────────────────────────────────────
void ledCor(int r, int g, int b) {
analogWrite(PIN_R, r);
analogWrite(PIN_G, g);
analogWrite(PIN_B, b);
}
void piscaVerde() {
ledCor(0, 200, 0);
delay(40);
ledCor(basR, basG, basB); // retorna à cor base atual
}
void piscaCiano() {
ledCor(0, 200, 200);
delay(40);
ledCor(basR, basG, basB); // retorna à cor base atual
}
// ── Setup ─────────────────────────────────────────────────────────────────────
void setup() {
Serial1.begin(BAUD_HC06);
Keyboard.begin();
Mouse.begin();
ledCor(basR, basG, basB); // azul = pronto
}
// ── Loop principal (non-blocking) ─────────────────────────────────────────────
void loop() {
while (Serial1.available() > 0) {
byte b = (byte)Serial1.read();
switch (estado) {
// ── Aguarda o byte de comando ──────────────────────────────────────────
case AGUARDA_CMD:
if (b == 'M') estado = AGUARDA_MOUSE_DX;
else if (b == 'W') estado = AGUARDA_MOUSE_WHEEL;
else if (b == 'K') estado = AGUARDA_TECLA;
else if (b == 'C') estado = AGUARDA_CLIQUE;
else if (b == 'P') estado = AGUARDA_PRESS_KEY;
else if (b == 'U') estado = AGUARDA_RELEASE_KEY;
else if (b == 'D') estado = AGUARDA_MOUSE_PRESS;
else if (b == 'E') estado = AGUARDA_MOUSE_RELEASE;
else if (b == 'A') {
Keyboard.releaseAll();
piscaVerde();
}
else if (b == 'O') {
// Entrou no PC cliente → LED laranja
basR = 255; basG = 80; basB = 0;
ledCor(basR, basG, basB);
}
else if (b == 'H') {
// Voltou ao host → flash verde, LED azul
basR = 0; basG = 0; basB = 255;
piscaVerde();
}
else if (b == '~') {
Serial1.print("[PONG]");
piscaCiano();
}
// qualquer outro byte é silenciosamente descartado
break;
// ── Mouse: lê DeltaX ──────────────────────────────────────────────────
case AGUARDA_MOUSE_DX:
pendingDX = (int8_t)b;
estado = AGUARDA_MOUSE_DY;
break;
// ── Mouse: lê DeltaY e executa o movimento ────────────────────────────
case AGUARDA_MOUSE_DY:
Mouse.move(pendingDX, (int8_t)b, 0);
estado = AGUARDA_CMD;
// Sem piscaVerde() aqui — delay(40) bloquearia o processamento serial
break;
// ── Mouse: roda do mouse ──────────────────────────────────────────────
case AGUARDA_MOUSE_WHEEL:
Mouse.move(0, 0, (int8_t)b);
estado = AGUARDA_CMD;
break;
// ── Teclado: lê o caractere e digita (write) ──────────────────────────
case AGUARDA_TECLA:
Keyboard.write(b);
estado = AGUARDA_CMD;
piscaVerde();
break;
// ── Clique: lê L ou R e clica ─────────────────────────────────────────
case AGUARDA_CLIQUE:
if (b == 'L') Mouse.click(MOUSE_LEFT);
if (b == 'R') Mouse.click(MOUSE_RIGHT);
estado = AGUARDA_CMD;
piscaVerde();
break;
// ── Tecla press (mantém pressionada) ─────────────────────────────────
case AGUARDA_PRESS_KEY:
Keyboard.press(b);
estado = AGUARDA_CMD;
piscaVerde();
break;
// ── Tecla release ─────────────────────────────────────────────────────
case AGUARDA_RELEASE_KEY:
Keyboard.release(b);
estado = AGUARDA_CMD;
piscaVerde();
break;
// ── Mouse press (botão segurado) ──────────────────────────────────────
case AGUARDA_MOUSE_PRESS:
if (b == 'L') Mouse.press(MOUSE_LEFT);
if (b == 'R') Mouse.press(MOUSE_RIGHT);
estado = AGUARDA_CMD;
piscaVerde();
break;
// ── Mouse release ─────────────────────────────────────────────────────
case AGUARDA_MOUSE_RELEASE:
if (b == 'L') Mouse.release(MOUSE_LEFT);
if (b == 'R') Mouse.release(MOUSE_RIGHT);
estado = AGUARDA_CMD;
piscaVerde();
break;
}
}
}