How to control GPIO via C or Python 3

Yeah… my official job title is “Woodsman” and I have very little experience with SBC’s, so “Device Tree Overlays for Noobs” would be greatly appreciated.

I know it’s a lot of work to put tutorials together, so even some external links to the information would be super helpful in this stage of my journey. I’m happy to learn and contribute the usefulness of this equipment, however, I often have difficulty trying to figure out if the resource I’m looking at has any relevance to the board I have.

Also, for me and hobbyists like me, the more simple and familiar way is sometimes preferable to the “recommended” way, so I don’t see any harm in showing both. I’m just getting started with C++, but I know how to blink a light or read a button in CircuitPython right now. I was able to write a functional script in minutes, and one that I can port to a micro-controller just as fast. Using Blinka also gives me access to modules written for a huge array of sensors, many of which I have lying around my desk. That’s much more valuable for my current use-case than a tangle of curly-braces that’ll take me all night to figure out. Just don’t let that make you think I don’t want to eventually learn to do things the “right” way too.

Thanks Guys!

1 Like

I’ve been using gpiod on Armbian and it works ok. I found it easier than other options, being a beginner. Maybe you could try it on Raspbian.

For your application im guessing you’ll want to set a pull-down bias and either use polling or listen for an edge event.

To make my life easier I made my own python library for executing gpiod instructions:

Hope this helps.

1 Like

Poorly coded libraries are a dime a dozen. They do no one any service. It’s best to use the right tool for the right job.

I just managed to wrap my head around this library. I’s not as straightforward as I’m used to, but I feel like I understand the hardware a little better for the challenge.

Here is a simple blink script written with libgpiod:

#!/usr/bin/env python3

#this script will blink an LED attached to pin 22 (chip# 1, linux# 79)


import gpiod
import time

# set the chip number
chip = gpiod.Chip('1')
# Set the line number (linux number from the pin map)
lines = chip.get_lines([79]) # set the line number
# set direction and default value
lines.request(consumer='spam', type=gpiod.LINE_REQ_DIR_OUT, default_vals=[0]) 

# Not sure what 'consumer' does, so I just set it to nonsense. 
# The guide I followed did likewise.


# here's the loop
for i in range(10):
	lines.set_values([1])
	time.sleep(0.25)
	lines.set_values([0])
	time.sleep(0.25)



1 Like

I have a piece of python code that runs on pi3b using pin 15 for button presses.

I tried to use it on my potato only to discover that the default functionality of pin 15 on the potato appears to be reset the computer?

According to the spreadsheet: AML-S905X-CC-V1.0A Headers - Google Sheets

pin 15 has multiple possible uses. one of which being: GPIOAO_14

Can someone explain how to change the behavior of the pins? Since it’s listed as a possible use, I am assuming that this does not require writing a custom overlay.

Thanks,
C.

Here’s a slightly more complex and complete example with inputs and cleanup:

#!/usr/bin/env python3

# This script will blink an LEDs attached to pins 32, 36, 38, and 40 and 
# accept input from pins 35 and 37. The line number is the 'Linux#' from
# the Pin Map. Buttons must be pulled to ground. Use appropriate
# current limiting resistors with your LEDs.
# Pin 37 is you quit button. 

import gpiod
import time

chip = gpiod.Chip('1') # set the chip number
linesOut = chip.get_lines([95, 81, 82, 83]) # set the line numbers
linesIn = chip.get_lines([86, 84]) 
# set direction and value
linesOut.request(consumer='spam', type=gpiod.LINE_REQ_DIR_OUT, default_vals=[0, 0, 0, 0]) 
linesIn.request(consumer='spam', type=gpiod.LINE_REQ_DIR_IN, default_vals=[0, 0])
# Not sure what 'consumer' does, so I just set it to nonesense. 
# The guide I followed did the likewise.


# define some functions for superior blinkage
# set_value uses the same order we set in linesOut, 1 = 3.3V, 0 = 0V
def chase():
	linesOut.set_values([1, 0, 0, 0])
	time.sleep(0.25)
	linesOut.set_values([0, 1, 0, 0])
	time.sleep(0.25)
	linesOut.set_values([0, 0, 1, 0])
	time.sleep(0.25)
	linesOut.set_values([0, 0, 0, 1])
	time.sleep(0.25)

def sideFlash():
	linesOut.set_values([1, 1, 0, 0])
	time.sleep(0.25)
	linesOut.set_values([0, 0, 1, 1])
	time.sleep(0.25)
	linesOut.set_values([1, 1, 0, 0])
	time.sleep(0.25)
	linesOut.set_values([0, 0, 1, 1])
	time.sleep(0.25)
	

try:
	while True:
		# quit if you press the big red button
		if linesIn.get_values()[1]:
			break
			
		# Play one animation if pressed, the other if not.
		if linesIn.get_values()[0]:
			chase()
		else:
			sideFlash()
	
finally: 
	# Turn the lights off when you're not using them, just like Mom said.
	linesOut.set_values([0, 0, 0, 0])
	linesIn.release()
	linesOut.release()
	chip.close()

I know this isn’t the recommended way to handle inputs, and there is a way to enable internal pull up/down resistors, but that is beyond my skill level at the moment. Hope this is helpful, I know a few people were looking for blink and input examples.

1 Like

I’m not sure, I’m just getting started with device tree stuff, but I’m pretty sure you’d need to write a overlay. It’s not covered in the sample overlays, and changing the behavior of the pins pretty much always needs one.

I have a fresh install of ubuntu-22.04.1-preinstalled-base-arm64+aml-s905x-cc.img
willi@potato:~/shared/piezo$ apt list --installed | grep gpio

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

gpiod/jammy,now 1.6.3-1build1 arm64 [installed]
libgpiod2/jammy,now 1.6.3-1build1 arm64 [installed,automatic]
libretech-gpio/unknown,now 0.1.2 all [installed]

Using your gpioget.py example above I get no module named gpiod.
willi@potato:~/shared/piezo$ python3 gpioget.py Traceback (most recent call last):
File “/home/willi/shared/piezo/gpioget.py”, line 12, in
import gpiod
ModuleNotFoundError: No module named ‘gpiod’

I did willi@potato:~/shared/piezo$ python3 -m pip uninstall
/usr/bin/python3: No module named pip

Your gpioget.py will work if I sudo apt install python3-libgpiod.

THIS IS A DEFECT IN YOUR DISTRIBUTION there is no useable python library, even though apt indicates libgpiod is installed (C not python?). Maybe its just on the python path or not linked properly. Bottom line it does not work as delivered and insufficient information here for workaround.

Yeah, I’m pretty sure that’s the C library you’re seeing. I had to install it before use as well, so I think the all caps screaming about defects may have been uncalled-for.

Yeah,on my odroid n2+:

angus@odroidn2:~$ apt list --installed | grep gpio

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

gpiod/jammy,now 1.6.3-1build1 arm64 [installed]
libgpiod2/jammy,now 1.6.3-1build1 arm64 [installed,automatic]
angus@odroidn2:~$ sudo apt install python3-libgpiod
[sudo] password for angus: 
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed:
  python3-libgpiod
0 upgraded, 1 newly installed, 0 to remove and 1 not upgraded.
Need to get 21.6 kB of archives.
After this operation, 89.1 kB of additional disk space will be used.
Get:1 http://ports.ubuntu.com jammy/universe arm64 python3-libgpiod arm64 1.6.3-1build1 [21.6 kB]
Fetched 21.6 kB in 0s (44.9 kB/s)           
Selecting previously unselected package python3-libgpiod:arm64.
(Reading database ... 174424 files and directories currently installed.)
Preparing to unpack .../python3-libgpiod_1.6.3-1build1_arm64.deb ...
Unpacking python3-libgpiod:arm64 (1.6.3-1build1) ...
Setting up python3-libgpiod:arm64 (1.6.3-1build1) ...
angus@odroidn2:~$ apt list --installed | grep gpio

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

gpiod/jammy,now 1.6.3-1build1 arm64 [installed]
libgpiod2/jammy,now 1.6.3-1build1 arm64 [installed,automatic]
python3-libgpiod/jammy,now 1.6.3-1build1 arm64 [installed]
angus@odroidn2:~$ 

python3-libgpiod is indeed a separate package. There is no defect.

Hello everyone, could someone help me in controlling volt meter? Which library should I use, and how do I do that? I tried periphery, but I count to get it to work. Volt meter gives different voltage depending on the voltage that is getting measured. I just can’t find a way to measure that voltage.

Neither this board or Raspberry Pi is not capable of reading a 0-5V analog input. Use an Arduino.

I just ran the libgpiod blink example I posted above on my Odroid n2+ with Armbian, all I had to do was look up the chip and line numbers, and change them in the code. No problems whatsoever, had LEDs blinking in like 2 minutes. The argument in favor of libgpiod for portability reasons has definitely been validated in my mind.

Thank you to @librecomputer for exposing me to this method, and encouraging us to learn to do things the proper way. It’s well worth the learning curve.

I’m glad to have started out on my SBC adventure here, and not some other fruity place. I never would have bothered with this library had it not been for the Pi shortage.

1 Like

I am using the Renegade, and this is what I get for gpioget.py 0 5 :

AttributeError: ‘gpiod.Chip’ object has no attribute ‘getlines’

This is a fresh install of the provided Raspbian image and have only done:
sudo apt install python3-libgpiod

Please read the OP more carefully. You need to use the gpioget.py that matches your userspace library version.

Does this work with Raspbian 12?

It works across every distro the same way.

Although it worked when I just read lines (direct voltage reads whether a gpio line was 1 or 0), it was doing something weird like not releasing the line correctly (system interrupts occur but then don’t go away):

def run(self) -> None:
        """Monitors the GPIO line for tap events and processes them.

        Continuously checks a specific GPIO line for voltage changes. If a change is detected,
        interprets it as a tap event and triggers the tap handling process.
        """
        # TODO: Create chip/pin mapping in gpio_config
        chip_path = '/dev/gpiochip1'
        line_offset = 26

        with gpiod.request_lines(
            path=chip_path,
            consumer='watch-touch-sensor-line',
            config={
                line_offset:
                    gpiod.LineSettings(
                        edge_detection=Edge.RISING,
                        bias=Bias.PULL_DOWN,
                        debounce_period=timedelta(seconds=0.5)
                )
            }
        ) as line:
            while True:
                # Block until rising edge event happens
                if line.read_edge_events():
                    self.handle_tap()

This will work the first run, but then as soon as it reads once, it then can’t read again. In fact, if I rerun the program, the gpiod.request_lines throws an error like resource is busy or something like that. Only once I reboot will the process run once again (but always only once). I figured the reboot removes the system interrupt that occurs when gpiod.request_lines or line.read_edge_events() is called. What can I do to fix this, or at least debug to gain more insight?

Have you updated the kernel to the latest?

I will attempt it again for the Dec. 3rd update, but assuming that wasn’t already fixed from the Dec. 2nd update, I have the following kernel/distro:

Linux raspbian-bookworm-aml-s905x-cc 6.1.64-11810-g69b1ffe8ef6c #1 SMP PREEMPT_DYNAMIC Sat Dec  2 01:00:10 EST 2023 aarch64 GNU/Linux