GPIO - Erlang GPIO interface

Hi,

I just published an Erlang GPIO library (for Linux) here: gpio | Hex

We have been using it for some customer projects that have been in the field for a while now, and it was written using the modern gpio interface on Linux, not the old one based on sysfs which allows for cleaner APIs without resource leaks.

We also have an SPI library that needs to be extracted, an i2c one can be easily built based on what the SPI library does. I can try to get that one extracted and published if that would be helpful.

Happy to help with any of these, just let me know.

9 Likes

There is not much documentation, but hopefully the README here can get you started:

2 Likes

Dear Ali,

that seems to be exactly what I have been looking for!
In the meantime since I wrote this posting I did a little port of circuits_gpio to erlang,
this is working so far.
As I just saw, it uses the sysfs interface. I was not aware of the deprecation yet.

I tried the library on a RPi4, but was not very successful yet.

I can open a chip, but opening lines does not work yet on my side:

I want to use GPIO16, 20 and 21. According to gpioinfo, the offsets should be identical to the
numbers in the GPIO name:

pi@r40:~ $ gpioinfo 
gpiochip0 - 58 lines:
	line   0:     "ID_SDA"       unused   input  active-high 
[..]
	line  16:     "GPIO16"       unused   input  active-high 
	line  17:     "GPIO17" "onewire@11"  output  active-high [used open-drain]
	line  18:     "GPIO18"       unused   input  active-high 
	line  19:     "GPIO19"       unused   input  active-high 
	line  20:     "GPIO20"       unused   input  active-high 
	line  21:     "GPIO21"       unused   input  active-high 
[..]
1> {ok,Chip0}=gpio:open_chip("/dev/gpiochip0").
{ok,#Ref<0.2094800486.2528772108.122490>}
2> {ok, L3} = gpio:open_lines(Chip0, [16,20,21], [output], [0,0,0], "my-application"). 
** exception error: bad argument
     in function  gpio:open_lines_nif/5
        called as gpio:open_lines_nif(#Ref<0.2094800486.2528772108.122490>,
                                      [16,20,21],
                                      [output],
                                      [0,0,0],
                                      "my-application")

It seems to fail in enif_get_resource.
Have you tried the library on a Raspberry, by any chance?
If not, I could try some changes, if you have an idea…

If I use the gpiod tools, all works fine (also with gpiochip0), so the hardware seems to be fine.

1 Like

This was used on an i.MX8 system as well as an RPI CM3, I extracted the code from a project so I might have messed something up during the extraction. I am not able to test immediately, but it shouldn’t be hard to figure out the issue/fix.

The easiest could be to sprinkle some printf calls before every enif_make_badarg in gpio_nif.c

1 Like

ok, so in principle it should also work on the RPi4.
I will play around some more.

Thanks!

1 Like

I realise we never really tested the opening of multiple lines for reading/writing. I just pushed a fix to the git repo, but haven’t made a new release yet.

1> {ok, Chip0} = gpio:open_chip("/dev/gpiochip0").
{ok,#Ref<0.2675552943.3685613578.35853>}
2> {ok, L3} = gpio:open_lines(Chip0, [16,20,21], [output], [0,0,0], "my-application").
{ok,#Ref<0.2675552943.3685613578.35855>}

Checking using debugfs:

cat /sys/kernel/debug/gpio | grep my-application
 gpio-16  (GPIO16              |my-application      ) out lo
 gpio-20  (GPIO20              |my-application      ) out lo
 gpio-21  (GPIO21              |my-application      ) out lo

From the erlang shell:

3> gpio:write_lines(L3, {1,0,0}).
ok

Then checking again using debugfs

cat /sys/kernel/debug/gpio | grep my-application
 gpio-16  (GPIO16              |my-application      ) out hi
 gpio-20  (GPIO20              |my-application      ) out lo
 gpio-21  (GPIO21              |my-application      ) out lo

Please let me know how it goes on your end so I can publish a new version on hex.pm

2 Likes

Good morning!

Thank you for the update and the info about the debugfs info!
I think I found the reason for yesterday’s problems. I was testing in parallel with my circuits_gpio_erl library, which uses sysfs.
And it seems that the gpio interface respects that a line is already in use by another driver.
That seems reasonable.

On the other hand, a GPIO line, which has been used by sysfs is not freed when the sysfs program ends, even when I use gpio:close().

This might not be the case in general, that’s just what I have seen on my RPi4.

So I think I will switch my application to use your library and run some long-duration stress tests.
Yesterday in the beginning I experienced some seg faults. Since you used the library in production I guess that were some user errors as well on my side.

1 Like

That’s the main problem with the sysfs API and the main reason for the existence of the gpio chardev Linux API:

The sysfs API treated the GPIO access as a global shared resource that was not attached to any particular Linux process, this meant that when for example a program who used the sysfs GPIO interface crashed, the GPIOs were left in an undefined state, whereas with the chardev API, the GPIOs get reset to their initial state upon program termination. I personally avoid using the gpio sysfs API unless working with old Linux kernels (< 4.8).

Please let me know if you experience any segfaults as I would like to get them fixed. To give you a bit more background about the gpio library I published yesterday, It has been in use since 2018 in different shapes and forms, we had different “forks” of it spread out across multiple projects (Yes, I know…). What I tried doing some time ago was to consolidate these forks into a single library.

I will go ahead and release a 0.6.1 that fixes the multi line issue.

1 Like

Thanks for this library! Works on an old Rpi2B as a charm.
Had to install the cli tools first with:

apt-get install gpiod libgpiod-dev libgpiod-doc

I would be very interested in the SPI lib as well.

2 Likes

Glad to hear :slight_smile: However, the library doesn’t depend on libgpiod, as it directly uses the necessary ioctl calls to interact with the device, so you don’t need to install them unless you want to use the debugging tools.

I will see what I can do, hopefully I can get the SPI and USB libraries open sourced as well.

2 Likes

it is not very clear: what is the problem with sysfs? Why have you decided to make a NIF instead of using 'old 'good plain files?

1 Like

from https://www.kernel.org/doc/html/latest/admin-guide/gpio/sysfs.html:

THIS ABI IS DEPRECATED, THE ABI DOCUMENTATION HAS BEEN MOVED TO Documentation/ABI/obsolete/sysfs-gpio AND NEW USERSPACE CONSUMERS ARE SUPPOSED TO USE THE CHARACTER DEVICE ABI. THIS OLD SYSFS ABI WILL NOT BE DEVELOPED (NO NEW FEATURES), IT WILL JUST BE MAINTAINED.

The reason for deprecation is that it is inherently unsafe. The new chardev based interface ensures:

  1. exclusive access to a GPIO pin(s), including restricting access to GPIO pins that have already been claimed by in-kernel device drivers;
  2. restoration of initial state whenever the owning process terminates.

I would have avoided using a NIF, if there was a way of using ioctl and select/poll/... calls from erlang.

1 Like

wow! Thank you for this documentation link. I haven’t seen it yet =(

1 Like

Hello @asabil

Your library looks good. I’ll update one of my blog posts related to embedded buses library state (Buses | BEAM Mignon).

We (the company that I’m working for) had a similar approach too. One of our main source of pain was reference counting when open/close gpio lines. e.g.: when the code has many process using the same gpiochip with many opened lines from different process.

BTW, there is an EEF WG for such libraries which interfacing with buses (likes I2C, SPI, GPIO, …) here: Erlang Ecosystem Foundation - Supporting the BEAM community we could start some discussion around there.

3 Likes

Thank you :slight_smile:

Out of curiosity, why do you share the gpiochip? why not open the gpiochip many times from the different processes that needs it? You are only restricted when it comes to gpiolines.

How can one join such working group? I would love to help with whatever I can.

1 Like

That is easy. Please, follow the procedures from here: Erlang Ecosystem Foundation - Supporting the BEAM community it’s just about getting a slack invite. Once get there you can join the Embedded WG (or any other that you want to). At the main EEF website, bottom page, there is a calendar with all meetings from all WGs.

That is a nicer approach as well. I didn’t consider that. It could solve the question. Thanks.

1 Like