You may have read some of my post about RF433 and Raspberry PI. Basically with RPI 1, I was using wiringPi interrupt handler to manage the RF433 decoding. The problem is that with RPI2 and RPI B+ the delay to take an interrupt that was becomes unpredictable. And the timing constraints are not respected. As a consequence part of the messages are loss because for these delay.
One of the solution (the software one) is to be more efficient to proceed the interrupts and the way to do this is to compile a kernel driver for directly handling the interrupts. This is what this post is about. This comes to complete the RFRPI code and associated hardware. A complete source code and software for using it is on the rfrpi bitbucket repository.
Challenge of the coming days : write a kernel driver to manage interruption quicker on a raspberry pi 2. I’m happy to find a lot of example on Internet and in particular this one, that is really looking like what I’m trying to do. This post is describing all the step needed to do this.
Be prepared to compile a kernel module
From the same source this post also describes well what you have to do to be able to compile a kernel module on a raspberry pi.
The first step for me was to install a fresh new raspbian image on a 8GB card as you need at least 2GB for doing a such operation. The process to reinstall an image is here.
Then you need to configure the Raspbian : expend filesystem, activate ssh, configure keyboard and so on. After the final reboot you can connect to your RPI over ssh and you are ready to start the installation.
Update your distribution and install needed packages
# apt-get update # apt-get upgrade # apt-get install bc
Clone the kernel repo :
# git clone http://github.com/raspberrypi/linux rpi-kernel
This is going to take a while, it is about 2Gb… have a coffee and browse my website 😉
Check if your kernel version match the one cloned
# cd /rpi-kernel # head Makefile VERSION = 3 PATCHLEVEL = 18 SUBLEVEL = 12 # uname -a Linux (none) 3.18.7-v7+...
Here the Version and Patch match, we can clone the current configuration to make a new kernel.
# zcat /proc/config.gz > ./.config
Now we can compile a kernel benefiting of parallelism of RPI2
# make -j 4
If in the version of the kernel you clone some configuration have been added, you will be asked for a few question. To make it simple, just hit ENTER every time. This will take a while … a while … a while … but less than 6 hours on a RPI2
# make modules_install
Install kernel and reboot
# cp /boot/kernel7.img /boot/kernel7.old # cp arch/arm/boot/zImage /boot/kernel7.img # reboot
Check after reboot kernel version
# uname -a Linux (none) 3.18.12-v7+ ...
Create a kernel module
Basically, what we want is to attach an interrupt on a specific GPIO and we want to measure the time between two interrupts. This time is reported to a character device : one line = one time.
The module contains an init part run with insmod
static int __init rfrpi_init(void) { int ret = 0; printk(KERN_INFO "%s\n", __func__); // INITIALIZE IRQ TIME AND Queue Management getnstimeofday(&lastIrq_time); pRead = 0; pWrite = 0; wasOverflow = 0; // register GPIO PIN in use ret = gpio_request_array(signals, ARRAY_SIZE(signals)); if (ret) { printk(KERN_ERR "RFRPI - Unable to request GPIOs for RX Signals: %d\n", ret); goto fail2; } // Register IRQ for this GPIO ret = gpio_to_irq(signals[0].gpio); if(ret < 0) { printk(KERN_ERR "RFRPI - Unable to request IRQ: %d\n", ret); goto fail2; } rx_irqs[0] = ret; printk(KERN_INFO "RFRPI - Successfully requested RX IRQ # %d\n", rx_irqs[0]); ret = request_irq(rx_irqs[0], rx_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED, "rfrpi#rx", NULL); if(ret) { printk(KERN_ERR "RFRPI - Unable to request IRQ: %d\n", ret); goto fail3; } // Register a character device for communication with user space misc_register(&rx433_misc_device); return 0; // cleanup what has been setup so far fail3: free_irq(rx_irqs[0], NULL); fail2: gpio_free_array(signals, ARRAY_SIZE(signals)); return ret; }
The irq is managing a queue of timing in micro-second between two pin status change
/* * The interrupt service routine called on every pin status change */ static irqreturn_t rx_isr(int irq, void *data) { struct timespec current_time; struct timespec delta; unsigned long ns; getnstimeofday(¤t_time); delta = timespec_sub(current_time, lastIrq_time); ns = ((long long)delta.tv_sec * 1000000)+(delta.tv_nsec/1000); lastDelta[pWrite] = ns; getnstimeofday(&lastIrq_time); pWrite = ( pWrite + 1 ) & (BUFFER_SZ-1); if (pWrite == pRead) { // overflow pRead = ( pRead + 1 ) & (BUFFER_SZ-1); if ( wasOverflow == 0 ) { printk(KERN_ERR "RFRPI - Buffer Overflow - IRQ will be missed"); wasOverflow = 1; } } else { wasOverflow = 0; } return IRQ_HANDLED; }
The character device is declared with an handler for reading that returns the content of the queue:
static ssize_t rx433_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { // returns one of the line with the time between two IRQs // return 0 : end of reading // return >0 : size // return -EFAULT : error char tmp[256]; int _count; int _error_count; _count = 0; if ( pRead != pWrite ) { sprintf(tmp,"%ld\n",lastDelta[pRead]); _count = strlen(tmp); _error_count = copy_to_user(buf,tmp,_count+1); if ( _error_count != 0 ) { printk(KERN_ERR "RFRPI - Error writing to char device"); return -EFAULT; } pRead = (pRead + 1) & (BUFFER_SZ-1); } return _count; } static int rx433_open(struct inode *inode, struct file *file) { return nonseekable_open(inode, file); } static int rx433_release(struct inode *inode, struct file *file) { return 0; } static ssize_t rx433_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { return -EINVAL; }
Device is declared in a specific structure :
static struct file_operations rx433_fops = { .owner = THIS_MODULE, .open = rx433_open, .read = rx433_read, .write = rx433_write, .release = rx433_release, }; static struct miscdevice rx433_misc_device = { .minor = MISC_DYNAMIC_MINOR, .name = DEV_NAME, .fops = &rx433_fops, };
The full code and Makefile can be downloaded here : https://bitbucket.org/disk_91-admin/rfrpi/src/625129fecef974059a4f8f4353aa31e1ca48b8c4/rfrpi_src/krfrpi/?at=master
Compile your kernel module
Copy the kernel module source code into any directory and create a Makefile :
ifneq (${KERNELRELEASE},) obj-m = krfrpi.o else KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build MODULE_DIR := $(shell pwd) .PHONY: all all: modules .PHONY:modules modules: ${MAKE} -C ${KERNEL_DIR} SUBDIRS=${MODULE_DIR} modules clean: rm -f *.o *.ko *.mod.c .*.o .*.ko .*.mod.c .*.cmd *~ rm -f Module.symvers Module.markers modules.order rm -rf .tmp_versions endif
Then run the compilation with typing
# make
You can insert the module by typing
# insmod krfrpi.ko
Then check it has been inserted correctly
# lsmod
Module Size Used by
krfrpi 2586 0
Also with dmesg
# dmesg | tail [ 496.798143] rfrpi_init [ 496.798186] Successfully requested RX IRQ # 260
The char device has been created on the filesystem:
# ls -ld /dev/rfrpi crw------- 1 root root 10, 58 déc. 6 10:50 /dev/rfrpi
Now when some Interrupt occurs, you can get timing in micro-second between each of them by running cat on the device :
# cat /dev/rfrpi 8853115 4539218 83 12 8 8 8 4446084
Sources of information
- kernel device example : http://blog.jwhitham.org/2013/06/a-kernel-mode-solution-for-real-time.html
- kernel module examples : http://gpio.kaltpost.de/?p=1699
- Irq latency test : https://sites.google.com/site/hamlinhomeprojects/projects/raspberry-pi-isr
- Irq latency test : https://github.com/gkaindl/linux-gpio-irq-latency-test/blob/master/test-irq-latency.c
- Use RaspberryPi as a frequency meter : http://www.blaess.fr/christophe/2014/01/22/gpio-du-raspberry-pi-mesure-de-frequence/
Seems a pretty solid walk-through.
But I have to ask why? Whats the end goal?
THis is going with my rfrpi stuff ; introduction updated to make more sense !
Great.Fantastic.
The kernel and the driver can be compiled in a standard pc using cross-compiling. The process take me only 20 mins.
Hi,
I use raspberry 2 with debian-jessie.
But I didn’ t found /proc/config.gz
The file is still supplied ?
Regards.
I’ve tríed to do every step and i didnt find also /proc/config.gz?
The so is debian-jessie, any clue?
Thanks
this tuto is based on raspbian
You can just
sudo modprobe configs
to generate it.
Hello there,
Thanx for this great tutorial !
I can’t compile the krfrpi module, here is the error i get when i “make” :
/home/pi/krfrpi/krfrpi.c: In function ‘rfrpi_init’:
/home/pi/krfrpi/krfrpi.c:170:85: error: ‘IRQF_DISABLED’ undeclared (first use in this function)
Any idea ? I’m on RPI2. Thank you.
Change the line 170 of krfrpi.c for:
ret = request_irq(rx_irqs[0], rx_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, “rfrpi#rx”, NULL);
because IRQF_DISABLED is deprecated and it’s not used any more.
First of all, thanks for sharing your amazing work, that was a great help !
I did some tests with both versions, the first version using soft irq and the second using the kernel module. Both are working but have different sensitivity to noise. For example, when I moved slightly the antenna it stopped receiving data with the kernel module version and when I put it back in place both version worked again but with a crappy reception range (10m with no missed data) but as soon as I moved the arduino beside the raspberri pi, the range got bad again (<2m).
Note that I've got better range result while powering the receiver with 3.3v instead of 5v (better meaning ~5m). I tried different antennas (1/4 wave, Helical, straight coil…) with no significant differences.
I believe the raspberri py (or it's power supply and all the wiring around it) is creating lots of noise messing with the data decoding.
Did you encounter these kind of issues ?
Hello, I did not so much once using the right receiver and mounting it on a proper PCB. For sure the RPI is generating noise and having an external antenna and a box around the RPI helps. The RPI frequency is also important if it run at 890MHz or around you may have more noise at 433 MHZ by the way any harmonical frequency may generate a larger noise.
It compiled but I am wondering to know that, which GPIO pin should I use!! Thank in advance
Search for the other post about RF433, there are some with schematics. I’ve written this post a long time ago so I don’t have the answer in mind but you may find it that way.