Last Updated on
Infrared sensors are really common to devices which need to be controlled from a remote within reduced distances. Typical applications are in remoting TV, media center, robots, air conditioning systems, etc.
With the growth of mass production, their prices have plummeted and can be found on e-commerce websites.
Linux systems have a spread package called LIRC (Linux Infrared Remote Control). This is a really handy package with most common remotes, but it becomes a little tricky when you havea remote not included in LIRC remotes database because you have to know you remote infrared signal map to create your custom configuration file.
A second chance comes from ir-keytable package. This package is really simple to use and allows you to record one by one you remote keys signals and map them to your custom config file, even without knowing signal profile of your remote.
In this article, I’ll show how to connect your Raspberry PI to an IR receiver and control it from a remote with ir-keytable. For this article, I’m going to use a Raspberry PI Zero W, but the same procedure works also with newer Raspberry PI boards.
How Infrared Sensors Work
Before starting with terminal commands, let’s dig on how they works. A very good explaination is available at Adafruit Infrared Sensor description page:
IR detectors are little microchips with a photocell that are tuned to listen to infrared light. They are almost always used for remote control detection – every TV and DVD player has one of these in the front to listen for the IR signal from the clicker. Inside the remote control is a matching IR LED, which emits IR pulses to tell the TV to turn on, off or change channels. IR light is not visible to the human eye, which means it takes a little more work to test a setup.
There are a few difference between these and say a CdS Photocells:
– IR detectors are specially filtered for Infrared light, they are not good at detecting visible light. On the other hand, photocells are good at detecting yellow/green visible light, not good at IR light
– IR detectors have a demodulator inside that looks for modulated IR at 38 KHz. Just shining an IR LED wont be detected, it has to be PWM blinking at 38KHz. Photocells do not have any sort of demodulator and can detect any frequency (including DC) within the response speed of the photocell (which is about 1KHz)
– IR detectors are digital out – either they detect 38KHz IR signal and output low (0V) or they do not detect any and output high (5V). Photocells act like resistors, the resistance changes depending on how much light they are exposed to.
What We Need
As usual, I suggest adding from now to your favourite ecommerce shopping chart all needed hardware, so that at the end you will be able to evaluate overall costs and decide if continuing with the project or removing them from shopping chart. So, hardware will be only:
- Raspberry PI Zero W (including proper power supply or using a smartphone micro usb charger with at least 3A) or newer Raspberry PI board
- micro SD card (at least 16 GB, at least class 10)
- Infrared Sensor
- IR Remote
- dupont wiring (female to female)
Following picture shows wiring diagram adopted:
Install OS following install Raspbian Buster Lite guide. Make your OS up to date:
sudo apt update sudo apt upgrade
Add GPIO to Device Tree overlay in boot config. Edit /boot/config.txt
sudo nano /boot/config.txt
Uncomment and change dtoverlay row as following (GPIO pin parameter refers to BCM naming for RPI pins):
Close and save. Reboot your Raspeberry PI:
After restart, please login again from terminal and install ir-keytable package:
sudo apt install ir-keytable
Map Your Remote With ir-Keytable
Before starting to customize our remote mapping, we can try our luck to find supported remotes. To list supported pre-built Keymaps:
ir-keytable has less pre-built remotes than LIRC, but I prefer it because – as said – it is simpler to customize. If you was lucky and found your remote in pre-built ones, than you can simply test it by typing “ir-keytable -t” on terminal and testing if buttons work. If it returns key names, then you are ok and this procedure ended.
Create Your Custom Mapping
For not so lucky people, list supported kernel protocols with ir-keytable command:
pi@raspberrypi:~ $ sudo ir-keytable Found /sys/class/rc/rc0/ (/dev/input/event0) with: Name: gpio_ir_recv Driver: gpio_ir_recv, table: rc-rc6-mce LIRC device: /dev/lirc0 Attached BPF protocols: Operation not permitted Supported kernel protocols: lirc rc-5 rc-5-sz jvc sony nec sanyo mce_kbd rc-6 sharp xmp imon Enabled kernel protocols: lirc rc-6 bus: 25, vendor/product: 0001:0001, version: 0x0100 Repeat delay = 500 ms, repeat period = 125 ms
In next step we’ll use ir-keytable in test mode to identify and take note about supported protocol and keys pressure ID. Let’s start test procedure with all protocols supported:
sudo ir-keytable -v -t -p rc-5,rc-5-sz,jvc,sony,nec,sanyo,mce_kbd,rc-6,sharp,xmp
Initialization procedure will start:
Found device /sys/class/rc/rc0/ Parsing uevent /sys/class/rc/rc0/lirc0/uevent /sys/class/rc/rc0/lirc0/uevent uevent MAJOR=252 /sys/class/rc/rc0/lirc0/uevent uevent MINOR=0 /sys/class/rc/rc0/lirc0/uevent uevent DEVNAME=lirc0 Input sysfs node is /sys/class/rc/rc0/input0/ Event sysfs node is /sys/class/rc/rc0/input0/event0/ Parsing uevent /sys/class/rc/rc0/input0/event0/uevent /sys/class/rc/rc0/input0/event0/uevent uevent MAJOR=13 /sys/class/rc/rc0/input0/event0/uevent uevent MINOR=64 /sys/class/rc/rc0/input0/event0/uevent uevent DEVNAME=input/event0 Parsing uevent /sys/class/rc/rc0/uevent /sys/class/rc/rc0/uevent uevent NAME=rc-rc6-mce /sys/class/rc/rc0/uevent uevent DRV_NAME=gpio_ir_recv /sys/class/rc/rc0/uevent uevent DEV_NAME=gpio_ir_recv input device is /dev/input/event0 /sys/class/rc/rc0/protocols protocol rc-5 (enabled) /sys/class/rc/rc0/protocols protocol nec (enabled) /sys/class/rc/rc0/protocols protocol rc-6 (enabled) /sys/class/rc/rc0/protocols protocol jvc (enabled) /sys/class/rc/rc0/protocols protocol sony (enabled) /sys/class/rc/rc0/protocols protocol rc-5-sz (enabled) /sys/class/rc/rc0/protocols protocol sanyo (enabled) /sys/class/rc/rc0/protocols protocol sharp (enabled) /sys/class/rc/rc0/protocols protocol mce_kbd (enabled) /sys/class/rc/rc0/protocols protocol xmp (enabled) /sys/class/rc/rc0/protocols protocol imon (disabled) /sys/class/rc/rc0/protocols protocol lirc (enabled) Opening /dev/input/event0 Input Protocol version: 0x00010001 Protocols changed to rc-5 rc-5-sz jvc sony nec sanyo mce_kbd rc-6 sharp xmp Testing events. Please, press CTRL-C to abort.
you are now prompted to generate key pressure events. Point your remote to IR sensor and press a key. In my test, with an Elegoo remote, following rows are generated:
1274.230088: lirc protocol(nec): scancode = 0x45 1274.230144: event type EV_MSC(0x04): scancode = 0x45 1274.230144: event type EV_SYN(0x00).
First line shows that my remote uses “nec” protocol. trace this in your notes. Protocol will be the same for all buttons.
First and second rows show that my key pressure generated a “0x45” scancode. I had this event pressing my Power button on remote. So my first matching is:
0x45 = Power key
Repeat this step for each remote button and create a mapping table similar to following one:
protocol: nec 0x45 = Power 0x46 = Vol+ 0x47 = FUNC/STOP 0x44 = BCW 0x40 = PLAY/PAUSE 0x43 = FRW 0x07 = DOWN 0x15 = VOL- 0x09 = UP 0x16 = 0 0x19 = EQ 0x0d = ST/REPT 0x0c = 1 0x18 = 2 0x5e = 3 0x08 = 4 0x1c = 5 0x5a = 6 0x42 = 7 0x52 = 8 0x4a = 9
Create Your Custom .toml File From Mapping
In this section, we are going to create our custom .toml file that will be used by ir-keytable at kernel level. A .toml file is a simple file with a short description of remote name and a map of system buttons to scancodes. Usable system buttons are listed in following text file you can get from my dowload area:
Usually, correct ones are these keys starting with “KEY_” prefix.
Create a toml file similar to (or download from next link) my Elegoo.toml, but with your data you noted in previous step and related kernel keys:
[[protocols]] name = "Elegoo" protocol = "nec" variant = "nec32" [protocols.scancodes] 0x45 = "KEY_POWER" 0x46 = "KEY_VOLUMEUP" 0x47 = "KEY_FN_S" 0x44 = "KEY_BACK" 0x40 = "KEY_PLAYPAUSE" 0x43 = "KEY_FORWARD" 0x07 = "KEY_DOWN" 0x15 = "KEY_VOLUMEDOWN" 0x09 = "KEY_UP" 0x16 = "KEY_0" 0x19 = "KEY_EQUAL" 0x0d = "KEY_REPLY" 0x0c = "KEY_1" 0x18 = "KEY_2" 0x5e = "KEY_3" 0x08 = "KEY_4" 0x1c = "KEY_5" 0x5a = "KEY_6" 0x42 = "KEY_7" 0x52 = "KEY_8" 0x4a = "KEY_9"
To test our toml file, we need to clean current keytable, load toml file, read i to check and then we can test (diring test, consider that if you press power button and if it works, then your Raspberry PI will shut down):
sudo ir-keytable -c sudo ir-keytable -w /pathToFile/your.toml sudo ir-keytable -r sudo ir-keytable -t
Please find below my terminal output for comparison:
pi@raspberrypi:~ $ sudo ir-keytable -c Old keytable cleared pi@raspberrypi:~ $ sudo ir-keytable -w Elegoo.toml Wrote 21 keycode(s) to driver Protocols changed to nec pi@raspberrypi:~ $ sudo ir-keytable -r scancode 0x0007 = KEY_DOWN (0x6c) scancode 0x0008 = KEY_4 (0x05) scancode 0x0009 = KEY_UP (0x67) scancode 0x000c = KEY_1 (0x02) scancode 0x000d = KEY_REPLY (0xe8) scancode 0x0015 = KEY_VOLUMEDOWN (0x72) scancode 0x0016 = KEY_0 (0x0b) scancode 0x0018 = KEY_2 (0x03) scancode 0x0019 = KEY_EQUAL (0x0d) scancode 0x001c = KEY_5 (0x06) scancode 0x0040 = KEY_PLAYPAUSE (0xa4) scancode 0x0042 = KEY_7 (0x08) scancode 0x0043 = KEY_FORWARD (0x9f) scancode 0x0044 = KEY_BACK (0x9e) scancode 0x0045 = KEY_POWER (0x74) scancode 0x0046 = KEY_VOLUMEUP (0x73) scancode 0x0047 = KEY_FN_S (0x1e3) scancode 0x004a = KEY_9 (0x0a) scancode 0x0052 = KEY_8 (0x09) scancode 0x005a = KEY_6 (0x07) scancode 0x005e = KEY_3 (0x04) Enabled kernel protocols: lirc nec pi@raspberrypi:~ $ sudo ir-keytable -t Testing events. Please, press CTRL-C to abort. 749.430103: lirc protocol(nec): scancode = 0x18 749.430181: event type EV_MSC(0x04): scancode = 0x18 749.430181: event type EV_KEY(0x01) key_down: KEY_2(0x0003) 749.430181: event type EV_SYN(0x00). 749.480258: lirc protocol(nec): scancode = 0x18 repeat 749.480310: event type EV_MSC(0x04): scancode = 0x18 749.480310: event type EV_SYN(0x00). 749.610091: event type EV_KEY(0x01) key_up: KEY_2(0x0003) 749.610091: event type EV_SYN(0x00). 750.430096: lirc protocol(nec): scancode = 0x19 750.430167: event type EV_MSC(0x04): scancode = 0x19 750.430167: event type EV_KEY(0x01) key_down: KEY_EQUAL(0x000d) 750.430167: event type EV_SYN(0x00). 750.560106: event type EV_KEY(0x01) key_up: KEY_EQUAL(0x000d) 750.560106: event type EV_SYN(0x00). 751.710097: lirc protocol(nec): scancode = 0xd 751.710168: event type EV_MSC(0x04): scancode = 0x0d 751.710168: event type EV_KEY(0x01) key_down: KEY_REPLY(0x00e8) 751.710168: event type EV_SYN(0x00). 751.770263: lirc protocol(nec): scancode = 0xd repeat 751.770319: event type EV_MSC(0x04): scancode = 0x0d 751.770319: event type EV_SYN(0x00). 751.900086: event type EV_KEY(0x01) key_up: KEY_REPLY(0x00e8) 751.900086: event type EV_SYN(0x00).
If you have a similar behaviour and keys showed are the same you pressed, then your toml file is valid and working.
Make Your Mapping Permanent
We need now to make our configuration permanent after reboot.
Move your toml file to “/etc/rc_keymaps/”
sudo mv /pathToFile/your.toml /etc/rc_keymaps/
Edit /etc/rc_maps.cfg to add a row
sudo nano /etc/rc_maps.cfg
Add a row on devices table referencing your toml file. Use * wildcard both on driver and table columns. In my case, my file is named Elegoo.toml and my rc_maps.cfg section will appear as the following:
..... ..... # Table to automatically load the rc maps for the bundled IR's provided with the # devices supported by the linux kernel # driver table file * * Elegoo.toml * rc-adstech-dvb-t-pci adstech_dvb_t_pci.toml * rc-alink-dtu-m alink_dtu_m.toml * rc-anysee anysee.toml ..... .....
Test configuration loading:
sudo ir-keytable -a /etc/rc_maps.cfg -s rc0
This should return somthing like following (keycodes number and protocol may vary according to your toml:
Old keytable cleared Wrote 21 keycode(s) to driver Protocols changed to nec
Moreover, “ir-keytable -r” terminal command should show your mapping.
To make it permanent after reboot, edit /etc/rc.local
sudo nano /etc/rc.local
and add loading configuration command before “exit 0” final row:
..... ..... printf "My IP address is %s\n" "$_IP" fi ir-keytable -a /etc/rc_maps.cfg -s rc0 exit 0
Reboot and check it is persistent.