Home

Awesome

What's this?

Firmata_mod is a collection of Linux kernel modules that allow you to access GPIO pins, I2C and SPI devices on an Arduino running the Firmata firmware in such a way, that they appear as GPIO/SPI/I2C devices on the host computer connecting to the Arduino.

Installation

Install build dependencies to build external kernel modules. On Debian you can simply install the linux headers and all necessary build dependencies such as the compiler used to build the running kernel will be pulled in, too:

$ sudo apt-get install linux-headers-`uname -r`

In the firmata_mod directory do:

$ make
$ sudo make modules_install

You can ignore the warnings regarding BTF generation.

Now you can load the firmata kernel module:

$ sudo modprobe firmata_mod [firmata_port=TTY_PORT] [baud_rate=N]

where TTY_PORT is your tty-port which connects to the Arduino. The default is "ttyUSB0". This must match the Port setting under Tools in the Arduino configuration menu, but without the "/dev/" part. The optional baud_rate argument gives the baud_rate. By default it is 57600 like in StandardFirmata, ConfigurableFirmata would use 115200. The modprobe command above may take several seconds to return, since the module verifies the Arduino with Firmata firmware is correctly configured. You can verify whether the drivers are loaded using lsmod:

$ lsmod | grep firmata
spi_firmata            16384  0
i2c_firmata            16384  0
gpio_firmata           16384  0
firmata_mod            20480  3 spi_firmata,i2c_firmata,gpio_firmata

You can unload the driver again with:

$ sudo rmmod gpio_firmata spi_firmata i2c_firmata firmata_mod

GPIO-support

In order to get more information about the now available GPIO lines, you can install the gpiod tools. Alternatively, the information is also directly available in the /sys file-system. In order to test gpio-access via python, the python3-libgpiod library is very convenient:

$ sudo apt install gpiod python3-libgpiod

After loading firmata_mod, a new GPIO chip should be available with the 20 pins of the Arduino:

$ sudo gpiodetect
gpiochip0 [INT34C8:00] (340 lines)
gpiochip1 [ftdi-cbus] (4 lines)
gpiochip2 [firmata-gpio] (20 lines)

The on-board LED of the Arduino which is by default using pin 13 can then be enabled/disabled with:

$ sudo gpioset gpiochip2 13=1
$ sudo gpioset gpiochip2 13=0

You can also directly manipulate GPIOs from within the /sys filesystem

$ cd /sys/class/gpio
$ ls -l
 total 0
 --w------- 1 root root 4096 Aug 29 08:31 export
 lrwxrwxrwx 1 root root    0 Aug 29 08:31 gpiochip660 -> ../../devices/platform/firmata.0/firmata-gpio.3.auto/gpio/gpiochip660
 lrwxrwxrwx 1 root root    0 Aug 29 08:31 gpiochip680 -> ../../devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/gpio/gpiochip680
 lrwxrwxrwx 1 root root    0 Aug 29 07:43 gpiochip684 -> ../../devices/platform/INT34C8:00/gpio/gpiochip684
 --w------- 1 root root 4096 Aug 29 08:31 unexport

$ echo 677 > export
$ echo "out" > gpio677/direction 
$ echo 1 > gpio677/value 
$ echo 0 > gpio677/value 

Be aware that pin 13 (which is typically the on-board LED) may be assigned to SPI when running ConfigurableFirmata. That means that with the spi-firmata module loaded setting that pin via gpio will not work.

I2C-support

If you want to work with I2C devices, you need to make sure i2c is accessible through userspace. Verify that the "i2c-tools" package is installed, otherwise install it with

$ sudo apt install i2c-tools

which will make udev rules for the i2c-devices available and provide userspace tools to communicate with i2c devices. You also need to load the i2c_dev module, which creates i2c-devices such as /dev/i2c-1 with the help of the udev rules:

$ sudo modprobe i2c_dev
$ ls -l /dev | grep i2c
crw-rw----  1 root   i2c      89,     0 Aug 15 14:28 i2c-0
crw-rw----  1 root   i2c      89,     1 Aug 15 14:28 i2c-1
crw-rw----  1 root   i2c      89,    10 Aug 15 14:28 i2c-10
crw-rw----  1 root   i2c      89,    11 Aug 15 14:28 i2c-11
crw-rw----  1 root   i2c      89,    12 Aug 15 14:28 i2c-12
crw-rw----  1 root   i2c      89,    13 Aug 15 14:28 i2c-13
crw-rw----  1 root   i2c      89,    14 Aug 15 14:28 i2c-14
crw-rw----  1 root   i2c      89,    15 Aug 15 14:28 i2c-15
crw-rw----  1 root   i2c      89,    16 Aug 15 14:28 i2c-16
crw-rw----  1 root   i2c      89,     2 Aug 15 14:28 i2c-2
crw-rw----  1 root   i2c      89,     3 Aug 15 14:28 i2c-3
crw-rw----  1 root   i2c      89,     4 Aug 15 14:28 i2c-4
crw-rw----  1 root   i2c      89,     5 Aug 15 14:28 i2c-5
crw-rw----  1 root   i2c      89,     6 Aug 15 14:28 i2c-6
crw-rw----  1 root   i2c      89,     7 Aug 15 14:28 i2c-7
crw-rw----  1 root   i2c      89,     8 Aug 15 14:28 i2c-8
crw-rw----  1 root   i2c      89,     9 Aug 15 14:28 i2c-9

A python test-script can be found in the test source directory, which expects an SHT21 temperature / relative humidity sensor connected to the primary I2C bus on the Arduino. It uses the smbus python package, which can be installed under debian with

$ sudo apt install python3-smbus
$ cd test
$ sudo ./test_sht21.py
1692102741 20.8 86.3

Note, that due to the permissions under /dev/ for the i2c devices, the user must either be in the i2c group, or the command must be executed as "root". Evidently for the the python implementation of smbus to work, the i2c_dev kernel module will need to be loaded, otherwise you will get something like:

    bus = smbus.SMBus(16)
          ^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory

The bus-number give (16 in the above case) can be learned from running

$ i2cdetect -l
i2c-0   i2c             Synopsys DesignWare I2C adapter         I2C adapter
i2c-1   smbus           SMBus I801 adapter at efa0              SMBus adapter
i2c-2   i2c             Synopsys DesignWare I2C adapter         I2C adapter
i2c-3   i2c             Synopsys DesignWare I2C adapter         I2C adapter
i2c-4   i2c             Synopsys DesignWare I2C adapter         I2C adapter
i2c-5   i2c             Synopsys DesignWare I2C adapter         I2C adapter
i2c-6   i2c             i915 gmbus dpa                          I2C adapter
i2c-7   i2c             i915 gmbus dpb                          I2C adapter
i2c-8   i2c             i915 gmbus dpc                          I2C adapter
i2c-9   i2c             i915 gmbus tc1                          I2C adapter
i2c-10  i2c             i915 gmbus tc2                          I2C adapter
i2c-11  i2c             i915 gmbus tc3                          I2C adapter
i2c-12  i2c             i915 gmbus tc4                          I2C adapter
i2c-13  i2c             i915 gmbus tc5                          I2C adapter
i2c-14  i2c             i915 gmbus tc6                          I2C adapter
i2c-15  i2c             AUX B/DDI B/PHY B                       I2C adapter
i2c-16  i2c             firmata-i2c-firmata.0-0                 I2C adapter

It is also possible to use existing kernel modules with I2C hardware, to use e.g. the in-kernel SHT21 module, do:

/sys/bus/i2c/devices/i2c-16# echo sht21 0x40 >new_device
# dmesg | tail
 [ 6735.579122] i2c i2c-16: new_device: Instantiated device sht21 at 0x40
/sys/bus/i2c/devices/i2c-16/16-0040/hwmon/hwmon4# cat temp1_input 
  25404

Which means it is 25.4 degrees warm.

SPI-Support

To test SPI, again we first need a udev rule for the creation of an spidev device under /dev/. You can create one manually (note that on a Raspberry Pi such rules are already present under "99-com.rules"), while on a Debian PC I created one manually:

$ cat /lib/udev/rules.d/60-spidev.rules
$ SUBSYSTEM=="spidev", KERNEL=="spidev[0-9]*.[0-9]*", GROUP="spi", MODE="0660"

Make sure there is an spi group available. To reload the udev rules without a reboot, do:

$ sudo udevadm control --reload-rules && sudo udevadm trigger

In order for the spi kernel module to be able to create an spidev user-space device, the driver needs to be listed in the spidev_spi_ids[] in the spidev module, see https://docs.kernel.org/spi/spidev.html

The easiest approach here is to copy spidev.c from your kernel sources into the firmata_mod directory, add it among the modules in the Makefile and compile them along with the firmata modules, after having added the line

static const struct spi_device_id spidev_spi_ids[] = {
	[...]
	{ .name = "spi-firmata" },
	{},
};

You can query the spi device using the spi-tools and python3-spidev packages.

First, figure out which spi-device is being made available:

$ cd /sys/bus/spi/devices
$ ls -l
total 0
lrwxrwxrwx 1 root root 0 Sep  3 16:07 spi1.0 -> ../../../devices/platform/firmata.0/firmata-spi.5.auto/spi_master/spi1/spi1.0

This means that firmata makes spi-device 0 on bus 1 available (device spi1.0) in userspace through the spidev module.