I’m writing a GPIO Python module from scratch. I’ve managed to write code to (in theory, I haven’t tested it on my Pi yet) turn pins on and off, and to read from pins, but for my project (and probably future ones) I’d like to be able to control GPIO devices. I do intend to run this as root because as it is I am writing to /sys/class/gpio
. I know that /sys/class/pwm
exists, but I have no clue how to use it. I found this answer, but the documentation that it pointed me to didn’t say anything about sysfs. I also found this answer but it didn’t make any sense to me. It also pointed to a web domain which had expired.
I tried reading the gpiozero source code to try and implement this in raw python but I found nothing of use. Or if anyone can point to an asyncio compatible GPIO library which can do this, that would be good too because the whole reason I was making my own is to make an asyncio version. I’m running Raspberry Pi OS legacy (because it wasn’t legacy when I first installed it).
My existing code (should you want to build on it) is this:
import asyncio
import sys
if __debug__: # just a disclaimer
print("asyncio_gpio_control: warning: NONE OF THIS IS TESTED", file=sys.stderr)
print("To suppress this message, run python with the -O option") # to not be PyGame with non-suppressable messages on import
pins = ["" for i in range(40)] # to keep track of what was in direction files before I overwrite it
pinDirections = pins # to keep track of pin directory
# arduino constants
HIGH = 1
LOW = 0
INPUT = 0
INPUT_PULLUP = NotInplemented
OUTPUT = 1
BASE_GPIO_PATH = '/sys/class/gpio/' # this may not be correct
async def write_to_file(filename: str, data: bytes): # underlying framework
cmd = ['echo', data.decode(), '>', filename]
proc = await asyncio.create_subprocess_exec(*cmd)
r = await proc.wait()
if r != 0:
raise OSError(f"Command {cmd} returned non-zero exit code {r}")
async def read_frm_file(filename: str):
cmd = ['cat', filename]
proc = await asynio.create_subprocess_exec(*cmd)
data = await proc.stdout.read().decode('ascii')
await proc.wait()
return data
async def setOutput(pin: int):
pins[pin] = await read_frm_file(BASE_GPIO_PATH + 'gpio' + int(pin) + '/direction')
await write_to_file(BASE_GPIO_PATH + 'gpio' + int(pin) + '/direction', 'out')
pinDirections[pin] = 'out'
async def setInput(pin: int):
#print("warning: asyncio_gpio_control currently cannot verify support for input pins", file=sys.stderr)
pins[pin] = await read_frm_file(BASE_GPIO_PATH + 'gpio' + int(pin) + '/direction')
await write_to_file(BASE_GPIO_PATH + 'gpio' + int(pin) + '/direction', 'in')
pinDirection[pin] = 'in'
async def digitalWrite(pin: int, value): # to put a value to a pin
if type(value) == type(0):
_value = str(value)
elif type(value) == type(''):
_value = value
else:
raise TypeError(f"value must be int or string, not {type(value)}.")
await write_to_file(BASE_GPIO_PATH + 'gpio' + int(pin) + '/direction', value)
async def digitalRead(pin: int): # to read a value from a pin
return (async read_frm_file(BASE_GPIO_PATH + 'gpio' + int(pin) + '/direction'))
async def pinMode(pin: int, direction): # just an arduino-named wrapper for setInput() and setOutput()
if type(direction) == type(''):
dir = 0 if direction in ('0', 'in', 'inp', 'input') else 1
elif type(direction) == type(0):
dir = direction
else:
raise TypeError(f"direction must be int or string, not {type(direction)}.")
if dir == 0:
setInput(pin)
elif dir == 1:
setOutput(pin)
else:
raise ValueError(f"If direction is int, it must be 0 or 1, not {direction}"
### put PWM functions here
I’m new to the stack exchange, but not the Raspberry Pi or Linux or Python.
libgpiod
Python bindings (pip install gpiod) orgpiozero
(which can lgpio to drive the character device interface). Using async writes to sysfs is mostly pointless as they are virtual files in kernel memory. The async machinery adds more overhead than just performing the operation with the character device interface, which is also faster than the sysfs. IMHO the only place async makes sense for GPIO is when waiting for edge events.