if you have any questions, please ask. some notes below.
the code implements 3 HID interfaces/endpoints:
1. a keyboard with most of standard keys mapped somewhere on the matrix. this will work everywhere including BIOS setup, but the keymap doesn't match as a standard USB keyboard can only have the standard layout...
2. a wheel mouse emulated by the analog joystick, 2 buttons and the MPG wheel
3. a custom HID with 3 buttons, 32-pos dial (rotary switch), 2-axis joystick, a wheel (MPG), 64 keys, 11 LEDs
after linux boots i set a custom xkb keymap (yop_fagor) which fixes the layout so keys with shift produce correct symbols which are printed on them.
all HID events from the controller are processed by the linux input device. in linuxcnc i use hal_input. the sim_encoder component in 16k20.hal is used to generate a pulse train and feed it to a watchdog in the controller thru unused LED setting messages. it signals the controller that hal_input is listening, to switch the keyboard layout once again: to a linuxcnc/axis-optimized one, joystick/MPG events are being sent thru the custom interface 3 so they are visible at hal_input pins.
the rotary switch position 0 (unmarked/unused on the fagor panel) is treated specially - it re-enables mouse/wheel emulation.
on the hardware.
the MCU is STM32F103C8T6 on DIP40 PCB. the only additional electronic components used are 4 resistors to scale down 5V voltage outputs of the joystick to match ADC input range.
the key matrix is 8 rows/14 columns. MCU internal weak pull-up resistors are enabled for column inputs. a few unused crossings in the matrix are used to wire external hardware buttons.
the 24-pos rotary switch has 120ohm resistors soldered between terminals, so it effectively acts as a 2.76kOm potentiometer. it is given a 3.3v voltage across with the wiper connected to the MCU ADC input. ADC resolution/noise is enough to reliably detect 24 positions.
LEDs are a string of serial-addressed WS2812 RGB LEDs so 8 of them need only 1 pin on the MCU.
the MPG has 5V TTL output, wired directly. quadrature counting is done by the timer configured in encoder mode.