This repository contains code I use to communicate with QMK firmware-powered keyboards via the Human Interface Device (HID) class of the USB specification.
Basically, I want to trigger keyboard functionality from external devices, like USB foot pedals etc, and QMK's Raw HID feature allows this to happen. But, a "host" is required to send and receive the messages to the keyboard.
The code is currently solely concerned with passing messages to and from
Georgi stenographic keyboards (see the raw_hid_receive
function definition
in my Georgi keymap to see how the message is received, handled, and then
sent back to the computer), but it can be adapted for any keyboard's QMK
firmware:
- Find your keyboard's firmware in QMK's keyboard list
- Look for the
VENDOR_ID
andPRODUCT_ID
values in the top-levelconfig.h
file - Substitute those values, as well as any payload changes you want to make, into whatever file in this repo you would like to use
I wrote a post covering the pain points I went through in trying to get a stable HID connection to my Georgi, and the solution that got integrated to this repo's host code here:
The HIDAPI library is required to open up HID communication channels, so install it with your operating system's package manager. HIDAPI provides some minimal information about this in their Installing HIDAPI section, but if you are using macOS, you can install it with Homebrew:
brew install hidapi
The HID host uses the Steno Tape library to output custom entries to the steno tape, so that needs to be installed:
git clone git@github.com:paulfioravanti/steno_tape.git
cd steno_tape
make install
Feel free to compile the host as you see fit, but I use pkg-config (repo) to provide an easier interface to include libraries. You can install it via your operating system package manager, or use Homebrew if you use macOS:
brew install pkg-config
If you use pkg-config
, you can use the included build file:
./build.sh
Otherwise, feel free to use it as a guide to make your own.
Currently, the C executable only accounts for integers to be sent through as command line parameters, which are then received in my Georgi keymap.
./hid_host 1
./hid_host 2
In order to run the host with an Elgato Stream Deck Pedal, I needed to
create a simple shell wrapper around the C executable
(hid_host_client.sh
), since the only kinds of external scripts Stream Deck
seems to be able to run are shell scripts:
./hid_host_client.sh 1
./hid_host_client.sh 2
I have had issues where I was getting random errors when reading to or writing from the HID device (in this case, the Georgi). Going into the Keyboard settings for macOS and changing/resetting the "Key repeat rate" and/or the "Delay until repeat" settings, and then restarting the computing seemed to get things more stable.
More info at:
---
title: Steno Tape Custom Entries
---
flowchart BT
FirmwareRawHID["Raw HID"]
FirmwareProcessRecordUser["Process Record User"]
HIDHost["HID Host"]
HIDHostClient["HID Host Client"]
StenoTapeLibrary["Steno Tape Library"]
StenoTapeClient["Steno Tape Client"]
StenoTapeCustomEntriesWorkflow["Steno Tape Custom Entries Workflow"]
StreamDeck["Stream Deck"]
StreamDeckPedal[/"STREAM DECK PEDAL"\]
TapeyTape[Tapey Tape]
TapeLogFile["Tape Log\n(tapey_tape.txt)"]
TapeFilterScript["Tape Filter Script"]
classDef current fill:#ffb3b3, stroke:#333, stroke-width:4px, color:black
subgraph Computer["COMPUTER"]
subgraph Alfred["Alfred"]
StenoTapeCustomEntriesWorkflow
end
subgraph Elgato["Elgato"]
StreamDeck
end
subgraph HIDHosts["HID Hosts"]
HIDHost:::current
HIDHostClient:::current -- "`./hid_host`" --> HIDHost
end
class HIDHosts current
subgraph Plover["Plover"]
subgraph Plugins
TapeLogFile
TapeyTape -- outputs to --> TapeLogFile
end
end
subgraph StenoTape["Steno Tape"]
StenoTapeLibrary
StenoTapeClient -- "calls API" --> StenoTapeLibrary
TapeFilterScript
end
Terminal(Terminal)
end
subgraph Keyboard["KEYBOARD"]
subgraph QMKFirmware["QMK Firmware"]
FirmwareProcessRecordUser
FirmwareRawHID
end
end
Terminal -- "`./run-tape-feed.sh --filter`" --> TapeFilterScript
TapeFilterScript -- filters --> TapeLogFile
StenoTapeLibrary -- outputs to --> TapeLogFile
StenoTapeCustomEntriesWorkflow -- "`./steno_tape_client`" --> StenoTapeClient
FirmwareProcessRecordUser -- "SEND_STRING(...)" --> StenoTapeCustomEntriesWorkflow
HIDHost -- "calls API" --> StenoTapeLibrary
FirmwareRawHID -. "hid_read/\nraw_hid_send" .-> HIDHost
HIDHost -- "hid_write/\nraw_hid_receive" --> FirmwareRawHID
StreamDeck -- "`./hid_host_client.sh`" --> HIDHostClient
StreamDeckPedal -- connects --> StreamDeck
click Dictionaries href "https://github.com/paulfioravanti/steno-dictionaries" "Steno Dictionaries" _blank
click FirmwareRawHID href "https://github.com/paulfioravanti/qmk_keymaps/blob/master/keyboards/gboards/georgi/keymaps/paulfioravanti/user/hid.c" "Georgi HID" _blank
click FirmwareProcessRecordUser href "https://github.com/paulfioravanti/qmk_keymaps/blob/master/keyboards/gboards/georgi/keymaps/paulfioravanti/user/process_record_user.c" "Georgi Process Record User" _blank
click HIDHost href "https://github.com/paulfioravanti/hid_hosts/blob/main/hid_host.c" "HID Host" _blank
click HIDHostClient href "https://github.com/paulfioravanti/hid_hosts/blob/main/hid_host_client.sh" "HID Host Client" _blank
click StenoTapeClient href "https://github.com/paulfioravanti/steno_tape/blob/main/clients/steno_tape_client.c" "Steno Tape Client" _blank
click StenoTapeCustomEntriesWorkflow href "https://github.com/paulfioravanti/dotfiles/tree/master/macos/alfred" "Steno Tape Custom Entries Workflow" _blank
click StenoTapeLibrary href "https://github.com/paulfioravanti/steno_tape/blob/main/src/steno_tape.c" "Steno Tape" _blank
click StreamDeck href "https://www.elgato.com/us/en/s/welcome-to-stream-deck" "Stream Deck" _blank
click StreamDeckPedal href "https://www.elgato.com/us/en/p/stream-deck-pedal" "Stream Deck Pedal" _blank
click TapeyTape href "https://github.com/rabbitgrowth/plover-tapey-tape" "Tapey Tape" _blank
click TapeFilterScript href "https://github.com/paulfioravanti/steno_tape/blob/main/bin/run-tape-feed.sh" "Tape Filter Script" _blank