From 0178a847118618727e3667e6490b8a88d98acb7c Mon Sep 17 00:00:00 2001 From: Brendan <2bndy5@gmail.com> Date: Sat, 23 Mar 2024 09:12:29 -0700 Subject: [PATCH] expose GPIO & interrupt functions in new digital_io module --- CMakeLists.txt | 32 ++++++++-- src/pyArduinoAPI.cpp | 122 ++++++++++++++++++++++++++++++++++++++ src/pyrf24/digital_io.pyi | 15 +++++ 3 files changed, 163 insertions(+), 6 deletions(-) create mode 100644 src/pyArduinoAPI.cpp create mode 100644 src/pyrf24/digital_io.pyi diff --git a/CMakeLists.txt b/CMakeLists.txt index 18c3783..e823f64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,23 +19,22 @@ if(NOT "${RF24_LINKED_DRIVER}" STREQUAL "") endif() # suplement our copy of linux/gpio.h into SPIDEV & RPi driver sources -set(SUPPLEMEN_LINUX_GPIO_H FALSE) +set(SUPPLEMENT_LINUX_GPIO_H FALSE) if("${RF24_DRIVER}" STREQUAL "RPi" OR "${RF24_DRIVER}" STREQUAL "SPIDEV") - set(SUPPLEMEN_LINUX_GPIO_H TRUE) + set(SUPPLEMENT_LINUX_GPIO_H TRUE) message(STATUS "Supplementing ${RF24_DRIVER} driver with linux/gpio.h") # file(COPY src/linux/gpio.h DESTINATION RF24/utility/${RF24_DRIVER}/linux) list(APPEND RF24_DRIVER_SOURCES src/linux/gpio.h) endif() ################################# RF24 ############################# -set(CMAKE_VERBOSE_MAKEFILE ON) pybind11_add_module(rf24 src/pyRF24.cpp) target_include_directories(rf24 PUBLIC RF24 RF24/utility RF24/utility/${RF24_DRIVER} ) -if(SUPPLEMEN_LINUX_GPIO_H) +if(SUPPLEMENT_LINUX_GPIO_H) target_include_directories(rf24 PUBLIC src) endif() target_sources(rf24 PUBLIC @@ -56,7 +55,7 @@ target_include_directories(rf24_network PUBLIC RF24/utility RF24/utility/${RF24_DRIVER} ) -if(SUPPLEMEN_LINUX_GPIO_H) +if(SUPPLEMENT_LINUX_GPIO_H) target_include_directories(rf24_network PUBLIC src) endif() # don't let source look for an installed RF24 lib @@ -84,7 +83,7 @@ target_include_directories(rf24_mesh PUBLIC RF24/utility RF24/utility/${RF24_DRIVER} ) -if(SUPPLEMEN_LINUX_GPIO_H) +if(SUPPLEMENT_LINUX_GPIO_H) target_include_directories(rf24_mesh PUBLIC src) endif() # don't let source look for an installed RF24 lib @@ -104,6 +103,23 @@ target_sources(rf24_mesh PUBLIC ) apply_flags(rf24_mesh) +################################# Arduino DigitalIO ############################# + +pybind11_add_module(digital_io src/pyArduinoAPI.cpp) +target_include_directories(digital_io PUBLIC + RF24 + RF24/utility + RF24/utility/${RF24_DRIVER} +) +if(SUPPLEMENT_LINUX_GPIO_H) + target_include_directories(digital_io PUBLIC src) +endif() +# don't let source look for an installed RF24 lib +target_compile_definitions(digital_io PUBLIC USE_RF24_LIB_SRC) +target_sources(digital_io PUBLIC + ${RF24_DRIVER_SOURCES} +) +apply_flags(digital_io) ################################ INSTALL RULES #################################### # these are needed for scikit builds since the resulting .so files are copied into @@ -111,3 +127,7 @@ apply_flags(rf24_mesh) install(TARGETS rf24 DESTINATION .) install(TARGETS rf24_network DESTINATION .) install(TARGETS rf24_mesh DESTINATION .) +install(TARGETS digital_io DESTINATION .) + +# uncomment to show compiler args used in build logs +# set(CMAKE_VERBOSE_MAKEFILE ON) diff --git a/src/pyArduinoAPI.cpp b/src/pyArduinoAPI.cpp new file mode 100644 index 0000000..63f1d7b --- /dev/null +++ b/src/pyArduinoAPI.cpp @@ -0,0 +1,122 @@ +#include +#include +#include // The public API will pull in the backend hardware interface for GPIO + +namespace py = pybind11; + +#ifndef HIGH +static const int HIGH = 1; +#endif +#ifndef LOW +static const int LOW = 0; +#endif + +PYBIND11_MODULE(digital_io, m) +{ + m.doc() = "Some Arduino-style API used to expose GPIO interface for digital input and outputs"; + + // ******************************************** General GPIO + m.attr("HIGH") = HIGH; + m.attr("LOW") = LOW; + m.attr("INPUT") = INPUT; + m.attr("OUTPUT") = OUTPUT; + + m.def("pinMode", &GPIO::open, R"docstr( + pinMode(pin: int, direction: int) -> None + + Initialize a GPIO pin. + + :param pin: The pin number to initialize. + :param direction: The direction of the initialized pin. + This only be either `INPUT` or `OUTPUT`. + )docstr", + py::arg("pin"), py::arg("direction")); + m.def("pinClose", &GPIO::close, R"docstr( + pinClose(pin: int) -> None + + Deinitialize a GPIO pin. + + :param pin: The pin number to deinitialize. + )docstr", + py::arg("pin")); + m.def("digitalWrite", &GPIO::write, R"docstr( + digitalWrite(pin: int, value: int) -> None + + Write a digital output value to a GPIO pin. + + :param pin: The pin number to manipulate (as an output). + :param value: The value set to the output pin. + This can only be either `HIGH` or `LOW`. + )docstr", + py::arg("pin"), py::arg("value")); + + m.def("digitalRead", &GPIO::read, R"docstr( + digitalRead(pin: int) -> int + + Read the current value of a given pin. + + :param pin: The GPIO input pin to read. + + :returns: + ``1`` if the pin is currently `HIGH`, ``0`` if the pin is currently `LOW`. + )docstr", + py::arg("pin")); + +#ifndef MRAA + + // ******************************************** Interrupt support + + #ifndef RF24_WIRINGPI + m.def("detachInterrupt", &detachInterrupt, R"docstr( + detachInterrupt(pin: int) + + Discontinue watching the given GPIO input ``pin`` for an event. This is similar to + `pinClose()` but also deallocates the background thread used to monitor the pin for + events. + + .. note:: + This function is only available for the SPIDEV and RPi drivers. + It is not exposed when the pyrf24 package is built (from source) with the MRAA or + wiringPi drivers. + + :param pin: The pin number to watch for events described by ``mode``. + + :returns: + ``1`` if the pin was un-initialized and the background thread was canceled, + ``0`` if there was nothing to do. + )docstr", + py::arg("pin")); + #endif // !defined(RF24_WIRINGPI) + + m.attr("FALLING") = INT_EDGE_FALLING; + m.attr("RISING") = INT_EDGE_RISING; + m.attr("CHANGE") = INT_EDGE_BOTH; + m.def( + "attachInterrupt", [](rf24_gpio_pin_t pin, int mode, std::function& func) { + return attachInterrupt(pin, mode, *func.target()); + }, + R"docstr( + attachInterrupt(pin: int, mode: int, function: Callable[[], None]) -> int + + Attach an Interrupt Service Routine (ISR), a callback function, to a GPIO input pin. + + .. note:: + This function is only available for the SPIDEV, RPi, and wiringPi drivers. + It is not exposed when the pyrf24 package is built (from source) with the MRAA + driver. + + :param pin: The pin number to watch for events described by ``mode``. + :param mode: The event type that triggers a call to the given ``function``. + This value can only be `FALLING`, `RISING`, or `CHANGE`. + :param function: The function to call when an event (described by ``mode``) occurs on + the specified ``pin``. This function signature expects no arguments and returns + nothing. + + :returns: + ``1`` if pin is successfully setup to watch for an event, ``0`` if the given ``mode`` + is malformed. If ``0`` is returned, then specified pin remains un-initialized. + )docstr", + py::arg("pin"), py::arg("mode"), py::arg("function")); + +#endif // !defined(MRAA) +}; diff --git a/src/pyrf24/digital_io.pyi b/src/pyrf24/digital_io.pyi new file mode 100644 index 0000000..0c7c332 --- /dev/null +++ b/src/pyrf24/digital_io.pyi @@ -0,0 +1,15 @@ +from typing import Callable + +INPUT: int = ... +OUTPUT: int = ... +HIGH: int = ... +LOW: int = ... + +FALLING: int = ... +RISING: int = ... +CHANGE: int = ... + +def pinMode(pin: int, mode: int) -> None: ... +def pinClose(pin: int) -> None: ... +def detachInterrupt(pin: int) -> None: ... +def attachInterrupt(pin, int, mode: int, function: Callable[[], None]) -> int: ...