UPDATE 2024-03-14: Just use xremap. It can remap or keybind things at the evdev level with only/except device filtering and, if you’re on X11 or supported Wayland compositors, it also supports applying only/except application filtering for your remaps/keybinds.
UPDATE 2021-04-18: There’s now a tool for reprogramming several models of these footswitches, so consider this just a fallback for if yours isn’t supported.
Well, I have a Chinese USB foot pedal that I picked up on eBay for $11 CA to play around with but, for ages, it’s been worth bupkiss because it’s mapped to the “b” key on the keyboard and I was too busy to look into how to remap a key on just one device on Linux… that ends here and now.
Try 1: Remapping in XKB
The most obvious solution would be to remap the thing by applying a custom X11 keyboard layout to just the one device. (ie. Redefine how keycodes from evdev map to X11 keysyms.)
OK, doesn’t seem too hard and, Google to the rescue, someone even wrote some example code for it. (See also: XKB Remapping)
The gist is:
- Use
xinput list
to find the ID for the device you want to limit the remapping to - Use
setxkbmap -device <the ID> -print
to dump the keymap - Write a custom supplemental XKB keymap which redefines the key in question
sed
the dumped keymap to append the supplemental keymap to the stack of what gets applied.- Use
xkbcomp
to re-apply it.
…but, no matter what I tried, I couldn’t get my Ubuntu 14.04 LTS system to acknowledge the change.
Verdict: Failure on *buntu 14.04 LTS
Try 2: Remapping in XKB (hackier but more foolproof)
I was getting errors from the previous version and I didn’t know whether any of them were spurious, so I decided to try the old “hacky but foolproof” standby described in this ArchWiki page … dump the keymap, hand-edit the stupid thing, and then re-apply it.
xkbcomp -i <the ID> $DISPLAY ~/out.xkb
vim ~/out.xkb
xkbcomp -i <the ID> ~/out.xkb $DISPLAY
First, let me caution you that it has to be -i ID
and not -iID
. The latter form doesn’t work.
That said, it ran without complaint and even showed the expected results when I re-dumped the keymap fresh from the X server… it just had no effect.
(I think it might be because, when you look at the USB HID descriptor that evtest
dumps on startup, the stupid pedal announces that it’s a 162-key keyboard, plus a mouse with scroll wheel, all in one device, which causes xinput list
to file it as a mouse rather than a keyboard.)
Verdict: Failure on *buntu 14.04 LTS
(Naturally, I didn’t continue on to research how to make it survive restarts.)
Try 3: Remapping via udev
OK, no big deal. My ATi Remote Wonder II didn’t need remapping because it sent keycodes that were already mapped to the XF86AudioPlay
and XF86AudioPause
keysyms. Let’s drop below X11 and redefine how the kernel maps hardware scan codes from the pedal to evdev keycodes.
Now, since we need to redefine it for only one input device, that means we can’t use the setkeycodes
command, so udev it is. (Besides, I’ve heard that setkeycodes
doesn’t work with USB devices.)
According to these StackExchange answers [1] [2] and these ArchWiki pages [1] [2], I need to put together a definition like this, and store it at /etc/udev/hwdb.d/some-unique-name.hwdb
, then refresh the database:
keyboard:input:b0003v0C45p7403* KEYBOARD_KEY_70005=playpause
Oh, and make sure you only indent the second line one space. More will cause a silent failure and the first guide I read not only didn’t mention that, but actually used two spaces in the example!
To keep things simple, here’s the overview of the process:
- Create something in the above format as a
/etc/udev/hwdb.d/whatever.hwdb
file. - The numbers in the file are as follows:
- The
b0003
means “Bus type: USB” - The
v0c45
andp7403
are the USB vendor and product IDs fromlsusb
- The
70005
is theMSC_SCAN
value from pressing the pedal whileevtest
is listening. - The
playpause
is the kernel-internal name for the desired keycode. They’re defined in a header I don’t seem to have (linux/input-event-codes.h
) but the ArchWiki pages have a link to an online list of them.
- The
- After putting the file in place, run
sudo udevadm hwdb --update
(older systems) orsudo systemd-hwdb update
(newer systems) to merge it into the hardware database. - Finally, either reboot your system or run
sudo udevadm trigger
to apply the changes. (Un-plugging and re-plugging the thing may also work)
…so, did it work? Well… no. It turns out *buntu 14.04 is using the last version of udev before that feature was added (14.04 has udev 204 and it needs 205 or higher).
(Also, note that whether you use the keyboard:
prefix or the evdev:
prefix will depend on the udev version you use. I tried both, but this site explains what to use for which version.)
Verdict: Failure on *buntu 14.04 LTS
NOTE: This AskUbuntu answer that I found after hacking together my own solution suggests that it might actually be possible, but that 14.04 includes a version of udev with some funny requirements for applying the changes.
Try 4: Remapping via udev (the old way)
*sigh* Ok, so we can’t use the clean, easy solution everyone’s listing because 14.04 is too old… so what’s the old solution?
Well, according to this page and this AskUbuntu answer, versions of udev up to and including 204 do key remapping by using the old familiar udev rules.d
system and a helper utility named /lib/udev/keymap
. Cool.
To paraphrase Jurassic Park, “It’s a rules.d
system, I know this.” (From fixing permissions on various devices, to getting my Chinese NES controller adapter to announce itself as a joystick, I have a fair bit of experience writing rules.d
files.)
OK, so I write this /etc/udev/keymaps/microdia-foot-switch
file, using the same values from the previous attempt:
0x70005 playpause
…and this /etc/udev/rules.d/99-microdia-foot-switch.rules
file:
ACTION!="remove", SUBSYSTEMS=="usb", ENV{ID_VENDOR_ID}=="0c45", ENV{ID_MODEL_ID}=="7403", RUN+="keymap $name /etc/udev/keymaps/microdia-foot-switch"
…and unplug and replug the foot pedal… and nothing happens. I check `dmesg` and whadda ya know, `/lib/udev/keymap` isn’t installed.
(NOTE: While it didn’t affect me, your distro may have a bug in keymap
which forces you to use the keycode itself instead of the playpause
name alias for it. If you happen to have another device which can emit the target keycode, you can use sudo showkey --keycodes
to get it really easily.)
So, anyway, I pop over to packages.ubuntu.com to search up the package to install… and it turns out it was in udev
in Precise and got pulled (with no replacement) in Trusty… way to jump the gun, guys!
Of course, this isn’t the only botch in 14.04 (it also shipped with a buggy version of Geeqie), so this is old-hat.
- Pop over to the precise-updates version of the
udev
package on packages.ubuntu.com, scroll to the bottom, and download whichever architecture matches your hardware. (Probably amd64, in this day and age) - Open it up with
file-roller
(because it’s smart enough to hide the fact that a.deb
is an archive containing more archives) and copy out/lib/udev/keymap
. sudo cp keymap /lib/udev/keymap && chmod +x /lib/udev/keymap
Now, try unplugging and re-plugging the thing again and it should work.
Verdict: Success on *buntu 14.04 LTS
…but wait, there’s more!
While evtest
and xev
were showing the right keysym, my Audacious Media Player wasn’t listening to the resulting XF86AudioPlay
presses.
Apparently, not all XF86AudioPlay
s are created equal, because adding a second mapping from “Pause/Resume” to XF86AudioPlay
by stepping on the pedal fixed it.
(My hypothesis is that, while Audacious is displaying the keysym names, it’s binding using the underlying keycodes and the one from my ATi Remote Wonder II began life as a play
keypress while the one from the pedal began life as playpause
.)
Getting Your Cheap Chinese USB Foot Pedal Doing Useful Things on Linux by Stephan Sokolow is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Nice work! Helped me a great deal. Your style of writing, clear instructions, summaries, and use of text features is something that most tech experts and forums lack.
You’ll do well in IT.
Has anybody had any success doing this with multiple of these pedals? I’m looking to build a sort of software-defined effects pedal system and will need more than one pedal.
I’d try it myself and let you know, but I only have the one pedal.
In theory, there are two issues you’ll need to address:
First, I doubt the pedals contain unique serial numbers and I’m not sure whether Linux will enumerate USB devices in a predictable order, so you might need to have your effects pedal system ask the user to press the pedals in order on startup to figure out the mapping between devices and physical ordering.
(Which reminds me of how Nintendo 64 controllers assume the stick is centered on startup and you can hold L+R+Start to reset them if that wasn’t the case.)
Second, you’ll need some way to tell them apart… and I’m not sure doing it at this level would be the best solution.
Instead, you could just let them all be mapped to the same button (ideally, something that has no default behaviour to collide with on your keyboard) and then detect which one the input came from in your application.
The XInput2 API provides device-of-origin information under X11 and this AskUbuntu answer provides some good starting points for using it. (In reply to a question about telling apart input from two USB-attached touchscreens.)
You can change the key, store a custom one, and don’t need to remember to tinker with udev or xkb configurations anymore:
https://github.com/rgerganov/footswitch
Thanks. I’ve added a note at the top of the post.