From 93d774d798af4a6dd95bfe8047ee57de16ceed13 Mon Sep 17 00:00:00 2001 From: michael Date: Mon, 4 Nov 2024 12:31:22 +0100 Subject: [PATCH] removing gpiozero from gpio backend. --- wigglecam/services/backends/io/gpio.py | 73 ++++++++++++++++++-------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/wigglecam/services/backends/io/gpio.py b/wigglecam/services/backends/io/gpio.py index a68cf92..885eb68 100644 --- a/wigglecam/services/backends/io/gpio.py +++ b/wigglecam/services/backends/io/gpio.py @@ -2,11 +2,11 @@ import os import time from pathlib import Path +from queue import Empty, Queue from threading import current_thread import gpiod -from gpiod.line import Bias, Clock, Direction, Edge -from gpiozero import DigitalOutputDevice +from gpiod.line import Bias, Clock, Direction, Edge, Value from ....utils.stoppablethread import StoppableThread from ...config.models import ConfigBackendGpio @@ -23,8 +23,9 @@ def __init__(self, config: ConfigBackendGpio): self._config: ConfigBackendGpio = config # private props - self._gpio_thread: StoppableThread = None - self._trigger_out: DigitalOutputDevice = None + self._gpio_monitor_thread: StoppableThread = None + self._gpio_triggerout_thread: StoppableThread = None + self._queue_trigger_out: Queue[bool] = None # init private props pass @@ -37,14 +38,16 @@ def start(self): self._set_hardware_clock(enable=True) logger.info("generating clock using hardware pwm overlay") - self._trigger_out = DigitalOutputDevice(pin=self._config.trigger_out_pin_name, initial_value=False, active_high=True) - logger.info(f"forward trigger_out on {self._trigger_out}") + self._queue_trigger_out: Queue[bool] = Queue() + self._gpio_triggerout_thread = StoppableThread(name="_gpio_triggerout_thread", target=self._gpio_triggerout_fun, args=(), daemon=True) + self._gpio_triggerout_thread.start() + logger.info(f"forward trigger_out on {self._config.trigger_out_pin_name}") else: logger.info("skipped loading primary clockwork service because disabled in config") logger.info("skipped enabling trigger_out because disabled in config, trigger out should be enabled on primary node usually only.") - self._gpio_thread = StoppableThread(name="_gpio_thread", target=self._gpio_fun, args=(), daemon=True) - self._gpio_thread.start() + self._gpio_monitor_thread = StoppableThread(name="_gpio_monitor_thread", target=self._gpio_monitor_fun, args=(), daemon=True) + self._gpio_monitor_thread.start() logger.debug(f"{self.__module__} started") @@ -54,12 +57,13 @@ def stop(self): if self._config.is_primary: self._set_hardware_clock(enable=False) - if self._trigger_out: - self._trigger_out.close() + if self._gpio_monitor_thread and self._gpio_monitor_thread.is_alive(): + self._gpio_monitor_thread.stop() + self._gpio_monitor_thread.join() - if self._gpio_thread and self._gpio_thread.is_alive(): - self._gpio_thread.stop() - self._gpio_thread.join() + if self._gpio_triggerout_thread and self._gpio_triggerout_thread.is_alive(): + self._gpio_triggerout_thread.stop() + self._gpio_triggerout_thread.join() def derive_nominal_framerate_from_clock(self) -> int: """calc the framerate derived by monitoring the clock signal for 11 ticks (means 10 intervals). @@ -90,16 +94,12 @@ def derive_nominal_framerate_from_clock(self) -> int: return fps def set_trigger_out(self, on: bool): - if not self._trigger_out: + if not self._queue_trigger_out: logger.debug("trigger requested to forward on this device but disabled in config!") return - if on: - self._trigger_out.on() - logger.debug("set trigger_out ON") - else: - self._trigger_out.off() - logger.debug("set trigger_out OFF") + logger.debug(f"set trigger_out={on}") + self._queue_trigger_out.put(on) def _set_hardware_clock(self, enable: bool = True): """ @@ -137,8 +137,35 @@ def _set_hardware_clock(self, enable: bool = True): logger.info(f"set hw clock sysfs chip {pwm_dir}, period={PERIOD}, duty_cycle={DUTY_CYCLE}, enable={1 if enable else 0}") - def _gpio_fun(self): - logger.debug("starting _gpio_fun") + def _gpio_triggerout_fun(self): + logger.debug("starting _gpio_triggerout_fun") + + with gpiod.Chip(self._config.chip) as chip: + try: + _gpiod_trigger_out = chip.line_offset_from_id(self._config.trigger_out_pin_name) + except OSError as exc: + # An OSError is raised if the name is not found. + raise RuntimeError(f"gpio not found: {exc}") from exc + + # setup lines + with gpiod.request_lines( + self._config.chip, + consumer="trigger_out", + config={_gpiod_trigger_out: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE)}, + ) as request: + while not current_thread().stopped(): + # timeout to allow the thread to end after 1s if stopped and no events received (read_edge_events would block infinite otherwise) + try: + value = self._queue_trigger_out.get(block=True, timeout=1.0) + except Empty: + pass + else: + request.set_value(_gpiod_trigger_out, value) + + logger.debug("_gpio_triggerout_fun left") + + def _gpio_monitor_fun(self): + logger.debug("starting _gpio_monitor_fun") _gpiod_clock_in = None _gpiod_trigger_in = None @@ -193,4 +220,4 @@ def _event_callback(event: gpiod.EdgeEvent): # print("no events") pass - logger.info("_gpio_fun left") + logger.info("_gpio_monitor_fun left")