diff --git a/README.md b/README.md index 2d2a4fba25f946c0f3d3c7ccc813fad1e44f6505..635c9ed5b56785739c1b35f5c02b5197bb265f8d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ GPIO Modules - PCF8575 IO chip (`pcf8575`) - PiFaceDigital 2 IO board (`piface2`) - Beaglebone GPIO (`beaglebone`) +- Linux Kernel 4.8+ GPIOd (`gpiod`) Sensors ------- diff --git a/pi_mqtt_gpio/__init__.py b/pi_mqtt_gpio/__init__.py index 7dd2eb19c2b999941a44cfb6a8f9c5f31375a7a6..8dadd06d0510d3937e238e3aa373ca970d0e324d 100644 --- a/pi_mqtt_gpio/__init__.py +++ b/pi_mqtt_gpio/__init__.py @@ -148,6 +148,11 @@ sensor_modules: type: boolean required: no default: yes + chip: + type: string + required: no + default: "/dev/gpiochip0" + empty: no stream_modules: type: list diff --git a/pi_mqtt_gpio/modules/gpiod.py b/pi_mqtt_gpio/modules/gpiod.py new file mode 100644 index 0000000000000000000000000000000000000000..f9d14f20ac23733d997b82afad64d83e3fd37010 --- /dev/null +++ b/pi_mqtt_gpio/modules/gpiod.py @@ -0,0 +1,98 @@ +from pi_mqtt_gpio.modules import GenericGPIO, PinDirection, PinPullup, \ + InterruptEdge +import asyncio + +REQUIREMENTS = ("gpiod",) + +DIRECTIONS = None +PULLUPS = None +INTERRUPT = None +GPIO_INTERRUPT_CALLBACK_LOOKUP = {} + + +class GPIO(GenericGPIO): + """ + Implementation of GPIO class for Raspberry Pi native GPIO. + """ + + def __init__(self, config): + global DIRECTIONS, PULLUPS, INTERRUPT + import gpiod as gpio + + self.io = gpio + self.chip = config.chip + self.pins = {} + self.loop = asyncio.get_event_loop() + self.loop.run_forever() + + DIRECTIONS = {PinDirection.INPUT: gpio.line_request.DIRECTION_INPUT, PinDirection.OUTPUT: gpio.line_request.DIRECTION_OUTPUT} + + PULLUPS = { + PinPullup.OFF: gpio.line.BIAS_DISABLE, + PinPullup.UP: gpio.line.BIAS_PULL_UP, + PinPullup.DOWN: gpio.line.BIAS_PULL_DOWN, + } + + INTERRUPT = { + InterruptEdge.RISING: gpio.line_request.EVENT_RISING_EDGE, + InterruptEdge.FALLING: gpio.line_request.EVENT_FALLING_EDGE, + InterruptEdge.BOTH: gpio.line_request.EVENT_BOTH_EDGES + } + + def setup_pin(self, pin, direction, pullup, pin_config): + direction = DIRECTIONS[direction] + offset = pin + + if pullup is None: + pullup = PULLUPS[PinPullup.OFF] + else: + pullup = PULLUPS[pullup] + + pin = self.chip.get_line(offset) + pin.bias = pullup + + config = self.io.line_request() + config.consumer = 'pin' + offset + config.request_type = direction + + pin.request(config) + self.pins[offset] = pin + + def setup_interrupt(self, handle, pin, edge, callback, bouncetime=100): + """ + install interrupt callback function + handle: is returned in the callback function as identification + pin: gpio to watch for interrupts + edge: triggering edge: RISING, FALLING or BOTH + callback: the callback function to be called, when interrupt occurs + bouncetime: minimum time between two interrupts + """ + edge = INTERRUPT[edge] + offset = pin + + pin = self.chip.get_line(offset) + + config = self.io.line_request() + config.consumer = 'pin' + offset + config.request_type = edge + + self.loop.create_task(self._add_event_detect(pin, self.interrupt_callback)) + self.GPIO_INTERRUPT_CALLBACK_LOOKUP[offset] = {"handle": handle, + "callback": callback} + + def set_pin(self, pin, value): + offset = pin + self.pins[offset].set_value(value) + + def get_pin(self, pin): + offset = pin + return self.pins[offset].get_value() + + def cleanup(self): + self.loop.stop() + self.io.chip_deleter(self.chip) + + async def _add_event_detect(self, pin, callback): + while True: + if pin.event_wait(): + callback() diff --git a/requirements.txt b/requirements.txt index c0643a8df3873aedc546a174217641479dce7fcc..7ac117cc2c0e358fc34c83e0dcf0bbecd13a7e10 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ paho-mqtt PyYAML>=5.3.1 enum34 cerberus - +gpiod