Initial post: February 3, 2019
Bare metal IR means that button presses from an infrared remote control will be processed by a python script running on an Orange Pi Zero where LIRC (Linux Infrared Remote Control) and ir-keytable are not installed. What is being used is the Linux kernel support for IR remotes and the python-evdev package by Georgi Valkov.
While this note is about handling IR remotes on an Orange Pi Zero running Armbian Stretch, much would probably be valid for the Raspberry Pi. I will probably look into that some time later.
Linuxkernel then there is probably no need for LIRC or Python scripts and so on. Just read the sections 1 and 2 below to verify that the remote can be used. Read sections 6 and 7 to set the IR protocol or protocols permanently. Then look into using
ir-keytable. I have a few notes on that subject which can be found in the post entitled ir-keytable on the Orange Pi Zero.
Table of Contents
- Installing the Kernel Module
- Getting the Remote Scan Codes
- Python Prerequisites
- Handling IR Events
- A Short Python Script
- Setting the IR Protocol Permanently
- My Very Own IR Protocol Setter
By default the IR receiver driver, called
sunxi-cir, is not
installed. That makes sense as the Orange Pi Zero does not have an IR
receiver on board. However the optional expansion card does contain one, and
this is what I will be using. The simplest and indeed the only way I know of
loading the kernel module when booting is to use the
armbian-config utility. Enable the
cir module in
the System/Hardware settings. If you are not familiar with this
procedure, here are the details starting with launching the configuration
Make sure System is highlighted using the up and down cursor keys and then "clicking" on the < OK > button. That means pressing the space bar or Enter button with < OK > highlighted. If that button is not highlighted use the Tab or left and right cursor keys to make it so.
System settings screen, highlight the Hardware
option and click on the < OK > button.
Toggle hardware configuration screen highlight the
cir function and press the space bar so
that a star '*' appears to the left of cir. Click on the
< Save > button and then on the
< Exit > button.
Finally, click on the < Reboot > button.
After a minute or so, start a new SSH session with the OPiZ and perform some checks to see if the module is loaded and initialized.
Everything looks good. The module is loaded, it is initialized at boot
time and IR remote control button presses show up on input event0. Time to
try the remote control. Note if the
xxd filter is not installed,
just run the command without the pipe:
| xxd. The output will
look very weird, but who cares at this point.
It looked so promising, but nothing happened as I pressed buttons on the remote IR control aimed at the receiver. Break out of the loop by pressing the CtrlC combination. As can be seen, the remote is one of those very cheap remotes (about $1 US for the remote and a receiver) from the usual Chinese vendors. It seemed like a wise move, instead of trying this with the complex set box remote which I hope to use in the end. These cheap remotes are known to use the NEC protocol, so time to look at the supported protocol.
The output is the list of supported IR protocols, the one in square brackets is being used. I found that it was necessary to become the "superuser" to enable another protocol.
This worked, something was being read from the event as keys were pressed on the remote.
As before, press the CtrlC keys in combination when tired of looking at this unintelligible output in response to button presses on the remote.
nec protocol does not work, open a second session and
change the protocol as the super user as shown above (or see the last section below). The change is
immediate so you can keep on clicking on the remote to see if
cat reports an event in the first session.
One final remark at this juncture. The user,
probably needs to be a member of the
input group to read
from the events queue. By default, this is true in Armbian.
cir comes from? I was. linux-sunxi.org had a reference to a Wikipedia page Consumer
IR which I found informative. Among other things it mentioned that "CIR
is the most common type of free-space optical communication" with a link to
the latter. Here we go, down the rabbit hole again.
The reference for all this is the section entitled mainline kernel (4.x) and sunxi-cir on the IR page at linux-sunxi.org.
The binary output that was read from the
is not easily interpreted. Of course there is a program to make things
Here is some information about the supported event types. As can be seen there are two types of events being reported:
- Described as "miscellaneous input data" because these events do not fall in the other defined categories such as EV_KEY (keyboard, buttons...), EV_REL (mouse movements), EV_SW (switches).
- Describe as "markers to separate events".
Each event type has a set of codes identifying events. SYN_REPORT is one of 4 codes associated with EV_SYN and it is "synchronize[s] and separate[s] events into packets of input data changes occurring at the same moment in time". This is not very meaningful here but it could be useful to differentiate between X and Y movements of the mouse that occurred simultaneously and two sequential movements along the X and Y axis.
The code MSC_SCAN is not defined in the documentation but it is obviously
refering to a scan code generated by the input device. The scan code
associated with each button on the IR remote is reported in the
value field. The
time field is probably the
MSC_TIMESTAMP code which is the "number of microseconds elapsed since the
last reset". It is supposed to be 32 bit unsigned integer value, so it is not
immediately clear how the value should be read.
It did not take very long to create the following table to link the generated scan codes with the name of the buttons on the remote.
Python 3.5 is installed by default in Armbian
Strecth. However this is not the case for
Python package installer, and its associated tools which will be used to
install the needed library.
pip for Python 3) can be used
to get the needed library.
That is the only library that will be used here.
Documentation for the
evdev library can be found at Read the Docs. What follows is mostly a rehash of parts
of the Reading events tutorial with slight
modifications for the particulars in this context. I will begin with an
interactive Python session to test everything.
Press the keyboard combination CtrlC to interrupt the loop. The first field displayed by
evdev when a remote event is read is a time code. Each button
press seems to come as two events, a button press with
code=type=04 and a synchronization event (
code=type=00 which is not of much use. It can easily be
This simple script will read all events from the input queue, and display the name of the buttons pressed on the IR remote based on their scan codes.
Download version: test_ir.py.
If I had written this in Pascal, I would have created an array of records, with the scan code as the first field and the button name as the second field. The array would be sorted by the scan code and a binary search would be used to find the button name given the scan code. It remains for me to find the idiomatic way of doing this in Python.
The output of the script can be seen below.
The script can be made to behave as if it is a program. That is the
purpose of the shebang line stating with
#! at the top of
the file. However in Linux the file has to be marked
as executable. In Windows the
extension should be sufficient as it should be associated with the
Python interpreter in the registry. The extension plays no role in Linux.
There is an asynchronous version of the script. Using a second session
htop to monitor each script in turn, I was surprised to
find that the asynchronous version seemed a little slower in handling button
presses. However neither version consumed any appreciable processor time when
no remote button was pressed and processing a sequence of events related to a
button press took barely 1% of the processor time with the slower
This is just verification that it is possible to handle at least some types of IR remotes without LIRC. The script is not really usable as it is because the repeated codes from the remote accumulate in the event queue. Either time codes need to be used or the event queue needs to be read one event at a time, flushing the queue as needed to ignore spurious repeated codes. This could be the subject of a future post perhaps.
I glossed over the problem of setting the IR protocol to be used by the
sunxi-cir module at the beginning of this post. Why was it
necessary to run a shell as
root using the
command? In other words, why does the
sudo command not work? It
has to do with file permissions and persistence of the
As can be seen, writing to the
protocols file can only be
done by the owner of the file which is
echo command is performed as user
root because of the
sudo prefix, but writing the output of
to the file is being done as user
zero who does not have
that privilege. This reminds me of an error I sometimes make. The
will not work because only the first one is done as user
It has to be written as follows.
Something akin to that has to be done. This is a well-known problem discussed at length in a stackoverflow exchange since 2008. Here is one of two generally agreed upon solutions to the problem.
This is understandable. The output of
echo is fed into
the input of
tee. That command is a filter that writes its
input to the specified file. The
sudo command means that
tee which will have the needed write
privilege. Getting that straightened out was important because the Orange Pi Zero
is to be run as a headless server. It will not be possible to manually
set the IR protocol. Instead that is done as a
on each reboot.
Add the following line at the bottom of the file.
tee was available on Armbian but what can be done on a Linux
without that filter? The other proposed solution has
a shell to which is passed the
echo command that worked when run
Many prefer this solution as it does not involve a third program. There is a little caveat when multiple protocols need to be enabled.
Just make sure that single quotes are used around all the protocols to
be echoed to avoid confusion with the double quotes around the command passed
on to the shell. I suppose this version could just as easily used in the
Clearly there is some "magic" surrounding the
file. Let's create a file named
prots with the same content
and same permissions as the original
echo command is used to enable a protocol, the content
prots is just replaced.
Clearly access to the
protocols file is not direct. But how
is this done? I clearly have a lot to learn about Linux.
I was inspired by hololeap's answer on the stackoverflow
exchange and decided to create a bash function that combined setting
and displaying IR protocols in one command. Edit the shell configuration
.bashrc in the home directory.
Just add the following lines and save the modified file.
The session has to be stopped and restarted for the command to be available or run the exec bash command. Instead of a "man page" explaining how to use it, here is an exhaustive list of examples.
- Display the supported and enabled IR protocols
- zero@opi:~$ irp IR protocols: rc-5 nec rc-6 [jvc] sony rc-5-sz sanyo sharp mce_kbd xmp imon [lirc]Enabled protocols are surrounded by  brackets.
lircis always enabled.
- Enable a single IR protocol
- zero@opi:~$ irp nec IR protocols: rc-5 [nec] rc-6 jvc sony rc-5-sz sanyo sharp mce_kbd xmp imon [lirc]
- Enable a supplementary IR protocol
- zero@opi:~$ irp +jvc IR protocols: rc-5 [nec] rc-6 [jvc] sony rc-5-sz sanyo sharp mce_kbd xmp imon [lirc]
- Disable a single IR protocol
- zero@opi:~$ irp IR protocols: rc-5 [nec] rc-6 [jvc] sony rc-5-sz sanyo sharp mce_kbd xmp imon [lirc] zero@opi:~$ zero@opi:~$ irp -jvc IR protocols: rc-5 [nec] rc-6 jvc sony rc-5-sz sanyo sharp mce_kbd xmp imon [lirc]
- Combine multiple operations
- zero@opi:~$ irp IR protocols: rc-5 [nec] rc-6 [jvc] sony rc-5-sz sanyo sharp [mce_kbd] xmp imon [lirc] zero@opi:~$ zero@opi:~$ irp "-jvc +sony +sanyo" IR protocols: rc-5 [nec] rc-6 jvc [sony] rc-5-sz [sanyo] sharp [mce_kbd] xmp imon [lirc]
- Disable all IR protocols
- zero@opi:~$ irp lirc IR protocols: rc-5 nec rc-6 jvc sony rc-5-sz sanyo sharp mce_kbd xmp imon [lirc]