How to control GPIO via C or Python 3

In Raspberry Pi land, there are a bunch of libraries and userspace tools to interact with GPIOs. Most of them are proprietary and work only for select SoCs made by Broadcom for Raspberry Pi. Many of them are not well designed architecturally and not well coded. Do not ask us to port any of these libraries or tools because they are a waste of time for everyone involved.

Another interface that is used to control the GPIOs is the sysfs interface. This interface exposes the /sys/class/gpio/export and /sys/class/gpio/unexport files which you can echo into to expose GPIOs and control them. This interface is deprecated and will be dropped in the future. We do not recommend continuing to use this interface.

libgpiod is the preferred C interface to interact with the Linux kernel’s GPIO subsystem. Every one of our boards feature GPIOs that can be controlled through this interface. It is well supported, stable, reliable, and well designed.

GPIOs are organized into banks. These banks can be different voltages. The Linux pinctrl subsystem controls the operations of these GPIO banks. The Linux pinctrl subsystem organizes and exposes these banks to userspace as /dev/gpiochipX where a gpiochip can be a composite of multiple banks and individual GPIOs as lines.

Our libretech-wiring-tool offers a handy utility called lgpio. It allows you to quickly lookup the chip and line number of a specific pin on a specific header.

lgpio info HEADER PIN

Most of our boards feature a primary header so you can also leave out the header if you are looking up with pin number on the primary header.

lgpio info PIN

For example, we can lookup pin 3 on AML-S905X-CC Le Potato:

$ lgpio info 3
Chip    Line    sysfs   Name    Pad     Ref     Desc
0       5       506     GPIOAO_5        D13     I2C_SDA_AO      I2C_SDA_AO // I2C_SLAVE_SDA_AO // UART_RX_AO_B

This provides us with all the fields of that pin. The fields are described as follows:

  • Chip - the gpio chip number for libgpiod - /dev/gpiochipX
  • Line - the gpio line number on the chip for libgpiod
  • sysfs - the legacy gpio number for the deprecated sysfs interface - /sys/class/gpio
  • Name - SoC GPIO name
  • Pad - SoC physical pad
  • Ref - Schematic Reference Name
  • Desc - Pin Multiplex Functions

If all we care about is controlling the GPIOs via libgpiod, we can run the following to give us the chip and line numbers respectively:

$ lgpio info 3 gpiod
0       5

Every Linux distro may have a different version of the Python 3 libgpiod. The upstream repository is located here. Click on the version under branch that matches your distro’s version and then click tree, click bindings, click python, and click examples to see example scripts of using GPIOs.

  • Ubuntu 22.04 - 1.6.x
  • Ubuntu 20.04 - 1.4.x
  • Debian/Raspbian 11 - 1.6.x
  • Debian/Raspbian 10 - 1.2.x

gpioget.py

#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later

#
# This file is part of libgpiod.
#
# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
#

'''Simplified reimplementation of the gpioget tool in Python.'''

import gpiod
import sys

if __name__ == '__main__':
    if len(sys.argv) < 3:
        raise TypeError('usage: gpioget.py <gpiochip> <offset1> <offset2> ...')

    with gpiod.Chip(sys.argv[1]) as chip:
        offsets = []
        for off in sys.argv[2:]:
            offsets.append(int(off))

        lines = chip.get_lines(offsets)
        lines.request(consumer=sys.argv[0], type=gpiod.LINE_REQ_DIR_IN)
        vals = lines.get_values()

        for val in vals:
            print(val, end=' ')
        print()

gpioset.py

#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later

#
# This file is part of libgpiod.
#
# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
#

'''Simplified reimplementation of the gpioset tool in Python.'''

import gpiod
import sys

if __name__ == '__main__':
    if len(sys.argv) < 3:
        raise TypeError('usage: gpioset.py <gpiochip> <offset1>=<value1> ...')

    with gpiod.Chip(sys.argv[1]) as chip:
        offsets = []
        values = []
        for arg in sys.argv[2:]:
            arg = arg.split('=')
            offsets.append(int(arg[0]))
            values.append(int(arg[1]))

        lines = chip.get_lines(offsets)
        lines.request(consumer=sys.argv[0], type=gpiod.LINE_REQ_DIR_OUT)
        lines.set_values(values)
        input()
6 Likes

hi thanks for the Tutorial!
i have several questions regarding some of the code used but right now after following along i am getting the following error: module ‘gpiod’ has no attribute 'Chip’
i tried researching what could be causing this but i haven’t been able to figure it out.

Run sudo apt show python3-libgpiod. Make sure it’s using the upstream version and not some pip package. Numerous people created conflicting package names for gpiod so you have to check which one was installed.

after running it this was what i got:
“Package: python3-libgpiod
Version: 1.6.2-1
Priority: optional
Section: python
Source: libgpiod
Maintainer: SZ Lin (林上智) szlin@debian.org
Installed-Size: 89.1 kB
Depends: libc6 (>= 2.17), libgpiod2 (= 1.6.2-1), python3 (<< 3.10), python3 (>= 3.9~)
Homepage: libgpiod/libgpiod.git - C library and tools for interacting with the linux GPIO character device
Download-Size: 21.5 kB
APT-Sources: Index of /debian bullseye/main arm64 Packages
Description: Python bindings for libgpiod (Python 3)
libgpiod encapsulates the ioctl calls and data structures
behind a straightforward API. This new character device
interface guarantees all allocated resources are freed after
closing the device file descriptor and adds several new
features that are not present in the obsolete sysfs interface
(like event polling, setting/reading multiple values at once or
open-source and open-drain GPIOs).
.
This package contains Python 3 bindings for the libgpiod library.”

Did you previously install gpiod via pip3? If so uninstall. pip3 uninstall gpiod

i think i might have.
i went and uninstalled it then rebooted.
i ran

sudo apt show python3-libgpiod

again and got the same as what i posted. when i ran the python code its saying that there is no module named gpiod
do i reinstall using pip3 or another method?

Also sudo pip3 uninstall gpiod.

after running "sudo pip3 uninstall gpiod i got :

Skipping gpiod as it is not installed

Not sure what you did but just tested on the Raspbian image and it worked fine. What image are you using?

$ sudo apt install python3-libgpiod
$ python3 gpioget.py 
Traceback (most recent call last):
  File "/home/dxue/gpioget.py", line 17, in <module>
    raise TypeError('usage: gpioget.py <gpiochip> <offset1> <offset2> ...')
TypeError: usage: gpioget.py <gpiochip> <offset1> <offset2> ...
$ python3 gpioget.py 0 5
1 

I have no idea what happened with me but I had the same issue as you. I rebooted again & ran python3 -m pip uninstall gpiod and then it found it.

Now the script seems to work (at least it does not produce an error).

Following on this, I have had an LED & current limiting resistor hooked up & doing testing on pin 11 & nothing has worked.

When I finally got the Python test code to work (uninstalling the pip installed library —which for some reason would not work on the first couple of tries— did the trick), I thought back to how I also could not get it to work with the command line utilities and tried to put the LED on the pin next to it, pin 12.

It works now with that pin.

Is there any chance that I have a bad header pin?

The things I tried on the command line are:

  • sudo gpioset 0 8=1 (to turn pin 11 on: does not work).
  • sudo gpioset 0 8=0 (to turn pin 11 off: but who can tell?).
  • sudo lgpio set 11=1 (to turn pin 11 on: does not work).
  • sudo lgpio set 11=0 (to turn pin 11 off: but who can tell?).
  • sudo gpioset 0 6=1 (to turn pin 12 on: LED turns on).
  • sudo gpioset 0 6=0 (to turn pin 12 off: LED turns off).
  • sudo lgpio set 12=1 (to turn pin 12 on: LED turns on).
  • sudo lgpio set 12=0 (to turn pin 12 off: LED turns off).

Am I missing something here or do I have a bad pin 11?

Also, the get versions of those commands always seem to return a 1 (and turn off the LED if it was on). Am I lacking any understanding of what that command should do? That’s not what i expected.

See the GPIO map notes for AML-S905X-CC. Pin 11 requires you to move the HDMI CEC jumper.

gpioget returns arbitrary results unless you set either the default bias or pull it up to 3.3V or down to 0V.

1 Like

Correct. That was the issue.

sorry for the late response, seasons greetings everyone.
the issue has been resolved now, after uninstalling then reinstalling gpiod it works.
i have successfully blinked an led on several pins with my test script

thanks much guys for the help!!!

1 Like

A curious observation. I left the Le Potato on for days (probably since sometime around my last comment here so maybe around 3 days or more?) in the off state (I think) and the pin seemed… leaky (not fully on & not fully off)? I tried turning it off & it went off. I tried turning it on again & it went fully on.

What would be the reason for this and how do I address this?

If the GPIO is not set or driven to a specific state, it is in high Z state and could be high or low. This is the nature GPIOs and not specific to our boards.

They can also have default weak pull up or pull down. You can find more material on how GPIOs work online.

This thread is what I was looking for and while I know you’ve answered this questoin, I’m hoping to learn some more about this.

On a RPi3 when I apply power to the device (even before it’s booted up) the pins that I’m using always default to a LOW state. With my newly acquired AML-S905X-CC, it’s the opposite - and that’s not good for my scenario.

On the RPi3, I’ve read that the following is true:

  • GPIOs up to 8: default state is 1 (HIGH, or close to 3.3V).
  • GPIOs 9 to 27: default state is 0 (LOW, or close to 0V).

Is there a way to change the default behavior of the pins on this device, or is the “answer” for me to install a 10k Ohm resistor to ground to pull it down?

Thanks for any assistance here.

You have to create an overlay using the libretech-wiring-tool with changes to the default pin behavior.

See the pinctrl@14 section of aml-s905x-cc.dts.

1 Like

For whomever needs it: to be able to get/set GPIOs without root permissions, it is sufficient to add the user to the dialout group

adduser ubuntu dialout

Do not forget to logout to have the group changes take effect.

I believe that this is because /dev/gpiochip0 and /dev/gpiochip1 have permissions root:dialout by default.
I tested both the gpioset command and the python-periphery Python package, and neither gave me a Permission denied error.

1 Like