How to control GPIO via C or Python 3

Yes digitalio is accessed using Blinka as a pre-requisite.

Adafruit-Blinka · PyPI

This repository contains a selection of packages emulating the CircuitPython API for devices or hosts running CPython or MicroPython. Working code exists to emulate these CircuitPython packages:

  • digitalio - digital input/output pins, using pin identities from board+microcontroller packages

Make sure you have the very latest library as I worked with the team there to get some pin changes for the SPI library specifically for the Le Potato last week.

I’ve been banging my head against the examples included in /usr/share/doc/python3-libgpiod/examples, trying to get some of them working.

My hope is to use a Le Potato to listen for a button press, so basically respond when one of a series of buttons is pressed.

However, I can’t seem to get a button hooked up to line 82 (physical pin 38) to register.

pi@raspberrypi:/usr/share/doc/python3-libgpiod/examples $ ./gpiomon.py periphs-banks 82
Traceback (most recent call last):
  File "/usr/share/doc/python3-libgpiod/examples/./gpiomon.py", line 37, in <module>
    lines.request(consumer=sys.argv[0], type=gpiod.LINE_REQ_EV_BOTH_EDGES)
OSError: [Errno 19] No such device

This is on Le Potato, running the latest Raspbian OS image for it, with everything updated as of yesterday.

gpioget.py does seem to work:

pi@raspberrypi:/usr/share/doc/python3-libgpiod/examples $ ./gpioget.py periphs-banks 82
1

(Though it’s always inverse from what I expect—shouldn’t it default to 0 unless I press the button and close a circuit?)

How do you have it wired? Are you using a pull up/down resistor?

I use CircuitPython, as that’s what the rest of my micro-controllers run on (apt install python3-libgpiod, pip install Adafruit-Blinka). I wired one side of the switch to 3.3V (Pin 17) and the other side to Pin 38 and also pulled Pin 38 to 0V (Pin 25) with a 10K resistor.

The following script returns True when pressed and False when not.

#! /bin/python3

import board
import digitalio as dio
from time import sleep

button = dio.DigitalInOut(board.P38)
button.direction = dio.Direction.INPUT

while True:
 print(button.value)
 sleep(1)

The following bash script also returns 1 when pressed and 0 when not:

#! /bin/bash

while :
do
 lgpio get 38
 sleep 1
done

Hope that helps!

Ok, this isn’t using the pins you asked for, and I use CircuitPython, since it works on my micro controllers. This is how I happened to have things wired when I saw the question; you can switch around the pins. Circuit python labels the pins on the main header “Pn” where “n” in the physical pin number. So Pin 3 is P3, Pin 38 is P38. Nice and simple.

Wiring:
Led 0 short lead to resistor* to ground (Pin 9)
Led 0 long lead to Pin 3

Led 1 short lead to resistor* ground (Pin 6)
Led 1 long lead to Pin 5

Button side 1 to 3.3V (Pin 17)
Button side 2 to Pin 38
Pin 38 to 10K Resistor to ground (Pin 25)

*In my case I’m using 68 ohm resistors, google “leds and resistors” to learn how to pair them.

To Install CricuitPython:

$ sudo apt install -y python3-libgpiod python3-pip
$ pip3 install Adafruit-Blinka

The following script causes Led 0 to blink and Led 1 to light only when the button is pressed:


#! /bin/python3

# import stuff
import board
import digitalio as dio
from time import sleep

# Setup leds as digital in/out objects and specify 
# their direction and value
# type dir(board) the the REPL to get all the possibe pins, 
# they are numbered by physical location.
led0 = dio.DigitalInOut(board.P3)
led0.direction = dio.Direction.OUTPUT
led0.value = 0

led1 = dio.DigitalInOut(board.P5)
led1.direction = dio.Direction.OUTPUT
led1.value = 0

# setup button as an INPUT
# note, pin 7 must be pulld down to 0V with a resistor!
button = dio.DigitalInOut(board.P38)
button.direction = dio.Direction.INPUT

# start an endless loop
while True:
	# led0 on
	led0.value = 1
	# set led1's value to match button's value and print it
	led1.value = button.value
	print(button.value)
	# wait a quarter second
	sleep(0.25)
	#led0 off
	led0.value = 0
	# same as before
	led1.value = button.value
	sleep(0.25)

# Ctrl + C to exit

More info on CircuitPython and Blinka are available from learn.adafruit.com. As far as portability, with the small matter of changing the pin numbers, this script will run on a RPI Pico or similar, no linux needed.

Despite what may be written above, there are some people trying to make this stuff accessible for beginners like me, and I’ll keep my own council on what is to be worth my time. I don’t mind being told something isn’t getting official support, but I wish they wouldn’t discourage us from trying to make this board compatible with the devices we already own. Part of building a community is letting the masses do what we please with the hardware. Hopefully these guys will figure that out and drop the attitude. They might sell more boards that way too.

*Edited to include Current Limiting Resistors in led wiring instructions.

1 Like

There is default SoC internal pull-up and pull-downs are set in the DT: https://boot.libre.computer/ci/aml-s905x-cc.dts

You have to create an overlay to change the default mode.

gpiomon might use gpio_to_irq kernel function which is not implemented in mainline on Amlogic SoCs. However, you can create a gpio-key overlay that binds a GPIO to a button. It is the recommended way of doing it rather than polling the GPIO in userspace.

I think perhaps it would be more helpful if you assumed that there is a strong likelihood that many readers have no knowledge whatsoever of Device Trees and Overlays.

As you mention in various posts they are pretty essential for working with the Le Potato and the ldto tool mentioned in this post

How to Enable SPI on AML-S905X-CC Le Potato - Hardware - Libre Computer Hub

is an excellent tool for applying existing overlays.

After a good few hours of internet research, I have learned enough to modify an existing overlay but I do not believe there is any guide on this forum which explains the process.

The reality is that it would be fantastic to have an extended libraries of overlays for all sorts of different applications and that you probably don’t have time to develop them. The good news is that given some guidance users could develop their own, issue a pull request on Github and the libraries would grow organically.

Your Choice …

1 Like

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