README
rpi-lgpio is a compatibility package intended to provide compatibility with the rpi-gpio (aka RPi.GPIO) library, on top of kernels that only support the gpiochip device (and which have removed the deprecated sysfs GPIO interface).
Warning
You cannot install rpi-lgpio and rpi-gpio (aka RPi.GPIO, the library
it emulates) at the same time, in the same Python environment. Both
packages attempt to install a module named RPi.GPIO
and obviously this
will not work.
Contents
Installation
rpi-lgpio is distributed in several formats. The following sections detail installation from a variety of formats. But first a warning:
Warning
You cannot install rpi-lgpio and rpi-gpio (aka RPi.GPIO, the library it
emulates) at the same time, in the same Python environment. Both packages
attempt to install a module named RPi.GPIO
and obviously this will not
work.
apt/deb package
If your distribution includes rpi-lgpio in its archive of apt packages, then you can simply:
$ sudo apt remove python3-rpi.gpio
$ sudo apt install python3-rpi-lgpio
If you wish to go back to rpi-gpio:
$ sudo apt remove python3-rpi-lgpio
$ sudo apt install python3-rpi.gpio
wheel package
If your distribution does not include a “native” packaging of rpi-lgpio, you can also install using pip (preferably in a Python virtual environment):
$ pip uninstall rpi-gpio
$ pip install rpi-lgpio
On some platforms you may need to use a Python 3 specific alias of pip:
$ pip3 uninstall rpi-gpio
$ pip3 install rpi-lgpio
The instructions above assume that rpi-gpio is already installed by pip as well, but this may not be the case. For instance, you may have rpi-gpio installed from, say, apt, but your particular distro doesn’t also include rpi-lgpio. In this case you may need to remove rpi-gpio from apt first:
$ sudo apt remove python3-rpi.gpio
$ pip3 install rpi-lgpio
If you wish to install system-wide with pip, you may need to place sudo
in
front of the pip
(or pip3
) commands too. Please be aware that on modern
versions of pip you will need to explicitly accept the risk of trying to
co-exist apt
and pip
packages as follows:
$ sudo pip3 install --break-system-packages rpi-lgpio
Differences
Many of the assumptions underlying RPi.GPIO – that it has complete access to, and control over, the registers controlling the GPIO pins – do not work when applied to the Linux gpiochip devices. To that end, while the library strives as far as possible to be “bug compatible” with RPi.GPIO, there are differences in behaviour that may result in incompatibility.
Bug Compatible?
What does being “bug compatible” mean? It is not enough for the library to implement the RPi.GPIO API. It must also:
Act, as far as possible, in the same way to the same calls with the same values
Raise the same exception types, with the same messages, in the same circumstances
Break (i.e. fail to operate correctly) in the same way, as far as possible
This last point may sound silly, but a library is always used in unexpected or undocumented ways by some applications. Thus anything that tries to take the place of that library must do more than simply operate the same as the “documented surface” would suggest.
That said, given that the underlying assumptions are fundamentally different this will not always be possible…
Pi Revision
The RPi.GPIO module attempts to determine the model and revision of Raspberry
Pi board that it is running on when the module is imported, raising
RuntimeError
at import time if it finds it is not running on a Raspberry
Pi. rpi-lgpio emulates this behaviour, but this can be inconvenient for certain
situations including testing, but also usage of rpi-lgpio on other single board
computers.
To that end rpi-lgpio permits a Raspberry Pi revision code to be manually
specified via the environment in the RPI_LGPIO_REVISION
value. For example:
$ RPI_LGPIO_REVISION='c03114' python3
Python 3.10.6 (main, Aug 10 2022, 11:40:04) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from RPi import GPIO
>>> GPIO.RPI_INFO
{'P1_REVISION': 3, 'REVISION': 'c03114', 'TYPE': 'Pi 4 Model B',
'MANUFACTURER': 'Sony UK', 'PROCESSOR': 'BCM2711', 'RAM': '4GB'}
>>> exit()
$ RPI_LGPIO_REVISION='902120' python3
Python 3.10.6 (main, Aug 10 2022, 11:40:04) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from RPi import GPIO
>>> GPIO.RPI_INFO
{'P1_REVISION': 3, 'REVISION': '902120', 'TYPE': 'Zero 2 W',
'MANUFACTURER': 'Sony UK', 'PROCESSOR': 'BCM2837', 'RAM': '512M'}
>>> exit()
GPIO Chip
The lgpio library needs to know the number of the /dev/gpiochip
device it
should open. By default this will be calculated from the reported
Pi Revision (which may be customized as detailed in that section). In
practice this means the chip defaults to “4” on the Raspberry Pi Model 5B, and
“0” on all other boards.
You may also specify the chip manually using the RPI_LGPIO_CHIP
environment
variable. For example:
$ ls /dev/gpiochip*
crw------- 1 root root 254, 0 Oct 1 15:00 /dev/gpiochip0
crw------- 1 root root 254, 1 Oct 1 15:00 /dev/gpiochip1
crw------- 1 root root 254, 2 Oct 1 15:00 /dev/gpiochip2
crw------- 1 root root 254, 3 Oct 1 15:00 /dev/gpiochip3
crw-rw----+ 1 root dialout 254, 4 Oct 1 15:00 /dev/gpiochip4
crw------- 1 root root 254, 5 Oct 1 15:00 /dev/gpiochip5
$ RPI_LGPIO_CHIP=5 python3
Python 3.11.5 (main, Aug 29 2023, 15:31:31) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from RPi import GPIO
>>> GPIO.setmode(GPIO.BCM)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3/dist-packages/RPi/GPIO/__init__.py", line 513, in setmode
_chip = _check(lgpio.gpiochip_open(int(chip_num)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/lgpio.py", line 645, in gpiochip_open
return _u2i(handle)
^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/lgpio.py", line 458, in _u2i
raise error(error_text(v))
lgpio.error: 'can not open gpiochip'
This is primarily useful for other boards where the correct gpiochip device is something other than 0.
Alternate Pin Modes
The gpio_function()
function can be used to report the current mode of a
pin. In RPi.GPIO this may return several “alternate” mode values including
SPI
, I2C
, and HARD_PWM
. rpi-lgpio will only ever return
the basic IN
and OUT
values however, as the underlying gpiochip
device cannot report alternate modes.
For example, under RPi.GPIO:
>>> from RPi import GPIO
>>> GPIO.setmode(GPIO.BCM)
>>> GPIO.gpio_function(2) == GPIO.I2C
True
Under rpi-lgpio:
>>> from RPi import GPIO
>>> GPIO.setmode(GPIO.BCM)
>>> GPIO.gpio_function(2) == GPIO.I2C
False
>>> GPIO.gpio_function(2) == GPIO.IN
True
Stack Traces
While every effort has been made to raise the same exceptions with the same messages as RPi.GPIO, rpi-lgpio does raise the exceptions from pure Python so the exceptions will generally include a larger stack trace than under RPi.GPIO. For example, under RPi.GPIO:
>>> from RPi import GPIO
>>> GPIO.setmode(GPIO.BCM)
>>> GPIO.setup(26, GPIO.IN)
>>> GPIO.output(26, 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: The GPIO channel has not been set up as an OUTPUT
Under rpi-lgpio:
>>> from RPi import GPIO
>>> GPIO.setmode(GPIO.BCM)
>>> GPIO.setup(26, GPIO.IN)
>>> GPIO.output(26, 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/dave/projects/rpi-lgpio/rpi-lgpio/RPi/GPIO.py", line 626, in output
_check_output(mode, 'The GPIO channel has not been set up as an OUTPUT')
File "/home/dave/projects/rpi-lgpio/rpi-lgpio/RPi/GPIO.py", line 242, in _check_output
raise RuntimeError(msg)
RuntimeError: The GPIO channel has not been set up as an OUTPUT
Simultaneous Access
Two processes using RPi.GPIO can happily control the same pin. This is simply not permitted by the Linux gpiochip device and will fail under rpi-lgpio. For example, if another process has reserved GPIO26, and our script also tries to allocate it:
>>> from RPi import GPIO
>>> GPIO.setmode(GPIO.BCM)
>>> GPIO.setup(26, GPIO.OUT)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/dave/projects/rpi-lgpio/rpi-lgpio/RPi/GPIO.py", line 569, in setup
initial = _check(lgpio.gpio_read(_chip, gpio))
File "/home/dave/envs/rpi-lgpio/lib/python3.10/site-packages/lgpio.py", line 894, in gpio_read
return _u2i(_lgpio._gpio_read(handle&0xffff, gpio))
File "/home/dave/envs/rpi-lgpio/lib/python3.10/site-packages/lgpio.py", line 461, in _u2i
raise error(error_text(v))
lgpio.error: 'GPIO not allocated'
Debounce
Debouncing of signals works fundamentally differently in RPi.GPIO, and in lgpio (the library underlying rpi-lgpio). Rather than attempt to add more complexity in between users and lgpio, which would also inevitably slow down edge detection (with all the attendant timing issues for certain applications) it is likely preferable to just live with this difference, but document it thoroughly.
RPi.GPIO debounces signals by tracking the last timestamp at which it saw a specified edge and suppressing reports of edges that occur within the specified number of milliseconds after that.
lgpio (and thus rpi-lgpio) debounces by waiting for a signal to be stable for the specified number of milliseconds before reporting the edge.
For some applications, there will be little/no difference other than rpi-lgpio reporting an edge a few milliseconds later than RPi.GPIO would (specifically, by the amount of debounce requsted). The following diagram shows the waveform from a “bouncy” switch being pressed once, along with the positions in time where RPi.GPIO and rpi-lgpio would report the rising edge when debounce of 3ms is requested:
0ms 2ms 4ms 6ms 8ms
| | | | |
| ┌─┐ ┌─┐ ┌─────────────────┐
| │ │ │ │ │ : │
| │ │ │ │ │ : │
───────┘ └─┘ └─┘ : └────────────────────────
: :
: :
RPi.GPIO rpi-lgpio
RPi.GPIO reports the edge at 2ms, then suppresses the edges at 3ms and 4ms because they are within 3ms of the last edge. By contrast, rpi-lgpio ignores the first and second rising edges (because they didn’t stay stable for 3ms) and only reports the third edge at 7ms (after it’s spent 3ms stable).
However, consider this same scenario if debounce of 2ms is requested:
0ms 2ms 4ms 6ms 8ms
| | | | |
| ┌─┐ ┌─┐ ┌─────────────────┐
| │ │ │ │ │ : │
| │ │ │ │ │ : │
───────┘ └─┘ └─┘ : └────────────────────────
: : :
: : :
RPi.GPIO RPi.GPIO rpi-lgpio
In this case, RPi.GPIO reports the switch twice because the third edge is still 2ms after the first edge. However, rpi-lgpio only reports the switch once because only one edge stayed stable for 2ms. Also note in this case, that rpi-lgpio’s report time has moved back to 6ms because it’s not waiting as long for stability.
This implies that you may find shorter debounce periods preferable when working with rpi-lgpio, than with RPi.GPIO. They will still debounce effectively, but will reduce the delay in reporting edges.
One final scenario to consider is a waveform of equally spaced, repeating pulses (like PWM) every 2ms:
0ms 2ms 4ms 6ms 8ms 10ms 12ms
| | | | | | |
| ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌──
| │ │ │ │ │ │ │ │ │ │ │ │ │
| │ │ │ │ │ │ │ │ │ │ │ │ │
───────┘ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘
: : : :
: : : :
RPi.GPIO RPi.GPIO RPi.GPIO RPi.GPIO
If we request rising edge detection with a debounce of 3ms, RPi.GPIO reports half of the edges; it’s suppressing every other edge as they occur within 3ms of the edge preceding them. rpi-lgpio, on the other hand, reports no edges at all because none of them stay stable for 3ms.
PWM on inputs
RPi.GPIO (probably erroneously) permits PWM objects to continue operating on pins that are switched to inputs:
>>> from RPi import GPIO
>>> GPIO.setmode(GPIO.BCM)
>>> GPIO.setup(26, GPIO.OUT)
>>> p = GPIO.PWM(26, 1000)
>>> p.start(75)
>>> GPIO.setup(26, GPIO.IN)
>>> p.stop()
>>> p.start(75)
>>> p.stop()
This will not work under rpi-lgpio:
>>> from RPi import GPIO
>>> GPIO.setmode(GPIO.BCM)
>>> GPIO.setup(26, GPIO.OUT)
>>> p = GPIO.PWM(26, 1000)
>>> p.start(75)
>>> GPIO.setup(26, GPIO.IN)
>>> p.stop()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/dave/projects/rpi-lgpio/rpi-lgpio/RPi/GPIO.py", line 190, in stop
lgpio.tx_pwm(_chip, self._gpio, 0, 0)
File "/home/dave/envs/rpi-lgpio/lib/python3.10/site-packages/lgpio.py", line 1074, in tx_pwm
return _u2i(_lgpio._tx_pwm(
File "/home/dave/envs/rpi-lgpio/lib/python3.10/site-packages/lgpio.py", line 461, in _u2i
raise error(error_text(v))
lgpio.error: 'bad PWM micros'
Though note that the error occurs when the PWM
object is next acted
upon, rather than at the point when the GPIO is switched to an input.
API Reference
The API of rpi-lgpio (naturally) follows that of rpi-gpio (aka RPi.GPIO) as closely as possible. As such the following is simply a re-iteration of that API.
Initialization
- RPi.GPIO.setmode(new_mode)[source]
Set up the numbering mode to use for the pins on the board. The options for new_mode are:
If a numbering mode has already been set, and new_mode is not the same as the result of
getmode()
, aValueError
is raised.- Parameters:
new_mode (int) – The new numbering mode to apply
- RPi.GPIO.getmode()[source]
Get the numbering mode used for the pins on the board. Returns
BOARD
,BCM
orNone
.
- RPi.GPIO.setup(chanlist, direction, pull_up_down=20, initial=None)[source]
Set up a GPIO channel or iterable of channels with a direction and (optionally, for inputs) pull/up down control, or (optionally, for outputs) and initial state.
The GPIOs to affect are listed in chanlist which may be any iterable. The direction is either
IN
orOUT
.If direction is
IN
, then pull_up_down may specify one of the valuesPUD_UP
to set the internal pull-up resistor,PUD_DOWN
to set the internal pull-down resistor, or the defaultPUD_OFF
which disables the internal pulls.If direction is
OUT
, then initial may specify zero or one to indicate the initial state of the output.- Parameters:
Pin Usage
- RPi.GPIO.input(channel)[source]
Input from a GPIO channel. Returns 1 or 0.
This can also be called on a GPIO output, in which case the value returned will be the last state set on the GPIO.
- RPi.GPIO.output(channel, value)[source]
Output to a GPIO channel or list of channels. The value can be the integer
LOW
orHIGH
, or a list of integers.If a list of channels is specified, with a single integer for the value then it is applied to all channels. Otherwise, the length of the two lists must match.
Edge Detection
- RPi.GPIO.wait_for_edge(channel, edge, bouncetime=None, timeout=None)[source]
Wait for an edge on the specified channel. Returns channel or
None
if timeout elapses before the specified edge occurs.Note
Debounce works significantly differently in rpi-lgpio than it does in rpi-gpio; please see Debounce for more information on the differences.
- RPi.GPIO.add_event_detect(channel, edge, callback=None, bouncetime=None)[source]
Start background edge detection on the specified GPIO channel.
If callback is specified, it must be a callable that will be executed when the specified edge is seen on the GPIO channel. The callable must accept a single parameter: the channel on which the edge was detected.
Note
Debounce works significantly differently in rpi-lgpio than it does in rpi-gpio; please see Debounce for more information on the differences.
- Parameters:
channel (int) – The board pin number or BCM number depending on
setmode()
to watch for changescallback (callable or None) – The callback to run when an edge is detected; must take a single integer parameter of the channel on which the edge was detected
bouncetime (int or None) – Time (in ms) used to debounce signals
- RPi.GPIO.add_event_callback(channel, callback)[source]
Add a callback to the specified GPIO channel which must already have been set for background edge detection with
add_event_detect()
.
- RPi.GPIO.event_detected(channel)[source]
Returns
True
if an edge has occurred on the specified channel since the last query of the channel (if any). Querying this will also reset the internal edge detected flag for this channel.The channel must previously have had edge detection enabled with
add_event_detect()
.
Miscellaneous
PWM
- class RPi.GPIO.PWM(channel, frequency)[source]
Initializes and controls software-based PWM (Pulse Width Modulation) on the specified channel at frequency (in Hz).
Call
start()
andstop()
to generate and stop the actual output respectively.ChangeFrequency()
andChangeDutyCycle()
can also be used to control the output.Note
Letting the
PWM
object go out of scope (and be garbage collected) will implicitly stop the PWM.- ChangeDutyCycle(dc)[source]
Changes the duty cycle (percentage of the time that the pin is “on”) to dc.
Constants
- RPi.GPIO.RPI_INFO
A dictionary that provides information about the model of Raspberry Pi that the library is loaded onto. Includes the following keys:
- P1_REVISION
The revision of the P1 header. 0 indicates no P1 header (typical on the compute module range), 1 and 2 vary on the oldest Raspberry Pi models, and 3 is the typical 40-pin header present on all modern Raspberry Pis.
- REVISION
The hex board revision code as a
str
.- TYPE
The name of the Pi model, e.g. “Pi 4 Model B”
- MANUFACTURER
The name of the board manufacturer, e.g. “Sony UK”
- PROCESSOR
The name of the SoC used on the board, e.g. “BCM2711”
- RAM
The amount of RAM installed on the board, e.g. “4GB”
The board revision can be overridden with the
RPI_LGPIO_REVISION
environment variable; see Pi Revision for further details.
- RPi.GPIO.OUT
Used with
setup()
to set a GPIO to an output, andgpio_function()
to report a GPIO is an output
- RPi.GPIO.IN
Used with
setup()
to set a GPIO to an input, andgpio_function()
to report a GPIO is an input
- RPi.GPIO.HARD_PWM
- RPi.GPIO.SERIAL
- RPi.GPIO.I2C
- RPi.GPIO.SPI
Used with
gpio_function()
to indicate “alternate” modes of certain GPIO pins.Note
In rpi-lgpio these values will never be returned as the kernel device cannot report if pins are in alternate modes.
- RPi.GPIO.RISING
Used with
wait_for_edge()
andadd_event_detect()
to specify that rising edges only should be sampled
- RPi.GPIO.FALLING
Used with
wait_for_edge()
andadd_event_detect()
to specify that falling edges only should be sampled
- RPi.GPIO.BOTH
Used with
wait_for_edge()
andadd_event_detect()
to specify that all edges should be sampled
Changelog
Release 0.4 (2023-10-03)
Add compatibility with Raspberry Pi 5 (auto-selection of correct gpiochip device)
Add ability to override gpiochip selection; see GPIO Chip
Convert bouncetime -666 to
None
(bug compatibility, which also ensures this should work with GPIO Zero’s rpigpio pin driver)Fix
pull_up_down
default onsetup()
Fix changing
pull_up_down
of already-acquired inputEnsure
PWM.stop()
is idempotent
Release 0.3 (2022-10-14)
Permit override of Pi revision code; see Pi Revision
Document alternate pin modes in Differences
Release 0.2 (2022-10-14)
Add support for
RPI_REVISION
andRPI_INFO
globals
Release 0.1 (2022-10-14)
Initial release
License
The MIT License (MIT)
Copyright (c) 2022 Dave Jones
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.