How to control GPIO via C or Python 3

No Problem - Pay it Forward !

In all fairness pretty much every bit of code relies on multiple behind the scenes libraries - usually a bit more evident in C - which I mess with more often.

One of the attractions of the Pi originally was its price point, which allowed a Hobbyist/ Student to get started, but it is important to be able to make some progress quickly even with a ‘Blink’ program.

I’m going to try to recreate a project, developed in 2014 on a Pi 2 to drive two hand soldered 8x8 LED Xmas window lights, using the Le Potato for the challenge. I’ll probably use some I2C along the way so I gather I’ll be experimenting with Overlays.

I’m planning to leverage the libregpio library, which does call on gpioset, as there will be plenty of I/O unless somone can recommend a better option.

Happy Programming

Shout out to Roberto Chen the owner of the libregpio repository who has just added some very good documentation to his library.

https://libregpio.readthedocs.io/en/latest/

1 Like

These are standard Linux GPIO libraries and python bindings. You write it once and it runs anywhere. If you do it differently, you bear the cost of not being portable.

1 Like

I did try out the attached interface and works pretty well on the Le Potato.

HCDC RPi GPIO Status LED & Terminal Block Breakout Board HAT for Raspberry Pi A+ 3A+ B+ 2B 3B 3B+ 4B (xikentech.com)

Also the digitalio library under Adafruit Blinka operates successfully on the Le Potato.

@Iain_R is digitalio installed via Blinka ?

I’ve experimented briefly with GitHub - hhk7734/python3-gpiod: gpiod pure Python library with almost the same usage as libgpiodcxx (on a Pi Zero) and it looks like it might work with Le Potato but I’ve not had chance to try it yet.

“sudo apt install python3-libgpiod” also installed, but again I’ve not had chance to try it yet.

I’m still trying to move some code away from RPi.GPIO and having some teething issues with SPI pin conflicts.

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.