gpio: helper component for managing system gpios
Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
dfa11f0f3e
commit
83901b1896
|
@ -0,0 +1,135 @@
|
||||||
|
# GPIO Factory helper
|
||||||
|
#
|
||||||
|
# Copyright (C) 2021 Eric Callahan <arksine.code@gmail.com>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
from __future__ import annotations
|
||||||
|
import logging
|
||||||
|
from utils import load_system_module
|
||||||
|
|
||||||
|
# Annotation imports
|
||||||
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
Dict,
|
||||||
|
Tuple,
|
||||||
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from confighelper import ConfigHelper
|
||||||
|
|
||||||
|
class GpioFactory:
|
||||||
|
def __init__(self, config: ConfigHelper) -> None:
|
||||||
|
self.server = config.get_server()
|
||||||
|
self.gpiod: Any = load_system_module("gpiod")
|
||||||
|
self.chips: Dict[str, Any] = {}
|
||||||
|
self.reserved_gpios: Dict[str, GpioOutputPin] = {}
|
||||||
|
version: str = self.gpiod.version_string()
|
||||||
|
self.gpiod_version = tuple(int(v) for v in version.split('.'))
|
||||||
|
|
||||||
|
def _get_gpio_chip(self, chip_name) -> Any:
|
||||||
|
if chip_name in self.chips:
|
||||||
|
return self.chips[chip_name]
|
||||||
|
chip = self.gpiod.Chip(chip_name, self.gpiod.Chip.OPEN_BY_NAME)
|
||||||
|
self.chips[chip_name] = chip
|
||||||
|
return chip
|
||||||
|
|
||||||
|
def get_gpio_out_from_config(self,
|
||||||
|
config: ConfigHelper,
|
||||||
|
option: str = "pin",
|
||||||
|
initial_value: int = 0
|
||||||
|
) -> GpioOutputPin:
|
||||||
|
pin_name = config.get(option)
|
||||||
|
try:
|
||||||
|
return self.setup_gpio_out(pin_name, initial_value)
|
||||||
|
except Exception as e:
|
||||||
|
raise config.error(str(e)) from None
|
||||||
|
|
||||||
|
def setup_gpio_out(self,
|
||||||
|
pin_name: str,
|
||||||
|
initial_value: int = 0
|
||||||
|
) -> GpioOutputPin:
|
||||||
|
initial_value = int(not not initial_value)
|
||||||
|
pin_id, chip_id, invert = self._parse_pin(pin_name)
|
||||||
|
full_name = f"{pin_id}:{chip_id}"
|
||||||
|
if full_name in self.reserved_gpios:
|
||||||
|
raise self.server.error(f"GPIO {full_name} already reserved")
|
||||||
|
try:
|
||||||
|
chip = self._get_gpio_chip(chip_id)
|
||||||
|
line = chip.get_line(pin_id)
|
||||||
|
args: Dict[str, Any] = {
|
||||||
|
'consumer': "moonraker",
|
||||||
|
'type': self.gpiod.LINE_REQ_DIR_OUT
|
||||||
|
}
|
||||||
|
if invert:
|
||||||
|
args['flags'] = self.gpiod.LINE_REQ_FLAG_ACTIVE_LOW
|
||||||
|
if self.gpiod_version < (1, 3):
|
||||||
|
args['default_vals'] = [initial_value]
|
||||||
|
else:
|
||||||
|
args['default_val'] = initial_value
|
||||||
|
line.request(**args)
|
||||||
|
except Exception:
|
||||||
|
logging.exception(
|
||||||
|
f"Unable to init {pin_id}. Make sure the gpio is not in "
|
||||||
|
"use by another program or exported by sysfs.")
|
||||||
|
raise
|
||||||
|
gpio_out = GpioOutputPin(full_name, line, invert, initial_value)
|
||||||
|
self.reserved_gpios[full_name] = gpio_out
|
||||||
|
return gpio_out
|
||||||
|
|
||||||
|
def _parse_pin(self, pin_name: str) -> Tuple[int, str, bool]:
|
||||||
|
pin = pin_name
|
||||||
|
invert = False
|
||||||
|
if pin[0] == "!":
|
||||||
|
pin = pin[1:]
|
||||||
|
invert = True
|
||||||
|
chip_id: str = "gpiochip0"
|
||||||
|
pin_parts = pin.split("/")
|
||||||
|
if len(pin_parts) == 2:
|
||||||
|
chip_id, pin = pin_parts
|
||||||
|
elif len(pin_parts) == 1:
|
||||||
|
pin = pin_parts[0]
|
||||||
|
# Verify pin
|
||||||
|
if not chip_id.startswith("gpiochip") or \
|
||||||
|
not chip_id[-1].isdigit() or \
|
||||||
|
not pin.startswith("gpio") or \
|
||||||
|
not pin[4:].isdigit():
|
||||||
|
raise self.server.error(
|
||||||
|
f"Invalid Gpio Pin: {pin_name}")
|
||||||
|
pin_id = int(pin[4:])
|
||||||
|
return pin_id, chip_id, invert
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
for output_pin in self.reserved_gpios.values():
|
||||||
|
output_pin.release()
|
||||||
|
for chip in self.chips.values():
|
||||||
|
chip.close()
|
||||||
|
|
||||||
|
class GpioOutputPin:
|
||||||
|
def __init__(self,
|
||||||
|
name: str,
|
||||||
|
line: Any,
|
||||||
|
inverted: bool,
|
||||||
|
initial_val: int
|
||||||
|
) -> None:
|
||||||
|
self.name = name
|
||||||
|
self.line = line
|
||||||
|
self.inverted = inverted
|
||||||
|
self.value = initial_val
|
||||||
|
self.release = line.release
|
||||||
|
|
||||||
|
def write(self, value: int) -> None:
|
||||||
|
self.value = int(not not value)
|
||||||
|
self.line.set_value(self.value)
|
||||||
|
|
||||||
|
def is_inverted(self) -> bool:
|
||||||
|
return self.inverted
|
||||||
|
|
||||||
|
def get_value(self) -> int:
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
def get_name(self) -> str:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def load_component(config: ConfigHelper) -> GpioFactory:
|
||||||
|
return GpioFactory(config)
|
Loading…
Reference in New Issue