-
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Install libmraa from Radxa APT. - Monkey patch gpiozero.LED to be used with mraa. - Create custom button class that uses mraa because gpiozero.Button could not easily be monkey patched.
- Loading branch information
1 parent
96ccd23
commit cf305ef
Showing
11 changed files
with
345 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import time | ||
import threading | ||
|
||
from hm_pyhelper.logger import get_logger | ||
from gatewayconfig.gpio.mraa_gpio import init_mraa_pin | ||
|
||
|
||
LOGGER = get_logger(__name__) | ||
|
||
|
||
# How often to check for button presses | ||
MONITOR_CHECK_INTERVAL_SECONDS = 0.05 | ||
|
||
|
||
class MraaButton(threading.Thread): | ||
""" | ||
Unlike MraaLED, the button could not be easily monkey patched. | ||
Instead, basic button logic is implemented in its entirety. | ||
MraaButton will trigger #when_held if a button is pressed for | ||
hold_seconds. #when_held will only fire once if the button | ||
remains held down. If a button is pressed down for shorter, | ||
than hold_seconds, #when_held is not called. | ||
""" | ||
|
||
def __init__(self, pin_number, hold_seconds): | ||
""" | ||
pin_number refers to the pin number, not GPIO number: | ||
https://wiki.radxa.com/Rockpi4/hardware/gpio#GPIO_number | ||
""" | ||
|
||
super(MraaButton, self).__init__() | ||
LOGGER.info("Initializing mraa button on pin %s" | ||
% pin_number) | ||
self.daemon = True | ||
self.cancelled = False | ||
self.mraa_pin = init_mraa_pin(pin_number, False) | ||
self.hold_seconds = hold_seconds | ||
self.when_held = None | ||
self.reset_pressed_state() | ||
|
||
def run(self): | ||
while not self.cancelled: | ||
if not self.is_pressed(): | ||
self.reset_pressed_state() | ||
else: | ||
self.process_press() | ||
|
||
time.sleep(MONITOR_CHECK_INTERVAL_SECONDS) | ||
|
||
def process_press(self): | ||
""" | ||
If this is the first time this press has been detected, | ||
record the time for future reference. If the press has | ||
already been detected, attempt to trigger #when_held. | ||
""" | ||
|
||
if self.last_pressed_at is None: | ||
LOGGER.debug("Button pressed for first time") | ||
self.last_pressed_at = time.time() | ||
else: | ||
self.trigger_when_held_after_hold_seconds() | ||
|
||
def trigger_when_held_after_hold_seconds(self): | ||
""" | ||
If #when_held has not been invoked due to this press, | ||
and it is defined, invoke it. | ||
""" | ||
|
||
if self.when_held and not self.is_press_already_registered: | ||
if self.have_hold_seconds_elapsed(): | ||
LOGGER.debug("Button pressed down for " | ||
"`hold_seconds` (%s secs)" | ||
% self.hold_seconds) | ||
self.is_press_already_registered = True | ||
self.when_held() | ||
|
||
def have_hold_seconds_elapsed(self): | ||
elapsed_seconds = time.time() - self.last_pressed_at | ||
return elapsed_seconds >= self.hold_seconds | ||
|
||
def is_pressed(self): | ||
return self.mraa_pin.read() == 0 | ||
|
||
def reset_pressed_state(self): | ||
self.last_pressed_at = None | ||
self.is_press_already_registered = False | ||
|
||
def close(self): | ||
self.cancelled = True | ||
self.mraa_pin.write(0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import subprocess # nosec | ||
from hm_pyhelper.logger import get_logger | ||
|
||
LOGGER = get_logger(__name__) | ||
|
||
try: | ||
import mraa | ||
except Exception: | ||
LOGGER.warn("mraa not loaded. If you see this message outside of unit " | ||
"tests, this may cause a problem.") | ||
|
||
|
||
def init_mraa_pin(pin_number, is_input): | ||
""" | ||
Instantiate a new mraa.Gpio instance on `pin`. | ||
Pull the pin high before returning. | ||
Defining this method outside MraaButton | ||
so that it is easier to mock from tests. | ||
""" | ||
|
||
mraa_pin = mraa.Gpio(pin_number) | ||
if is_input: | ||
mraa_dir = mraa.DIR_IN | ||
else: | ||
mraa_dir = mraa.DIR_OUT | ||
|
||
mraa_pin.dir(mraa_dir) | ||
# pull up before usage | ||
mraa_gpio_write(pin_number, 1) | ||
return mraa_pin | ||
|
||
|
||
def mraa_gpio_write(pin_number, val): | ||
""" | ||
Some aspect of initialization prevents mraa.Gpio.write | ||
from having any effect. It is necessary to pull-up the | ||
GPIO before using it but this is not possible using the | ||
mraa Python library. In fact similar behavior is | ||
observed interacting with the filesystem directly. | ||
``` | ||
# echo 154 > /sys/class/gpio/export | ||
# echo 1 > /sys/class/gpio/gpio154/value | ||
bash: echo: write error: Operation not permitted | ||
``` | ||
Using `mraa-gpio set PIN 1` solves the issue. After | ||
invoking that command, the GPIO is then writable | ||
normally. | ||
This logic cannot be easily added to | ||
start-gateway-config.sh because it would need to | ||
include logic that replicates pyhelper methods | ||
like is_raspberry_pi and hardware_definitions. | ||
""" | ||
|
||
subprocess.check_call(['mraa-gpio', 'set', str(pin_number), str(val)]) # nosec NOSONAR |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
from gpiozero import LED | ||
from gpiozero.pins.mock import MockFactory | ||
|
||
from hm_pyhelper.logger import get_logger | ||
from gatewayconfig.gpio.mraa_gpio import init_mraa_pin | ||
|
||
LOGGER = get_logger(__name__) | ||
|
||
|
||
class MraaLED(LED): | ||
"""" | ||
Extend gpiozero.LED and override the write method to interact with libmraa. | ||
""" | ||
|
||
def __init__(self, pin_number=None, *, active_high=True, initial_value=False): | ||
super().__init__(pin_number, active_high=active_high, | ||
initial_value=initial_value, pin_factory=MockFactory()) | ||
|
||
LOGGER.info("Initializing mraa LED on pin %s" | ||
% pin_number) | ||
|
||
self.mraa_pin = init_mraa_pin(pin_number, True) | ||
|
||
def _write(self, value): | ||
self.mraa_pin.write(value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.