LIRC vs ir-keytool

Related to my recent IoT hacking, what started me down this path is the long term annoyance of my X10 lighting being unreliable. X10 has always been problematic due to it’s use of power line communication, this has gotten worse as we add more and more noisy electronic devices that cause additional feedback onto the house wiring.

With the X10 light switch I had an IR-543 which mapped IR (infra red) and the rest of my home theater gear is all IR controlled, so a single remote could control everything including the lights. Another nice feature of the X10 light switch I had was soft on / soft off – meaning that when you turned the lights off they would dim down to off, and the same for on. At the start of a movie this is pretty nice.

Of course with a wifi enabled light switch, how do I get IR control? This seemed like a good reason to DIY a solution and build an IR controller / repeater based on a Raspberry Pi. I found that it’s relatively easy to control Tasmota devices with curl, so I was able to easily turn the lights on or off using a simple program. I was pleased to discover that the new light switch also had the soft on / soft off behaviour.

To build an IR device on Linux, I first thought of LIRC as I’ve used this in the past. As I dug deeper, it seems the LIRC project is quite dormant and I was fighting with a lot of stale tooling. I was succeeding in getting something working with the various remotes I wanted to use but it felt like it was a lot of work. Then a friend mentioned ir-keytable to me which led me to the more modern IR control in Linux solution.

The short version of the story is that the ir-keytable support is in a similar state as the LIRC work. I believe this boils down to the fact that IR control is still very niche, and there are lots of hardware variables due to many different remote controls. If you want to do something simple: receive IR input to control a linux machine, then ir-keytable is the way to go. More complex situations may require LIRC. Both approaches have their challenges but ir-keytable is the more modern solution.

The rest of this article will be about getting ir-keytable going on Raspberry Pi OS with a TSOP4838 IR receiver. For my application I have a more complex set of requirements so I’ll be continuing with an LIRC based solution, but more on that another time.

For the hardware setup, and more details on LIRC I used this as a reference. For IR receiving we only need to wire up the TSOP4838 and can ignore the resistors and transistor, you can also avoid using a button cell and pull 3.3V directly from the Pi.

Once you’ve got things wired up, then you need to modify /boot/config.txt to enable the kernel support

Of course, if you used a different gpio pin, specify that instead. The easy way to test this is to reboot so the kernel is reconfigured. You can check things are setup right by looking at the loaded modules. There should also be a /dev/lirc0 device now.

Assuming you haven’t installed lirc (ie: this is a clean OS image) we can simply install ir-keytable and start playing with things. If you have lirc installed, first remove it.

Now just running ir-keytable should tell us some stuff about it’s configuration and state

The CEC section is due to the HDMI support that the Raspberry Pi has. If I were trying to control a more modern system that is interconnected with HDMI cables. I think this is probably the future of audio/video automation but my equipment stack pre-dates solid CEC support. We care about the rc0 section that shows our new IR receiver.

I have what I call a ‘slim remote’ which is one of these credit card sized remote controls which runs off a button cell. This is what I’m using to test things with. When I tried to test the ir-keytable install with this remote, nothing happened.

Enabling all of the protocols got me sorted out.

Now when I press the buttons on my remote I’m seeing button down / button up events flying by. I see also that repeats happen fairly quickly. I don’t see any keydown events, only scancodes. The enablement of ir-keytable -p all also does not survive a reboot so we have a few issues to address.

There are two files I need to customize to get things sorted out. I need a NNN.toml file in /etc/rc_keymaps/ and an entry in /etc/rc_maps.cfg to point at that file. A great way to get started on that is to figure out which of the existing config files are helping me see events when I enable all protocols.

There are a bunch of keymap files in /lib/udev/rc_keymaps/ – these are used when we enable all protocols. We need to figure out which one we should copy and modify.

Capture a couple of buttons using the test mode, remember which buttons we pressed and in which order. Then grep for the scancode in /lib/udev/rc_keymaps/ and we should find the right file to copy.

In my case it was terratec_slim_2.toml – let’s copy that into /etc/rc_keymaps/ and rename it to slim-remote.toml. Then we will add an entry to /etc/rc_maps.cfg that looks like:

This should result in the remote being recognized after a reboot. At this point I discovered something interesting, the power button on my remote causes the Pi to power off. While this is sort of useful, it’s not what I wanted. Also, if you look backĀ  at when we first ran just ir-keytable you’ll notice that there is Default keymap: rc-rc6-mce – this is causing the system to pull in another remote definition based on the entry in /etc/rc_maps.cfg, and from what I can tell the file is loaded from the /lib/udev/rc_keymaps/ directory. If you were using one of the popular rc6-mce remotes, this would be magical and useful.

I wasn’t able to understand how to give my new rc_keymap a table name, thus the second * in my rc_maps.cfg entry. Ideally I’d give it a table name, and then I’d be able to modify the kernel overlay to specify my keymap as the default. Oh well, challenges for another day. We can remove the rc-rc6-mce in one of two ways.

  1. Remove the rc-rc6-mce entry from the /etc/rc_maps.cfg file
  2. Specify a bogus default keymap

For the latter – we change the /boot/config.txt entry to read

Now we’ll get an empty default keymap. Good enough. Only the keymap we created is being picked up.

With the loss of the default rc6-mce remote, I no longer had KEY_POWER defined so that fixed my situation of having the machine power off. I wanted to be able to still get the button event, so I added an entry for scancodeĀ  0x8012. Of course, as soon as I added KEY_POWER the button was recognized, but the system would power off.

As an aside – all of the scan codes must map to one of the known codes. This list can be found in the linux kernel source. You will also notice that while you might have KEY_ZOOM in your definition file, it’s an alias for KEY_FULL_SCREEN which is what we see in the dump above.

I tried mapping scancode 0x8012 to KEY_POWER2 but that also had the same power off behaviour. This seems to be baked into the kernel? I gave up digging and just mapped the button to something that wouldn’t power off an picked KEY_TOUCHPAD_TOGGLE which allows me to read the button, but has no side effect.

You can support multiple keymaps in /etc/rc_keymaps/ and as long as you have entries that point to them in /etc/rc_maps.cfg they’ll get picked up. I was able to have two remotes both recognized without any problems. I found a good write up on generating new keymaps, testing, them and even some python code to read the events.

Unfortunately for my needs, it seems the events do not include which keymap defines them. This makes it difficult for any code I’d write to understand which remote was sending the codes. I could rely on unique KEY_CCC mappings per remote, but since this is constricted by the list of codes in the kernel things start getting very tricky.

I finally decided to take the advice from the LIRC documentation: Why should I use LIRC? In my case I want to build a receive which will take one remotes commands, and map them to many remotes output AND support simply forwarding multiple remotes signals and boosting them. Thus it’s both a universal remote and a signal booster. Using ir-keytable would be possible for the universal remote case, but as a signal booster I need to know which remote is sending codes so I can replicate sending the same codes.

LIRC isn’t dead, it’s just not the best tool for most users who simply want to have basic control of their linux machines over IR.

References I used in creating this post that were not directly linked above

One thought on “LIRC vs ir-keytool”

  1. Well – after much beating my head against the problem of getting LIRC to be stable for me, I’ve returned to ir-keytable and it seems like the way to get stable/reliable behaviour.

    More on this as I pull the complete solution together.

Leave a Reply

Your email address will not be published. Required fields are marked *