From 909d9aa13cec165c90890a5e1f487c842c518020 Mon Sep 17 00:00:00 2001 From: ponsy Date: Mon, 24 Aug 2026 21:33:26 +0200 Subject: [PATCH] Firmware: switch to RAW HID (12 buttons, no keyboard emulation); host-app: HID report listener --- firmware/macro_pad_key_definition.ino | 108 ------------------ .../.vscode/c_cpp_properties.json | 60 ++++++++++ .../macro_pad_key_definition.ino | 102 +++++++++++++++++ host-app/MacroPad.Host/Program.cs | 65 +++++++++-- 4 files changed, 217 insertions(+), 118 deletions(-) delete mode 100644 firmware/macro_pad_key_definition.ino create mode 100644 firmware/macro_pad_key_definition/.vscode/c_cpp_properties.json create mode 100644 firmware/macro_pad_key_definition/macro_pad_key_definition.ino diff --git a/firmware/macro_pad_key_definition.ino b/firmware/macro_pad_key_definition.ino deleted file mode 100644 index 79f1457..0000000 --- a/firmware/macro_pad_key_definition.ino +++ /dev/null @@ -1,108 +0,0 @@ -// Key definitions -#define BUTTON_KEY1 KEY_F13 -#define BUTTON_KEY2 KEY_F14 -#define BUTTON_KEY3 KEY_F15 -#define BUTTON_KEY4 KEY_F16 -#define BUTTON_KEY5 KEY_F17 -#define BUTTON_KEY6 KEY_F18 -#define BUTTON_KEY7 KEY_F19 -#define BUTTON_KEY8 KEY_F20 - -#define BUTTON_KEY9 KEY_F21 -#define BUTTON_KEY10 KEY_F22 -#define BUTTON_KEY11 KEY_F23 -#define BUTTON_KEY12 KEY_F24 - -// Pin definitions -#define BUTTON_PIN1 2 -#define BUTTON_PIN2 3 -#define BUTTON_PIN3 4 -#define BUTTON_PIN4 5 -#define BUTTON_PIN5 6 -#define BUTTON_PIN6 7 -#define BUTTON_PIN7 8 -#define BUTTON_PIN8 9 - -#define BUTTON_PIN9 10 -#define BUTTON_PIN10 15 -#define BUTTON_PIN11 14 -#define BUTTON_PIN12 16 - -// --------------------------------- - -#include "Keyboard.h" - -// Button helper class for handling press/release and debouncing -class button { - public: - const char key; - const uint8_t pin; - - button(uint8_t k, uint8_t p) : key(k), pin(p){} - - void press(boolean state){ - if(state == pressed || (millis() - lastPressed <= debounceTime)){ - return; // Nothing to see here, folks - } - - lastPressed = millis(); - - state ? Keyboard.press(key) : Keyboard.release(key); - pressed = state; - } - - void update(){ - press(!digitalRead(pin)); - } - - private: - const unsigned long debounceTime = 30; - unsigned long lastPressed = 0; - boolean pressed = 0; -} ; - -// Button objects, organized in array -button buttons[] = { - {BUTTON_KEY1, BUTTON_PIN1}, - {BUTTON_KEY2, BUTTON_PIN2}, - {BUTTON_KEY3, BUTTON_PIN3}, - {BUTTON_KEY4, BUTTON_PIN4}, - {BUTTON_KEY5, BUTTON_PIN5}, - {BUTTON_KEY6, BUTTON_PIN6}, - {BUTTON_KEY7, BUTTON_PIN7}, - {BUTTON_KEY8, BUTTON_PIN8}, - {BUTTON_KEY9, BUTTON_PIN9}, - {BUTTON_KEY10, BUTTON_PIN10}, - {BUTTON_KEY11, BUTTON_PIN11}, - {BUTTON_KEY12, BUTTON_PIN12}, -}; - -const uint8_t NumButtons = sizeof(buttons) / sizeof(button); -const uint8_t ledPin = 17; - -void setup() { - // Safety check. Ground pin #1 (RX) to cancel keyboard inputs. - pinMode(1, INPUT_PULLUP); - if(!digitalRead(1)){ - failsafe(); - } - - // Set LEDs Off. Active low. - pinMode(ledPin, OUTPUT); - digitalWrite(ledPin, HIGH); - TXLED0; - - for(int i = 0; i < NumButtons; i++){ - pinMode(buttons[i].pin, INPUT_PULLUP); - } -} - -void loop() { - for(int i = 0; i < NumButtons; i++){ - buttons[i].update(); - } -} - -void failsafe(){ - for(;;){} // Just going to hang out here for awhile :D -} \ No newline at end of file diff --git a/firmware/macro_pad_key_definition/.vscode/c_cpp_properties.json b/firmware/macro_pad_key_definition/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..4add5a4 --- /dev/null +++ b/firmware/macro_pad_key_definition/.vscode/c_cpp_properties.json @@ -0,0 +1,60 @@ +{ + "configurations": [ + { + "name": "arduino:avr:leonardo", + "includePath": [ + "${workspaceFolder}/**", + "C:\\Users\\yohan\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.8.8\\cores\\arduino", + "C:\\Users\\yohan\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.8.8\\variants\\leonardo", + "C:\\Users\\yohan\\AppData\\Local\\Arduino15\\libraries\\Keyboard\\src", + "C:\\Users\\yohan\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.8.8\\libraries\\HID\\src", + "C:\\Users\\yohan\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7\\avr\\include" + ], + "forcedInclude": [ + "C:\\Users\\yohan\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.8.8\\cores\\arduino\\Arduino.h" + ], + "defines": [ + "F_CPU=16000000L", + "ARDUINO=10607", + "ARDUINO_AVR_LEONARDO", + "ARDUINO_ARCH_AVR", + "USB_VID=0x2341", + "USB_PID=0x8036", + "USB_MANUFACTURER=\"Unknown\"", + "USB_PRODUCT=\"Arduino", + "USART1_UDRE_vect_num", + "SPIE", + "SPIF", + "USB_COM_vect_num", + "USART1_RX_vect_num", + "SPI_STC_vect_num", + "USBCON", + "SPI2X", + "SPI_STC_vect", + "USBS1", + "TWIE", + "USBSTA", + "TWI_vect_num", + "USB_COM_vect", + "USART1_UDRE_vect", + "TWI_vect", + "USB_GEN_vect_num", + "USART1_TX_vect", + "USART1_RX_vect", + "USART1_TX_vect_num", + "USBE", + "USBINT", + "UBRR1", + "TWINT", + "UBRR1H", + "UBRR1L", + "USB_GEN_vect" + ], + "compilerPath": "C:\\Users\\yohan\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-g++", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/firmware/macro_pad_key_definition/macro_pad_key_definition.ino b/firmware/macro_pad_key_definition/macro_pad_key_definition.ino new file mode 100644 index 0000000..22e13a9 --- /dev/null +++ b/firmware/macro_pad_key_definition/macro_pad_key_definition.ino @@ -0,0 +1,102 @@ +#include +#include + +// Pin definitions (à définir) +#define BUTTON_PIN1 5 +#define BUTTON_PIN2 4 +#define BUTTON_PIN3 3 +#define BUTTON_PIN4 2 +#define BUTTON_PIN5 9 +#define BUTTON_PIN6 8 +#define BUTTON_PIN7 7 +#define BUTTON_PIN8 6 +#define BUTTON_PIN9 16 +#define BUTTON_PIN10 15 +#define BUTTON_PIN11 14 +#define BUTTON_PIN12 10 + +// --------------------------------- + +#include "HID-Project.h" + +class button { + public: + const uint8_t pin; + const uint8_t bitIndex; + + button(uint8_t p, uint8_t idx) : pin(p), bitIndex(idx) {} + + boolean update(){ + boolean state = !digitalRead(pin); + if(state == pressed || (millis() - lastPressed <= debounceTime)){ + return pressed; + } + lastPressed = millis(); + pressed = state; + return pressed; + } + + private: + const unsigned long debounceTime = 30; + unsigned long lastPressed = 0; + boolean pressed = 0; +}; + +button buttons[] = { + {BUTTON_PIN1, 0}, {BUTTON_PIN2, 1}, {BUTTON_PIN3, 2}, {BUTTON_PIN4, 3}, + {BUTTON_PIN5, 4}, {BUTTON_PIN6, 5}, {BUTTON_PIN7, 6}, {BUTTON_PIN8, 7}, + {BUTTON_PIN9, 8}, {BUTTON_PIN10, 9}, {BUTTON_PIN11, 10}, {BUTTON_PIN12, 11}, +}; + +const uint8_t NumButtons = sizeof(buttons) / sizeof(button); +const uint8_t ledPin = 17; + +uint8_t rawhidBuffer[64]; +uint8_t lastReport[2] = {0}; + +void setup() { + Serial.begin(9600); + pinMode(1, INPUT_PULLUP); + if(!digitalRead(1)){ + failsafe(); + } + + pinMode(ledPin, OUTPUT); + digitalWrite(ledPin, HIGH); + TXLED0; + + for(int i = 0; i < NumButtons; i++){ + pinMode(buttons[i].pin, INPUT_PULLUP); + } + + RawHID.begin(rawhidBuffer, sizeof(rawhidBuffer)); +} + +void loop() { + uint16_t buttonMask = 0; + for(int i = 0; i < NumButtons; i++){ + if(buttons[i].update()){ + buttonMask |= (1 << buttons[i].bitIndex); + } + } + + uint8_t report[2] = { + (uint8_t)(buttonMask & 0xFF), (uint8_t)(buttonMask >> 8) + }; + + if(memcmp(report, lastReport, sizeof(report)) != 0){ + Serial.print("Envoi rapport, mask="); + Serial.println(buttonMask, BIN); + + uint8_t fullReport[64] = {0}; + fullReport[0] = report[0]; + fullReport[1] = report[1]; + RawHID.write(fullReport, sizeof(fullReport)); + + memcpy(lastReport, report, sizeof(report)); + } +} + +void failsafe(){ + for(;;){} +} \ No newline at end of file diff --git a/host-app/MacroPad.Host/Program.cs b/host-app/MacroPad.Host/Program.cs index d325530..516264f 100644 --- a/host-app/MacroPad.Host/Program.cs +++ b/host-app/MacroPad.Host/Program.cs @@ -1,16 +1,61 @@ using HidSharp; -var devices = DeviceList.Local.GetHidDevices(); +var candidates = DeviceList.Local.GetHidDevices(vendorID: 0x2341, productID: 0x8036).ToList(); -Console.WriteLine("Périphériques HID détectés :\n"); - -foreach (var device in devices) +Console.WriteLine($"{candidates.Count} interface(s) trouvée(s) pour ce VID/PID :\n"); +foreach (var d in candidates) { - Console.WriteLine($"{device.GetFriendlyName()}"); - Console.WriteLine($" VID: 0x{device.VendorID:X4} PID: 0x{device.ProductID:X4}"); - Console.WriteLine($" Path: {device.DevicePath}"); - Console.WriteLine(); + Console.WriteLine($" {d.DevicePath}"); +} +Console.WriteLine(); + +var device = candidates.FirstOrDefault(d => !d.DevicePath.Contains("kbd", StringComparison.OrdinalIgnoreCase)); + +if (device is null) +{ + Console.WriteLine("Interface Raw HID non trouvée (seule l'interface clavier standard a été détectée)."); + return; } -Console.WriteLine("Appuie sur une touche pour quitter..."); -Console.ReadKey(); \ No newline at end of file +Console.WriteLine($"Ouverture de : {device.DevicePath}\n"); + +using var stream = device.Open(); +stream.ReadTimeout = Timeout.Infinite; + +Console.WriteLine($"MaxInputReportLength: {device.GetMaxInputReportLength()}"); +Console.WriteLine($"MaxOutputReportLength: {device.GetMaxOutputReportLength()}"); + +var buffer = new byte[device.GetMaxInputReportLength()]; +ushort lastMask = 0; + +Console.WriteLine("En écoute... appuie sur chaque bouton un par un (Ctrl+C pour quitter)\n"); + +while (true) +{ + int count; + try + { + count = stream.Read(buffer, 0, buffer.Length); + } + catch (TimeoutException) + { + continue; + } + + int offset = count > 2 ? 1 : 0; + ushort mask = (ushort)(buffer[offset] | (buffer[offset + 1] << 8)); + + if (mask != lastMask) + { + ushort changed = (ushort)(mask ^ lastMask); + for (int bit = 0; bit < 12; bit++) + { + if ((changed & (1 << bit)) != 0) + { + bool pressed = (mask & (1 << bit)) != 0; + Console.WriteLine($"Bit {bit}: {(pressed ? "PRESSÉ" : "relâché")}"); + } + } + lastMask = mask; + } +} \ No newline at end of file