diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1c49d9b..6386396 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,3 +15,7 @@ updates: directory: "/" schedule: interval: "weekly" + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e5272bf..359c394 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,9 +38,10 @@ jobs: --style=file \ --tidy-checks='-*' \ --lines-changed-only=true \ - --ignore='!src|' \ + --ignore='src/linux/gpio.h' \ --format-review=true \ - --file-annotations=false + --file-annotations=false \ + --step-summary=true - name: C++ Linter checks failed? if: steps.linter.outputs.checks-failed > 0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 98c8a2c..4f3f28e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,78 +18,108 @@ if(NOT "${RF24_LINKED_DRIVER}" STREQUAL "") message(STATUS "Linking to utility driver '${RF24_LINKED_DRIVER}'") endif() -################################# RF24 ############################# +# suplement our copy of linux/gpio.h into SPIDEV & RPi driver sources +set(SUPPLEMENT_LINUX_GPIO_H FALSE) +if("${RF24_DRIVER}" STREQUAL "RPi" OR "${RF24_DRIVER}" STREQUAL "SPIDEV") + 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() + +### Build C++ RF24 stack as shared libraries (resulting in isolated binary drivers) + +################################# RF24 C++ ######################### +add_library(cpp_rf24 SHARED + RF24/RF24.cpp + ${RF24_DRIVER_SOURCES} +) +if(SUPPLEMENT_LINUX_GPIO_H) + target_include_directories(cpp_rf24 PUBLIC src) +endif() +target_include_directories(cpp_rf24 PUBLIC utility) +if(NOT "${RF24_LINKED_DRIVER}" STREQUAL "") + if("${RF24_DRIVER}" STREQUAL "wiringPi") + target_link_libraries(cpp_rf24 PUBLIC rt crypt ${RF24_LINKED_DRIVER}) + else() + target_link_libraries(cpp_rf24 PUBLIC ${RF24_LINKED_DRIVER}) + endif() +endif() +apply_flags(cpp_rf24) + +################################# RF24Network C++ ######################### +add_library(cpp_rf24_network SHARED + RF24Network/RF24Network.cpp +) +# don't let source look for an installed RF24 lib +target_compile_definitions(cpp_rf24_network PUBLIC USE_RF24_LIB_SRC) +target_include_directories(cpp_rf24_network PUBLIC + RF24 + RF24/utility + RF24/utility/${RF24_DRIVER} + RF24Network +) +target_link_libraries(cpp_rf24_network PUBLIC cpp_rf24) +apply_flags(cpp_rf24_network) + +################################# RF24Mesh C++ ######################### +add_library(cpp_rf24_mesh SHARED + RF24Mesh/RF24Mesh.cpp +) +# don't let source look for an installed RF24 lib +target_compile_definitions(cpp_rf24_mesh PUBLIC USE_RF24_LIB_SRC) +target_include_directories(cpp_rf24_mesh PUBLIC + RF24 + RF24/utility + RF24/utility/${RF24_DRIVER} + RF24Network + RF24Mesh +) +target_link_libraries(cpp_rf24_mesh PUBLIC cpp_rf24_network) +apply_flags(cpp_rf24_mesh) + +### Build python bindings for RF24 stack and link to shared C++ lib binaries +################################# RF24 ############################# pybind11_add_module(rf24 src/pyRF24.cpp) +target_link_libraries(rf24 PUBLIC cpp_rf24) target_include_directories(rf24 PUBLIC RF24 RF24/utility RF24/utility/${RF24_DRIVER} ) -target_sources(rf24 PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/RF24/RF24.h - ${CMAKE_CURRENT_LIST_DIR}/RF24/RF24.cpp - ${CMAKE_CURRENT_LIST_DIR}/RF24/nRF24L01.h - ${CMAKE_CURRENT_LIST_DIR}/RF24/RF24_config.h - ${RF24_DRIVER_SOURCES} -) -apply_flags(rf24) ################################# RF24NETWORK ############################# - pybind11_add_module(rf24_network src/pyRF24Network.cpp) +target_link_libraries(rf24_network PUBLIC cpp_rf24_network) target_include_directories(rf24_network PUBLIC RF24 - RF24Network RF24/utility RF24/utility/${RF24_DRIVER} + RF24Network ) # don't let source look for an installed RF24 lib target_compile_definitions(rf24_network PUBLIC USE_RF24_LIB_SRC) -target_sources(rf24_network PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/RF24/RF24.h - ${CMAKE_CURRENT_LIST_DIR}/RF24/RF24.cpp - ${CMAKE_CURRENT_LIST_DIR}/RF24/nRF24L01.h - ${CMAKE_CURRENT_LIST_DIR}/RF24/RF24_config.h - ${RF24_DRIVER_SOURCES} - ${CMAKE_CURRENT_LIST_DIR}/RF24Network/RF24Network.h - ${CMAKE_CURRENT_LIST_DIR}/RF24Network/RF24Network.cpp - ${CMAKE_CURRENT_LIST_DIR}/RF24Network/RF24Network_config.h -) -apply_flags(rf24_network) - ################################# RF24MESH ############################# - pybind11_add_module(rf24_mesh src/pyRF24Mesh.cpp) +target_link_libraries(rf24_mesh PUBLIC cpp_rf24_mesh) target_include_directories(rf24_mesh PUBLIC RF24 - RF24Network - RF24Mesh RF24/utility RF24/utility/${RF24_DRIVER} + RF24Network + RF24Mesh ) # don't let source look for an installed RF24 lib target_compile_definitions(rf24_mesh PUBLIC USE_RF24_LIB_SRC) -target_sources(rf24_mesh PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/RF24/RF24.h - ${CMAKE_CURRENT_LIST_DIR}/RF24/RF24.cpp - ${CMAKE_CURRENT_LIST_DIR}/RF24/nRF24L01.h - ${CMAKE_CURRENT_LIST_DIR}/RF24/RF24_config.h - ${RF24_DRIVER_SOURCES} - ${CMAKE_CURRENT_LIST_DIR}/RF24Network/RF24Network.h - ${CMAKE_CURRENT_LIST_DIR}/RF24Network/RF24Network.cpp - ${CMAKE_CURRENT_LIST_DIR}/RF24Network/RF24Network_config.h - ${CMAKE_CURRENT_LIST_DIR}/RF24Mesh/RF24Mesh.h - ${CMAKE_CURRENT_LIST_DIR}/RF24Mesh/RF24Mesh.cpp - ${CMAKE_CURRENT_LIST_DIR}/RF24Mesh/RF24Mesh_config.h -) -apply_flags(rf24_mesh) ################################ INSTALL RULES #################################### # these are needed for scikit builds since the resulting .so files are copied into # the binary distribution wheels (.whl files) for python. -install(TARGETS rf24 DESTINATION .) -install(TARGETS rf24_network DESTINATION .) -install(TARGETS rf24_mesh DESTINATION .) +install(TARGETS rf24 rf24_network rf24_mesh DESTINATION .) +install(TARGETS cpp_rf24 cpp_rf24_network cpp_rf24_mesh DESTINATION .) + +# uncomment to show compiler args used in build logs +# set(CMAKE_VERBOSE_MAKEFILE ON) diff --git a/README.rst b/README.rst index 517efe3..e10eadf 100644 --- a/README.rst +++ b/README.rst @@ -52,16 +52,17 @@ The IRQ pin is not typically connected, and it is only used in the interrupt_con ultimately depends on the nature of your power supply's stability. .. note:: - Notice that RPi.GPIO (for python) is used to manage the GPIO pins on the Raspberry Pi + Notice that `gpiod `_ is used to watch the radio's IRQ pin (exclusively during the `interrupt_configure.py `_ example). - RPi.GPIO is not required for normal usage (when not using the radio's IRQ pin). + `gpiod `_ is not required for normal usage (when not using the radio's IRQ pin). .. code-block:: bash - sudo apt install python3-rpi.gpio + sudo apt install python3-dev + pip install gpiod Installing from PyPI ~~~~~~~~~~~~~~~~~~~~ diff --git a/RF24 b/RF24 index 9eca153..a6eae52 160000 --- a/RF24 +++ b/RF24 @@ -1 +1 @@ -Subproject commit 9eca153dcf39a975fe662e7e86ffde73f7e5eead +Subproject commit a6eae52859c7a118600691a9e68381c399bbf58f diff --git a/cmake/using_flags.cmake b/cmake/using_flags.cmake index c190727..69a7d5c 100644 --- a/cmake/using_flags.cmake +++ b/cmake/using_flags.cmake @@ -4,11 +4,6 @@ # RF24 core specific options option(RF24_DEBUG "enable/disable debugging output for RF24 lib" OFF) -# Disabling IRQ support should be always done because -# IRQ support can be handled in python with different libs. -option(RF24_NO_INTERRUPT "disable IRQ support (dependent on pigpio)" ON) -# does not affect pigpio driver though - # RF24Network specific options option(SERIAL_DEBUG "enable/disable debugging output for RF24Network lib" OFF) option(SERIAL_DEBUG_MINIMAL "enable/disable minimal debugging output for RF24Network lib" OFF) @@ -60,27 +55,19 @@ option(MESH_DEBUG_MINIMAL "enable/disable minimal debugging output for RF24Mesh ############################################### # function to apply flags to applicable targets function(apply_flags target) - # apply RF24 flags to all targets - if(RF24_NO_INTERRUPT) - target_compile_definitions(${target} PUBLIC RF24_NO_INTERRUPT) - endif() - if(RF24_DEBUG) - message(STATUS "RF24_DEBUG asserted for ${target}") - target_compile_definitions(${target} PUBLIC SERIAL_DEBUG) + # apply RF24 flags to cpp_rf24 target + if("${target}" STREQUAL "cpp_rf24") + if(RF24_DEBUG) + message(STATUS "RF24_DEBUG asserted for ${target}") + target_compile_definitions(${target} PUBLIC SERIAL_DEBUG) + endif() endif() # pass driver used to expose as a constant in rf24 module. target_compile_definitions(${target} PUBLIC RF24_DRIVER="${RF24_DRIVER}") - if(NOT "${RF24_LINKED_DRIVER}" STREQUAL "") - if("${RF24_DRIVER}" STREQUAL "wiringPi") - target_link_libraries(${target} PRIVATE rt crypt ${RF24_LINKED_DRIVER}) - else() - target_link_libraries(${target} PRIVATE ${RF24_LINKED_DRIVER}) - endif() - endif() - # apply RF24Network flags to rf24_mesh and rf24_network targets - if("${target}" STREQUAL "rf24_network" OR "${target}" STREQUAL "rf24_mesh") + # apply RF24Network flags to cpp_rf24_network target + if("${target}" STREQUAL "cpp_rf24_network") if(SERIAL_DEBUG) message(STATUS "SERIAL_DEBUG asserted for ${target}") target_compile_definitions(${target} PUBLIC SERIAL_DEBUG) @@ -116,8 +103,8 @@ function(apply_flags target) endif() endif() - # apply RF24Mesh flags to only rf24_mesh target - if("${target}" STREQUAL "rf24_mesh") + # apply RF24Mesh flags to cpp_rf24_mesh target + if("${target}" STREQUAL "cpp_rf24_mesh") if(MESH_NOMASTER) message(STATUS "MESH_NOMASTER asserted for ${target}") target_compile_definitions(${target} PUBLIC MESH_NOMASTER) diff --git a/examples/acknowledgement_payloads.py b/examples/acknowledgement_payloads.py index eb92b84..1c4e0d9 100644 --- a/examples/acknowledgement_payloads.py +++ b/examples/acknowledgement_payloads.py @@ -1,12 +1,14 @@ """ Simple example of using the library to transmit and retrieve custom automatic acknowledgment payloads. + +See documentation at https://nRF24.github.io/pyRF24 """ -import sys import time -import argparse -from pyrf24 import RF24, RF24_PA_LOW +from pyrf24 import RF24, RF24_PA_LOW, RF24_DRIVER + +print(__file__) # print example name ########### USER CONFIGURATION ########### # See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md @@ -15,7 +17,14 @@ # their own pin numbering # CS Pin addresses the SPI bus number at /dev/spidev. # ie: RF24 radio(, *10+); spidev1.0 is 10, spidev1.1 is 11 etc.. -radio = RF24(22, 0) +CSN_PIN = 0 # aka CE0 on SPI bus 0: /dev/spidev0.0 +if RF24_DRIVER == "MRAA": + CE_PIN = 15 # for GPIO22 +elif RF24_DRIVER == "wiringPi": + CE_PIN = 3 # for GPIO22 +else: + CE_PIN = 22 +radio = RF24(CE_PIN, CSN_PIN) # using the python keyword global is bad practice. Instead we'll use a 1 item # list to store our integer number for the payloads' counter @@ -55,7 +64,7 @@ radio.open_rx_pipe(1, address[not radio_number]) # using pipe 1 # for debugging -radio.print_pretty_details() +# radio.print_pretty_details() def master(count: int = 5): # count = 5 will only transmit 5 packets @@ -154,33 +163,12 @@ def set_role(): return set_role() -print(sys.argv[0]) # print example name - - if __name__ == "__main__": - parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter - ) - parser.add_argument( - "-r", - "--role", - type=int, - choices=range(2), - help="'1' specifies the TX role. '0' specifies the RX role.", - ) - args = parser.parse_args() # parse any CLI args - try: - if args.role is None: # if not specified with CLI arg '-r' - while set_role(): - pass # continue example until 'Q' is entered - elif bool(args.role): # if role was set using CLI args, run role once and exit - master() - else: - slave() + while set_role(): + pass # continue example until 'Q' is entered except KeyboardInterrupt: print(" Keyboard Interrupt detected. Exiting...") radio.power = False - sys.exit() else: print(" Run slave() on receiver\n Run master() on transmitter") diff --git a/examples/fake_ble_test.py b/examples/fake_ble_test.py index fe0cfa3..e0f3465 100644 --- a/examples/fake_ble_test.py +++ b/examples/fake_ble_test.py @@ -1,5 +1,7 @@ """ This example uses the nRF24L01 as a 'fake' BLE Beacon + +See documentation at https://nRF24.github.io/pyRF24 """ import time @@ -15,8 +17,15 @@ TemperatureServiceData, ) +print(__file__) # print example name -# initialize the nRF24L01 on the spi bus object as a BLE compliant radio +########### USER CONFIGURATION ########### +# See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md +# Radio CE Pin, CSN Pin, SPI Speed +# CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use +# their own pin numbering +# CS Pin addresses the SPI bus number at /dev/spidev. +# ie: RF24 radio(, *10+); spidev1.0 is 10, spidev1.1 is 11 etc.. CSN_PIN = 0 # aka CE0 on SPI bus 0: /dev/spidev0.0 if RF24_DRIVER == "MRAA": CE_PIN = 15 # for GPIO22 @@ -26,10 +35,6 @@ CE_PIN = 22 radio = RF24(CE_PIN, CSN_PIN) ble = FakeBLE(radio) -# On Linux, csn value is a bit coded -# 0 = bus 0, CE0 # SPI bus 0 is enabled by default -# 10 = bus 1, CE0 # enable SPI bus 2 prior to running this -# 21 = bus 2, CE1 # enable SPI bus 1 prior to running this if not ble.begin(): raise OSError("radio hardware not responding") @@ -197,8 +202,6 @@ def set_role(): return set_role() -print(" nRF24L01 fake BLE beacon test") - if __name__ == "__main__": try: while set_role(): diff --git a/examples/general_network_test.py b/examples/general_network_test.py index bc6d7e4..061f276 100644 --- a/examples/general_network_test.py +++ b/examples/general_network_test.py @@ -1,8 +1,9 @@ """ An all-purpose example of using the nRF24L01 transceiver in a network of nodes. + +See documentation at https://nRF24.github.io/pyRF24 """ -import sys import time import struct from pyrf24 import ( @@ -16,6 +17,7 @@ RF24_DRIVER, ) +print(__file__) # print example name CSN_PIN = 0 # aka CE0 on SPI bus 0: /dev/spidev0.0 if RF24_DRIVER == "MRAA": @@ -28,7 +30,6 @@ network = RF24Network(radio) mesh = RF24Mesh(radio, network) -print(sys.argv[0]) IS_MESH = ( ( input("Would you like to run as a mesh network node (y/n)? Defaults to 'Y' ") diff --git a/examples/getting_started.py b/examples/getting_started.py index 33ac00e..7187796 100644 --- a/examples/getting_started.py +++ b/examples/getting_started.py @@ -1,13 +1,14 @@ """ Simple example of using the RF24 class. + +See documentation at https://nRF24.github.io/pyRF24 """ -import sys -import argparse import time import struct from pyrf24 import RF24, RF24_PA_LOW, RF24_DRIVER +print(__file__) # print example name ########### USER CONFIGURATION ########### # See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md @@ -62,7 +63,9 @@ radio.payload_size = struct.calcsize("= 125: + if curr_channel >= 124: sweeps += 1 - if sweeps % 100 == 0: + if int(sweeps / 100) > 0: endl = True - curr_channel = curr_channel + 1 if curr_channel < 125 else 0 + sweeps = 0 # output the signal counts per channel sig_cnt = signals[curr_channel] @@ -96,6 +99,10 @@ def scan(timeout: int = 30): sep="", end="" if curr_channel < 125 else ("\n" if endl else "\r"), ) + curr_channel = curr_channel + 1 if curr_channel < 125 else 0 + if endl: + signals = [0] * 126 # reset the signal counts for new line + # finish printing results and end with a new line while curr_channel < len(signals) - 1: diff --git a/examples/scanner_curses.py b/examples/scanner_curses.py index 085f202..bf65774 100644 --- a/examples/scanner_curses.py +++ b/examples/scanner_curses.py @@ -6,15 +6,27 @@ See documentation at https://nRF24.github.io/pyRF24 """ -# pylint: disable=no-member import curses import time from typing import List, Tuple, Any - -from pyrf24 import RF24, RF24_1MBPS, RF24_2MBPS, RF24_250KBPS - -CSN_PIN = 0 # connected to GPIO8 -CE_PIN = 22 # connected to GPIO22 +from pyrf24 import RF24, RF24_1MBPS, RF24_2MBPS, RF24_250KBPS, RF24_DRIVER + +print(__file__) # print example name + +########### USER CONFIGURATION ########### +# See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md +# Radio CE Pin, CSN Pin, SPI Speed +# CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use +# their own pin numbering +# CS Pin addresses the SPI bus number at /dev/spidev. +# ie: RF24 radio(, *10+); spidev1.0 is 10, spidev1.1 is 11 etc.. +CSN_PIN = 0 # aka CE0 on SPI bus 0: /dev/spidev0.0 +if RF24_DRIVER == "MRAA": + CE_PIN = 15 # for GPIO22 +elif RF24_DRIVER == "wiringPi": + CE_PIN = 3 # for GPIO22 +else: + CE_PIN = 22 radio = RF24(CE_PIN, CSN_PIN) diff --git a/examples/streaming_data.py b/examples/streaming_data.py index 1c1ccd4..9de6389 100644 --- a/examples/streaming_data.py +++ b/examples/streaming_data.py @@ -1,12 +1,13 @@ """ Example of library usage for streaming multiple payloads. + +See documentation at https://nRF24.github.io/pyRF24 """ -import sys -import argparse import time from pyrf24 import RF24, RF24_PA_LOW, RF24_DRIVER +print(__file__) # print example name ########### USER CONFIGURATION ########### # See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md @@ -160,33 +161,12 @@ def set_role(): return set_role() -print(sys.argv[0]) # print example name - - if __name__ == "__main__": - parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter - ) - parser.add_argument( - "-r", - "--role", - type=int, - choices=range(2), - help="'1' specifies the TX role. '0' specifies the RX role.", - ) - args = parser.parse_args() # parse any CLI args - try: - if args.role is None: # if not specified with CLI arg '-r' - while set_role(): - pass # continue example until 'Q' is entered - elif bool(args.role): # if role was set using CLI args, run role once and exit - master() - else: - slave() + while set_role(): + pass # continue example until 'Q' is entered except KeyboardInterrupt: print(" Keyboard Interrupt detected. Exiting...") radio.power = False - sys.exit() else: print(" Run slave() on receiver\n Run master() on transmitter") diff --git a/src/linux/gpio.h b/src/linux/gpio.h new file mode 100644 index 0000000..cb9966d --- /dev/null +++ b/src/linux/gpio.h @@ -0,0 +1,531 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * - userspace ABI for the GPIO character devices + * + * Copyright (C) 2016 Linus Walleij + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ +#ifndef _UAPI_GPIO_H_ +#define _UAPI_GPIO_H_ + +#include +#include +#include + +/* + * The maximum size of name and label arrays. + * + * Must be a multiple of 8 to ensure 32/64-bit alignment of structs. + */ +#define GPIO_MAX_NAME_SIZE 32 + +/** + * struct gpiochip_info - Information about a certain GPIO chip + * @name: the Linux kernel name of this GPIO chip + * @label: a functional name for this GPIO chip, such as a product + * number, may be empty (i.e. label[0] == '\0') + * @lines: number of GPIO lines on this chip + */ +struct gpiochip_info { + char name[GPIO_MAX_NAME_SIZE]; + char label[GPIO_MAX_NAME_SIZE]; + __u32 lines; +}; + +/* + * Maximum number of requested lines. + * + * Must be no greater than 64, as bitmaps are restricted here to 64-bits + * for simplicity, and a multiple of 2 to ensure 32/64-bit alignment of + * structs. + */ +#define GPIO_V2_LINES_MAX 64 + +/* + * The maximum number of configuration attributes associated with a line + * request. + */ +#define GPIO_V2_LINE_NUM_ATTRS_MAX 10 + +/** + * enum gpio_v2_line_flag - &struct gpio_v2_line_attribute.flags values + * @GPIO_V2_LINE_FLAG_USED: line is not available for request + * @GPIO_V2_LINE_FLAG_ACTIVE_LOW: line active state is physical low + * @GPIO_V2_LINE_FLAG_INPUT: line is an input + * @GPIO_V2_LINE_FLAG_OUTPUT: line is an output + * @GPIO_V2_LINE_FLAG_EDGE_RISING: line detects rising (inactive to active) + * edges + * @GPIO_V2_LINE_FLAG_EDGE_FALLING: line detects falling (active to + * inactive) edges + * @GPIO_V2_LINE_FLAG_OPEN_DRAIN: line is an open drain output + * @GPIO_V2_LINE_FLAG_OPEN_SOURCE: line is an open source output + * @GPIO_V2_LINE_FLAG_BIAS_PULL_UP: line has pull-up bias enabled + * @GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN: line has pull-down bias enabled + * @GPIO_V2_LINE_FLAG_BIAS_DISABLED: line has bias disabled + * @GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME: line events contain REALTIME timestamps + * @GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE: line events contain timestamps from + * hardware timestamp engine + */ +enum gpio_v2_line_flag { + GPIO_V2_LINE_FLAG_USED = _BITULL(0), + GPIO_V2_LINE_FLAG_ACTIVE_LOW = _BITULL(1), + GPIO_V2_LINE_FLAG_INPUT = _BITULL(2), + GPIO_V2_LINE_FLAG_OUTPUT = _BITULL(3), + GPIO_V2_LINE_FLAG_EDGE_RISING = _BITULL(4), + GPIO_V2_LINE_FLAG_EDGE_FALLING = _BITULL(5), + GPIO_V2_LINE_FLAG_OPEN_DRAIN = _BITULL(6), + GPIO_V2_LINE_FLAG_OPEN_SOURCE = _BITULL(7), + GPIO_V2_LINE_FLAG_BIAS_PULL_UP = _BITULL(8), + GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN = _BITULL(9), + GPIO_V2_LINE_FLAG_BIAS_DISABLED = _BITULL(10), + GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME = _BITULL(11), + GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE = _BITULL(12), +}; + +/** + * struct gpio_v2_line_values - Values of GPIO lines + * @bits: a bitmap containing the value of the lines, set to 1 for active + * and 0 for inactive. + * @mask: a bitmap identifying the lines to get or set, with each bit + * number corresponding to the index into &struct + * gpio_v2_line_request.offsets. + */ +struct gpio_v2_line_values { + __aligned_u64 bits; + __aligned_u64 mask; +}; + +/** + * enum gpio_v2_line_attr_id - &struct gpio_v2_line_attribute.id values + * identifying which field of the attribute union is in use. + * @GPIO_V2_LINE_ATTR_ID_FLAGS: flags field is in use + * @GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES: values field is in use + * @GPIO_V2_LINE_ATTR_ID_DEBOUNCE: debounce_period_us field is in use + */ +enum gpio_v2_line_attr_id { + GPIO_V2_LINE_ATTR_ID_FLAGS = 1, + GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES = 2, + GPIO_V2_LINE_ATTR_ID_DEBOUNCE = 3, +}; + +/** + * struct gpio_v2_line_attribute - a configurable attribute of a line + * @id: attribute identifier with value from &enum gpio_v2_line_attr_id + * @padding: reserved for future use and must be zero filled + * @flags: if id is %GPIO_V2_LINE_ATTR_ID_FLAGS, the flags for the GPIO + * line, with values from &enum gpio_v2_line_flag, such as + * %GPIO_V2_LINE_FLAG_ACTIVE_LOW, %GPIO_V2_LINE_FLAG_OUTPUT etc, added + * together. This overrides the default flags contained in the &struct + * gpio_v2_line_config for the associated line. + * @values: if id is %GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES, a bitmap + * containing the values to which the lines will be set, with each bit + * number corresponding to the index into &struct + * gpio_v2_line_request.offsets. + * @debounce_period_us: if id is %GPIO_V2_LINE_ATTR_ID_DEBOUNCE, the + * desired debounce period, in microseconds + */ +struct gpio_v2_line_attribute { + __u32 id; + __u32 padding; + union { + __aligned_u64 flags; + __aligned_u64 values; + __u32 debounce_period_us; + }; +}; + +/** + * struct gpio_v2_line_config_attribute - a configuration attribute + * associated with one or more of the requested lines. + * @attr: the configurable attribute + * @mask: a bitmap identifying the lines to which the attribute applies, + * with each bit number corresponding to the index into &struct + * gpio_v2_line_request.offsets. + */ +struct gpio_v2_line_config_attribute { + struct gpio_v2_line_attribute attr; + __aligned_u64 mask; +}; + +/** + * struct gpio_v2_line_config - Configuration for GPIO lines + * @flags: flags for the GPIO lines, with values from &enum + * gpio_v2_line_flag, such as %GPIO_V2_LINE_FLAG_ACTIVE_LOW, + * %GPIO_V2_LINE_FLAG_OUTPUT etc, added together. This is the default for + * all requested lines but may be overridden for particular lines using + * @attrs. + * @num_attrs: the number of attributes in @attrs + * @padding: reserved for future use and must be zero filled + * @attrs: the configuration attributes associated with the requested + * lines. Any attribute should only be associated with a particular line + * once. If an attribute is associated with a line multiple times then the + * first occurrence (i.e. lowest index) has precedence. + */ +struct gpio_v2_line_config { + __aligned_u64 flags; + __u32 num_attrs; + /* Pad to fill implicit padding and reserve space for future use. */ + __u32 padding[5]; + struct gpio_v2_line_config_attribute attrs[GPIO_V2_LINE_NUM_ATTRS_MAX]; +}; + +/** + * struct gpio_v2_line_request - Information about a request for GPIO lines + * @offsets: an array of desired lines, specified by offset index for the + * associated GPIO chip + * @consumer: a desired consumer label for the selected GPIO lines such as + * "my-bitbanged-relay" + * @config: requested configuration for the lines. + * @num_lines: number of lines requested in this request, i.e. the number + * of valid fields in the %GPIO_V2_LINES_MAX sized arrays, set to 1 to + * request a single line + * @event_buffer_size: a suggested minimum number of line events that the + * kernel should buffer. This is only relevant if edge detection is + * enabled in the configuration. Note that this is only a suggested value + * and the kernel may allocate a larger buffer or cap the size of the + * buffer. If this field is zero then the buffer size defaults to a minimum + * of @num_lines * 16. + * @padding: reserved for future use and must be zero filled + * @fd: if successful this field will contain a valid anonymous file handle + * after a %GPIO_GET_LINE_IOCTL operation, zero or negative value means + * error + */ +struct gpio_v2_line_request { + __u32 offsets[GPIO_V2_LINES_MAX]; + char consumer[GPIO_MAX_NAME_SIZE]; + struct gpio_v2_line_config config; + __u32 num_lines; + __u32 event_buffer_size; + /* Pad to fill implicit padding and reserve space for future use. */ + __u32 padding[5]; + __s32 fd; +}; + +/** + * struct gpio_v2_line_info - Information about a certain GPIO line + * @name: the name of this GPIO line, such as the output pin of the line on + * the chip, a rail or a pin header name on a board, as specified by the + * GPIO chip, may be empty (i.e. name[0] == '\0') + * @consumer: a functional name for the consumer of this GPIO line as set + * by whatever is using it, will be empty if there is no current user but + * may also be empty if the consumer doesn't set this up + * @offset: the local offset on this GPIO chip, fill this in when + * requesting the line information from the kernel + * @num_attrs: the number of attributes in @attrs + * @flags: flags for this GPIO line, with values from &enum + * gpio_v2_line_flag, such as %GPIO_V2_LINE_FLAG_ACTIVE_LOW, + * %GPIO_V2_LINE_FLAG_OUTPUT etc, added together. + * @attrs: the configuration attributes associated with the line + * @padding: reserved for future use + */ +struct gpio_v2_line_info { + char name[GPIO_MAX_NAME_SIZE]; + char consumer[GPIO_MAX_NAME_SIZE]; + __u32 offset; + __u32 num_attrs; + __aligned_u64 flags; + struct gpio_v2_line_attribute attrs[GPIO_V2_LINE_NUM_ATTRS_MAX]; + /* Space reserved for future use. */ + __u32 padding[4]; +}; + +/** + * enum gpio_v2_line_changed_type - &struct gpio_v2_line_changed.event_type + * values + * @GPIO_V2_LINE_CHANGED_REQUESTED: line has been requested + * @GPIO_V2_LINE_CHANGED_RELEASED: line has been released + * @GPIO_V2_LINE_CHANGED_CONFIG: line has been reconfigured + */ +enum gpio_v2_line_changed_type { + GPIO_V2_LINE_CHANGED_REQUESTED = 1, + GPIO_V2_LINE_CHANGED_RELEASED = 2, + GPIO_V2_LINE_CHANGED_CONFIG = 3, +}; + +/** + * struct gpio_v2_line_info_changed - Information about a change in status + * of a GPIO line + * @info: updated line information + * @timestamp_ns: estimate of time of status change occurrence, in nanoseconds + * @event_type: the type of change with a value from &enum + * gpio_v2_line_changed_type + * @padding: reserved for future use + */ +struct gpio_v2_line_info_changed { + struct gpio_v2_line_info info; + __aligned_u64 timestamp_ns; + __u32 event_type; + /* Pad struct to 64-bit boundary and reserve space for future use. */ + __u32 padding[5]; +}; + +/** + * enum gpio_v2_line_event_id - &struct gpio_v2_line_event.id values + * @GPIO_V2_LINE_EVENT_RISING_EDGE: event triggered by a rising edge + * @GPIO_V2_LINE_EVENT_FALLING_EDGE: event triggered by a falling edge + */ +enum gpio_v2_line_event_id { + GPIO_V2_LINE_EVENT_RISING_EDGE = 1, + GPIO_V2_LINE_EVENT_FALLING_EDGE = 2, +}; + +/** + * struct gpio_v2_line_event - The actual event being pushed to userspace + * @timestamp_ns: best estimate of time of event occurrence, in nanoseconds. + * @id: event identifier with value from &enum gpio_v2_line_event_id + * @offset: the offset of the line that triggered the event + * @seqno: the sequence number for this event in the sequence of events for + * all the lines in this line request + * @line_seqno: the sequence number for this event in the sequence of + * events on this particular line + * @padding: reserved for future use + * + * By default the @timestamp_ns is read from %CLOCK_MONOTONIC and is + * intended to allow the accurate measurement of the time between events. + * It does not provide the wall-clock time. + * + * If the %GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME flag is set then the + * @timestamp_ns is read from %CLOCK_REALTIME. + */ +struct gpio_v2_line_event { + __aligned_u64 timestamp_ns; + __u32 id; + __u32 offset; + __u32 seqno; + __u32 line_seqno; + /* Space reserved for future use. */ + __u32 padding[6]; +}; + +/* + * ABI v1 + * + * This version of the ABI is deprecated. + * Use the latest version of the ABI, defined above, instead. + */ + +/* Informational flags */ +#define GPIOLINE_FLAG_KERNEL (1UL << 0) /* Line used by the kernel */ +#define GPIOLINE_FLAG_IS_OUT (1UL << 1) +#define GPIOLINE_FLAG_ACTIVE_LOW (1UL << 2) +#define GPIOLINE_FLAG_OPEN_DRAIN (1UL << 3) +#define GPIOLINE_FLAG_OPEN_SOURCE (1UL << 4) +#define GPIOLINE_FLAG_BIAS_PULL_UP (1UL << 5) +#define GPIOLINE_FLAG_BIAS_PULL_DOWN (1UL << 6) +#define GPIOLINE_FLAG_BIAS_DISABLE (1UL << 7) + +/** + * struct gpioline_info - Information about a certain GPIO line + * @line_offset: the local offset on this GPIO device, fill this in when + * requesting the line information from the kernel + * @flags: various flags for this line + * @name: the name of this GPIO line, such as the output pin of the line on the + * chip, a rail or a pin header name on a board, as specified by the gpio + * chip, may be empty (i.e. name[0] == '\0') + * @consumer: a functional name for the consumer of this GPIO line as set by + * whatever is using it, will be empty if there is no current user but may + * also be empty if the consumer doesn't set this up + * + * Note: This struct is part of ABI v1 and is deprecated. + * Use &struct gpio_v2_line_info instead. + */ +struct gpioline_info { + __u32 line_offset; + __u32 flags; + char name[GPIO_MAX_NAME_SIZE]; + char consumer[GPIO_MAX_NAME_SIZE]; +}; + +/* Maximum number of requested handles */ +#define GPIOHANDLES_MAX 64 + +/* Possible line status change events */ +enum { + GPIOLINE_CHANGED_REQUESTED = 1, + GPIOLINE_CHANGED_RELEASED, + GPIOLINE_CHANGED_CONFIG, +}; + +/** + * struct gpioline_info_changed - Information about a change in status + * of a GPIO line + * @info: updated line information + * @timestamp: estimate of time of status change occurrence, in nanoseconds + * @event_type: one of %GPIOLINE_CHANGED_REQUESTED, + * %GPIOLINE_CHANGED_RELEASED and %GPIOLINE_CHANGED_CONFIG + * @padding: reserved for future use + * + * The &struct gpioline_info embedded here has 32-bit alignment on its own, + * but it works fine with 64-bit alignment too. With its 72 byte size, we can + * guarantee there are no implicit holes between it and subsequent members. + * The 20-byte padding at the end makes sure we don't add any implicit padding + * at the end of the structure on 64-bit architectures. + * + * Note: This struct is part of ABI v1 and is deprecated. + * Use &struct gpio_v2_line_info_changed instead. + */ +struct gpioline_info_changed { + struct gpioline_info info; + __u64 timestamp; + __u32 event_type; + __u32 padding[5]; /* for future use */ +}; + +/* Linerequest flags */ +#define GPIOHANDLE_REQUEST_INPUT (1UL << 0) +#define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1) +#define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2) +#define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3) +#define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4) +#define GPIOHANDLE_REQUEST_BIAS_PULL_UP (1UL << 5) +#define GPIOHANDLE_REQUEST_BIAS_PULL_DOWN (1UL << 6) +#define GPIOHANDLE_REQUEST_BIAS_DISABLE (1UL << 7) + +/** + * struct gpiohandle_request - Information about a GPIO handle request + * @lineoffsets: an array of desired lines, specified by offset index for the + * associated GPIO device + * @flags: desired flags for the desired GPIO lines, such as + * %GPIOHANDLE_REQUEST_OUTPUT, %GPIOHANDLE_REQUEST_ACTIVE_LOW etc, added + * together. Note that even if multiple lines are requested, the same flags + * must be applicable to all of them, if you want lines with individual + * flags set, request them one by one. It is possible to select + * a batch of input or output lines, but they must all have the same + * characteristics, i.e. all inputs or all outputs, all active low etc + * @default_values: if the %GPIOHANDLE_REQUEST_OUTPUT is set for a requested + * line, this specifies the default output value, should be 0 (low) or + * 1 (high), anything else than 0 or 1 will be interpreted as 1 (high) + * @consumer_label: a desired consumer label for the selected GPIO line(s) + * such as "my-bitbanged-relay" + * @lines: number of lines requested in this request, i.e. the number of + * valid fields in the above arrays, set to 1 to request a single line + * @fd: if successful this field will contain a valid anonymous file handle + * after a %GPIO_GET_LINEHANDLE_IOCTL operation, zero or negative value + * means error + * + * Note: This struct is part of ABI v1 and is deprecated. + * Use &struct gpio_v2_line_request instead. + */ +struct gpiohandle_request { + __u32 lineoffsets[GPIOHANDLES_MAX]; + __u32 flags; + __u8 default_values[GPIOHANDLES_MAX]; + char consumer_label[GPIO_MAX_NAME_SIZE]; + __u32 lines; + int fd; +}; + +/** + * struct gpiohandle_config - Configuration for a GPIO handle request + * @flags: updated flags for the requested GPIO lines, such as + * %GPIOHANDLE_REQUEST_OUTPUT, %GPIOHANDLE_REQUEST_ACTIVE_LOW etc, added + * together + * @default_values: if the %GPIOHANDLE_REQUEST_OUTPUT is set in flags, + * this specifies the default output value, should be 0 (low) or + * 1 (high), anything else than 0 or 1 will be interpreted as 1 (high) + * @padding: reserved for future use and should be zero filled + * + * Note: This struct is part of ABI v1 and is deprecated. + * Use &struct gpio_v2_line_config instead. + */ +struct gpiohandle_config { + __u32 flags; + __u8 default_values[GPIOHANDLES_MAX]; + __u32 padding[4]; /* padding for future use */ +}; + +/** + * struct gpiohandle_data - Information of values on a GPIO handle + * @values: when getting the state of lines this contains the current + * state of a line, when setting the state of lines these should contain + * the desired target state + * + * Note: This struct is part of ABI v1 and is deprecated. + * Use &struct gpio_v2_line_values instead. + */ +struct gpiohandle_data { + __u8 values[GPIOHANDLES_MAX]; +}; + +/* Eventrequest flags */ +#define GPIOEVENT_REQUEST_RISING_EDGE (1UL << 0) +#define GPIOEVENT_REQUEST_FALLING_EDGE (1UL << 1) +#define GPIOEVENT_REQUEST_BOTH_EDGES ((1UL << 0) | (1UL << 1)) + +/** + * struct gpioevent_request - Information about a GPIO event request + * @lineoffset: the desired line to subscribe to events from, specified by + * offset index for the associated GPIO device + * @handleflags: desired handle flags for the desired GPIO line, such as + * %GPIOHANDLE_REQUEST_ACTIVE_LOW or %GPIOHANDLE_REQUEST_OPEN_DRAIN + * @eventflags: desired flags for the desired GPIO event line, such as + * %GPIOEVENT_REQUEST_RISING_EDGE or %GPIOEVENT_REQUEST_FALLING_EDGE + * @consumer_label: a desired consumer label for the selected GPIO line(s) + * such as "my-listener" + * @fd: if successful this field will contain a valid anonymous file handle + * after a %GPIO_GET_LINEEVENT_IOCTL operation, zero or negative value + * means error + * + * Note: This struct is part of ABI v1 and is deprecated. + * Use &struct gpio_v2_line_request instead. + */ +struct gpioevent_request { + __u32 lineoffset; + __u32 handleflags; + __u32 eventflags; + char consumer_label[GPIO_MAX_NAME_SIZE]; + int fd; +}; + +/* + * GPIO event types + */ +#define GPIOEVENT_EVENT_RISING_EDGE 0x01 +#define GPIOEVENT_EVENT_FALLING_EDGE 0x02 + +/** + * struct gpioevent_data - The actual event being pushed to userspace + * @timestamp: best estimate of time of event occurrence, in nanoseconds + * @id: event identifier + * + * Note: This struct is part of ABI v1 and is deprecated. + * Use &struct gpio_v2_line_event instead. + */ +struct gpioevent_data { + __u64 timestamp; + __u32 id; +}; + +/* + * v1 and v2 ioctl()s + */ +#define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info) +#define GPIO_GET_LINEINFO_UNWATCH_IOCTL _IOWR(0xB4, 0x0C, __u32) + +/* + * v2 ioctl()s + */ +#define GPIO_V2_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x05, struct gpio_v2_line_info) +#define GPIO_V2_GET_LINEINFO_WATCH_IOCTL _IOWR(0xB4, 0x06, struct gpio_v2_line_info) +#define GPIO_V2_GET_LINE_IOCTL _IOWR(0xB4, 0x07, struct gpio_v2_line_request) +#define GPIO_V2_LINE_SET_CONFIG_IOCTL _IOWR(0xB4, 0x0D, struct gpio_v2_line_config) +#define GPIO_V2_LINE_GET_VALUES_IOCTL _IOWR(0xB4, 0x0E, struct gpio_v2_line_values) +#define GPIO_V2_LINE_SET_VALUES_IOCTL _IOWR(0xB4, 0x0F, struct gpio_v2_line_values) + +/* + * v1 ioctl()s + * + * These ioctl()s are deprecated. Use the v2 equivalent instead. + */ +#define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info) +#define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request) +#define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request) +#define GPIOHANDLE_GET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x08, struct gpiohandle_data) +#define GPIOHANDLE_SET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x09, struct gpiohandle_data) +#define GPIOHANDLE_SET_CONFIG_IOCTL _IOWR(0xB4, 0x0A, struct gpiohandle_config) +#define GPIO_GET_LINEINFO_WATCH_IOCTL _IOWR(0xB4, 0x0B, struct gpioline_info) + +#endif /* _UAPI_GPIO_H_ */ diff --git a/src/pyRF24.cpp b/src/pyRF24.cpp index 7afe84d..0f7d2fa 100644 --- a/src/pyRF24.cpp +++ b/src/pyRF24.cpp @@ -1,245 +1,5 @@ #include -#include -#include - -namespace py = pybind11; - -void throw_ba_exception(void) -{ - PyErr_SetString(PyExc_TypeError, "buf parameter must be bytes or bytearray"); - py::error_already_set(); -} - -char* get_bytes_or_bytearray_str(py::object buf) -{ - PyObject* py_ba; - py_ba = buf.ptr(); - if (PyByteArray_Check(py_ba)) - return PyByteArray_AsString(py_ba); - else if (PyBytes_Check(py_ba)) - return PyBytes_AsString(py_ba); - else - throw_ba_exception(); - - return NULL; -} - -int get_bytes_or_bytearray_ln(py::object buf) -{ - PyObject* py_ba; - py_ba = buf.ptr(); - if (PyByteArray_Check(py_ba)) - return PyByteArray_Size(py_ba); - else if (PyBytes_Check(py_ba)) - return PyBytes_Size(py_ba); - else - throw_ba_exception(); - - return 0; -} - -class RF24Wrapper : public RF24 -{ - -public: - RF24Wrapper(rf24_gpio_pin_t _ce_pin, rf24_gpio_pin_t _csn_pin, uint32_t _spi_speed = 10000000) : RF24(_ce_pin, _csn_pin, _spi_speed) - { - } - - RF24Wrapper(uint32_t _spi_speed = 10000000) : RF24(_spi_speed) - { - } - - // needed for polymorphic recognition - virtual ~RF24Wrapper() = default; - - std::tuple available_pipe() - { - uint8_t pipe = 7; - bool is_available = RF24::available(&pipe); - return std::tuple(is_available, pipe); - } - - std::tuple what_happened() - { - bool ds = 0, df = 0, dr = 0; - RF24::whatHappened(ds, df, dr); - return std::tuple(ds, df, dr); - } - - void open_tx_pipe(py::buffer address) - { - RF24::openWritingPipe(reinterpret_cast(get_bytes_or_bytearray_str(address))); - } - - void open_rx_pipe(uint8_t number, py::buffer address) - { - RF24::openReadingPipe(number, reinterpret_cast(get_bytes_or_bytearray_str(address))); - } - - py::bytearray read(uint8_t length = 0) - { - if (!length) - length = RF24::dynamic_payloads_enabled ? RF24::getDynamicPayloadSize() : RF24::getPayloadSize(); - else - length = rf24_min(length, static_cast(32)); - char* payload = new char[length + 1]; - RF24::read(payload, length); - payload[length] = '\0'; - py::bytearray buf = py::bytearray(payload, length); - delete[] payload; - return buf; - } - - void startFastWrite(py::buffer buf, const bool multicast = false, bool startTx = true) - { - RF24::startFastWrite( - get_bytes_or_bytearray_str(buf), - static_cast(get_bytes_or_bytearray_ln(buf)), - multicast, startTx); - } - - bool startWrite(py::buffer buf, const bool multicast) - { - return RF24::startWrite( - get_bytes_or_bytearray_str(buf), - static_cast(get_bytes_or_bytearray_ln(buf)), - multicast); - } - - bool writeFast(py::buffer buf, const bool multicast = false) - { - return RF24::writeFast( - get_bytes_or_bytearray_str(buf), - static_cast(get_bytes_or_bytearray_ln(buf)), - multicast); - } - - bool write(py::buffer buf, const bool multicast = false) - { - return RF24::write( - get_bytes_or_bytearray_str(buf), - static_cast(get_bytes_or_bytearray_ln(buf)), - multicast); - } - - bool writeBlocking(py::buffer buf, uint32_t timeout) - { - return RF24::writeBlocking( - get_bytes_or_bytearray_str(buf), - static_cast(get_bytes_or_bytearray_ln(buf)), - timeout); - } - - bool writeAckPayload(uint8_t pipe, py::buffer buf) - { - return RF24::writeAckPayload( - pipe, - get_bytes_or_bytearray_str(buf), - static_cast(get_bytes_or_bytearray_ln(buf))); - } - - char* sprintfDetails() - { - char* debug_info = new char[870]; - RF24::sprintfPrettyDetails(debug_info); - return debug_info; - } - - void set_radiation(rf24_pa_dbm_e level, rf24_datarate_e speed, bool lna_enable = true) - { - RF24::setRadiation(level, speed, lna_enable); - } - - /*********************************************************************************/ - /* wrappers for python-like properties */ - - uint8_t get_address_width() - { - return RF24::addr_width; - } - - bool is_ack_payloads_enabled() - { - return RF24::ack_payloads_enabled; - } - - void toggle_ack_payloads(const bool enable) - { - if (enable) - RF24::enableAckPayload(); - else - RF24::disableAckPayload(); - } - - bool is_dynamic_payloads_enabled() - { - return RF24::dynamic_payloads_enabled; - } - - void dynamic_payloads(const bool enable) - { - if (enable) - RF24::enableDynamicPayloads(); - else - RF24::disableDynamicPayloads(); - } - - bool isPowerUp() - { - return read_register(NRF_CONFIG) & _BV(PWR_UP); - } - - void set_pa_level(rf24_pa_dbm_e level) - { - RF24::setPALevel(level); - } - - rf24_pa_dbm_e get_pa_level() - { - uint8_t ret_val = RF24::getPALevel(); - if (ret_val == RF24_PA_MAX) - return RF24_PA_MAX; - if (ret_val == RF24_PA_HIGH) - return RF24_PA_HIGH; - if (ret_val == RF24_PA_LOW) - return RF24_PA_LOW; - // if (ret_val == RF24_PA_MIN) - return RF24_PA_MIN; - } - - rf24_datarate_e get_data_rate() - { - uint8_t ret_val = RF24::getDataRate(); - if (ret_val == RF24_2MBPS) - return RF24_2MBPS; - if (ret_val == RF24_1MBPS) - return RF24_1MBPS; - // if (ret_val == RF24_250KBPS) - return RF24_250KBPS; - } - - void power(const bool enable) - { - if (enable) - powerUp(); - else - powerDown(); - } - - bool isListening() - { - return read_register(NRF_CONFIG) & _BV(PRIM_RX); - } - - void listen(const bool enable) - { - if (enable) - startListening(); - else - stopListening(); - } -}; +#include "pyRF24.h" PYBIND11_MODULE(rf24, m) { diff --git a/src/pyRF24.h b/src/pyRF24.h new file mode 100644 index 0000000..584ac11 --- /dev/null +++ b/src/pyRF24.h @@ -0,0 +1,242 @@ +#include +#include +#include + +namespace py = pybind11; + +void throw_ba_exception(void) +{ + PyErr_SetString(PyExc_TypeError, "buf parameter must be bytes or bytearray"); + py::error_already_set(); +} + +char* get_bytes_or_bytearray_str(py::object buf) +{ + PyObject* py_ba; + py_ba = buf.ptr(); + if (PyByteArray_Check(py_ba)) + return PyByteArray_AsString(py_ba); + else if (PyBytes_Check(py_ba)) + return PyBytes_AsString(py_ba); + else + throw_ba_exception(); + + return NULL; +} + +int get_bytes_or_bytearray_ln(py::object buf) +{ + PyObject* py_ba; + py_ba = buf.ptr(); + if (PyByteArray_Check(py_ba)) + return PyByteArray_Size(py_ba); + else if (PyBytes_Check(py_ba)) + return PyBytes_Size(py_ba); + else + throw_ba_exception(); + + return 0; +} + +class RF24Wrapper : public RF24 +{ + +public: + RF24Wrapper(rf24_gpio_pin_t _ce_pin, rf24_gpio_pin_t _csn_pin, uint32_t _spi_speed = 10000000) : RF24(_ce_pin, _csn_pin, _spi_speed) + { + } + + RF24Wrapper(uint32_t _spi_speed = 10000000) : RF24(_spi_speed) + { + } + + // needed for polymorphic recognition + virtual ~RF24Wrapper() = default; + + std::tuple available_pipe() + { + uint8_t pipe = 7; + bool is_available = RF24::available(&pipe); + return std::tuple(is_available, pipe); + } + + std::tuple what_happened() + { + bool ds = 0, df = 0, dr = 0; + RF24::whatHappened(ds, df, dr); + return std::tuple(ds, df, dr); + } + + void open_tx_pipe(py::buffer address) + { + RF24::openWritingPipe(reinterpret_cast(get_bytes_or_bytearray_str(address))); + } + + void open_rx_pipe(uint8_t number, py::buffer address) + { + RF24::openReadingPipe(number, reinterpret_cast(get_bytes_or_bytearray_str(address))); + } + + py::bytearray read(uint8_t length = 0) + { + if (!length) + length = RF24::dynamic_payloads_enabled ? RF24::getDynamicPayloadSize() : RF24::getPayloadSize(); + else + length = rf24_min(length, static_cast(32)); + char* payload = new char[length + 1]; + RF24::read(payload, length); + payload[length] = '\0'; + py::bytearray buf = py::bytearray(payload, length); + delete[] payload; + return buf; + } + + void startFastWrite(py::buffer buf, const bool multicast = false, bool startTx = true) + { + RF24::startFastWrite( + get_bytes_or_bytearray_str(buf), + static_cast(get_bytes_or_bytearray_ln(buf)), + multicast, startTx); + } + + bool startWrite(py::buffer buf, const bool multicast) + { + return RF24::startWrite( + get_bytes_or_bytearray_str(buf), + static_cast(get_bytes_or_bytearray_ln(buf)), + multicast); + } + + bool writeFast(py::buffer buf, const bool multicast = false) + { + return RF24::writeFast( + get_bytes_or_bytearray_str(buf), + static_cast(get_bytes_or_bytearray_ln(buf)), + multicast); + } + + bool write(py::buffer buf, const bool multicast = false) + { + return RF24::write( + get_bytes_or_bytearray_str(buf), + static_cast(get_bytes_or_bytearray_ln(buf)), + multicast); + } + + bool writeBlocking(py::buffer buf, uint32_t timeout) + { + return RF24::writeBlocking( + get_bytes_or_bytearray_str(buf), + static_cast(get_bytes_or_bytearray_ln(buf)), + timeout); + } + + bool writeAckPayload(uint8_t pipe, py::buffer buf) + { + return RF24::writeAckPayload( + pipe, + get_bytes_or_bytearray_str(buf), + static_cast(get_bytes_or_bytearray_ln(buf))); + } + + char* sprintfDetails() + { + char* debug_info = new char[870]; + RF24::sprintfPrettyDetails(debug_info); + return debug_info; + } + + void set_radiation(rf24_pa_dbm_e level, rf24_datarate_e speed, bool lna_enable = true) + { + RF24::setRadiation(level, speed, lna_enable); + } + + /*********************************************************************************/ + /* wrappers for python-like properties */ + + uint8_t get_address_width() + { + return RF24::addr_width; + } + + bool is_ack_payloads_enabled() + { + return RF24::ack_payloads_enabled; + } + + void toggle_ack_payloads(const bool enable) + { + if (enable) + RF24::enableAckPayload(); + else + RF24::disableAckPayload(); + } + + bool is_dynamic_payloads_enabled() + { + return RF24::dynamic_payloads_enabled; + } + + void dynamic_payloads(const bool enable) + { + if (enable) + RF24::enableDynamicPayloads(); + else + RF24::disableDynamicPayloads(); + } + + bool isPowerUp() + { + return read_register(NRF_CONFIG) & _BV(PWR_UP); + } + + void set_pa_level(rf24_pa_dbm_e level) + { + RF24::setPALevel(level); + } + + rf24_pa_dbm_e get_pa_level() + { + uint8_t ret_val = RF24::getPALevel(); + if (ret_val == RF24_PA_MAX) + return RF24_PA_MAX; + if (ret_val == RF24_PA_HIGH) + return RF24_PA_HIGH; + if (ret_val == RF24_PA_LOW) + return RF24_PA_LOW; + // if (ret_val == RF24_PA_MIN) + return RF24_PA_MIN; + } + + rf24_datarate_e get_data_rate() + { + uint8_t ret_val = RF24::getDataRate(); + if (ret_val == RF24_2MBPS) + return RF24_2MBPS; + if (ret_val == RF24_1MBPS) + return RF24_1MBPS; + // if (ret_val == RF24_250KBPS) + return RF24_250KBPS; + } + + void power(const bool enable) + { + if (enable) + powerUp(); + else + powerDown(); + } + + bool isListening() + { + return read_register(NRF_CONFIG) & _BV(PRIM_RX); + } + + void listen(const bool enable) + { + if (enable) + startListening(); + else + stopListening(); + } +}; diff --git a/src/pyRF24Mesh.cpp b/src/pyRF24Mesh.cpp index ea244ee..b4dbddf 100644 --- a/src/pyRF24Mesh.cpp +++ b/src/pyRF24Mesh.cpp @@ -1,52 +1,4 @@ -#include -#include "pyRF24Network.cpp" -#include - -namespace py = pybind11; - -class RF24MeshWrapper : public RF24Mesh -{ -public: - RF24MeshWrapper(RF24Wrapper& _radio, RF24NetworkWrapper& _network) - : RF24Mesh(static_cast(_radio), static_cast(_network)) - { - } - - // needed for polymorphic recognition - virtual ~RF24MeshWrapper() = default; - - bool write(py::buffer buf, uint8_t msg_type, uint8_t nodeID = 0) - { - return RF24Mesh::write( - get_bytes_or_bytearray_str(buf), - msg_type, - static_cast(get_bytes_or_bytearray_ln(buf)), - nodeID); - } - - bool write(uint16_t to_node, py::buffer buf, uint8_t msg_type) - { - return RF24Mesh::write( - to_node, - get_bytes_or_bytearray_str(buf), - msg_type, - static_cast(get_bytes_or_bytearray_ln(buf))); - } - - uint8_t get_node_id() - { - return RF24Mesh::_nodeID; - } - - py::list get_addrList() - { - py::list list; - for (uint8_t i = 0; i < RF24Mesh::addrListTop; ++i) { - list.append(RF24Mesh::addrList[i]); - } - return list; - } -}; +#include "pyRF24Mesh.h" PYBIND11_MODULE(rf24_mesh, m) { diff --git a/src/pyRF24Mesh.h b/src/pyRF24Mesh.h new file mode 100644 index 0000000..193d654 --- /dev/null +++ b/src/pyRF24Mesh.h @@ -0,0 +1,49 @@ +#include +#include "pyRF24Network.cpp" +#include + +namespace py = pybind11; + +class RF24MeshWrapper : public RF24Mesh +{ +public: + RF24MeshWrapper(RF24Wrapper& _radio, RF24NetworkWrapper& _network) + : RF24Mesh(static_cast(_radio), static_cast(_network)) + { + } + + // needed for polymorphic recognition + virtual ~RF24MeshWrapper() = default; + + bool write(py::buffer buf, uint8_t msg_type, uint8_t nodeID = 0) + { + return RF24Mesh::write( + get_bytes_or_bytearray_str(buf), + msg_type, + static_cast(get_bytes_or_bytearray_ln(buf)), + nodeID); + } + + bool write(uint16_t to_node, py::buffer buf, uint8_t msg_type) + { + return RF24Mesh::write( + to_node, + get_bytes_or_bytearray_str(buf), + msg_type, + static_cast(get_bytes_or_bytearray_ln(buf))); + } + + uint8_t get_node_id() + { + return RF24Mesh::_nodeID; + } + + py::list get_addrList() + { + py::list list; + for (uint8_t i = 0; i < RF24Mesh::addrListTop; ++i) { + list.append(RF24Mesh::addrList[i]); + } + return list; + } +}; diff --git a/src/pyRF24Network.cpp b/src/pyRF24Network.cpp index 3d91ed6..20c88b6 100644 --- a/src/pyRF24Network.cpp +++ b/src/pyRF24Network.cpp @@ -1,121 +1,4 @@ -#include -#include "pyRF24.cpp" -#include -// #include -// #include - -namespace py = pybind11; - -/* -namespace pybind11 { -namespace detail { - - template - struct type_caster> : list_caster, Type> - { - }; -} // namespace detail -} // namespace pybind11 - -struct RF24NetworkFrameWrapper : public RF24NetworkFrame -{ - RF24NetworkFrameWrapper() : RF24NetworkFrame() - { - } - - RF24NetworkFrameWrapper(RF24NetworkHeader& header, py::object message) - { - RF24NetworkFrame::header = header; - set_message(message); - } - - py::bytearray get_message() - { - char* buf = new char[RF24NetworkFrame::message_size]; - memcpy(reinterpret_cast(buf), RF24NetworkFrame::message_buffer, RF24NetworkFrame::message_size); - py::bytearray py_ba = py::bytearray(buf, RF24NetworkFrame::message_size); - delete[] buf; - return py_ba; - } - - void set_message(py::object message) - { - RF24NetworkFrame::message_size = static_cast(get_bytes_or_bytearray_ln(message)); - memcpy(RF24NetworkFrame::message_buffer, reinterpret_cast(get_bytes_or_bytearray_str(message)), RF24NetworkFrame::message_size); - } -}; -*/ -class RF24NetworkWrapper : public RF24Network -{ -public: - RF24NetworkWrapper(RF24Wrapper& _radio) : RF24Network(static_cast(_radio)) - { - } - - // needed for polymorphic recognition - virtual ~RF24NetworkWrapper() = default; - - uint16_t peek_header(RF24NetworkHeader& header) - { - return RF24Network::peek(header); - } - - std::tuple peek_frame(uint16_t maxlen = MAX_PAYLOAD_SIZE) - { - RF24NetworkHeader header; - maxlen = static_cast(rf24_min(maxlen, RF24Network::peek(header))); - char* buf = new char[maxlen + 1]; - RF24Network::peek(header, buf, maxlen); - py::bytearray py_ba = py::bytearray(buf, maxlen); - delete[] buf; - return std::tuple(header, py_ba); - } - -#if defined(RF24NetworkMulticast) - bool multicast(RF24NetworkHeader header, py::buffer buf, uint8_t level = 7) - { - return RF24Network::multicast( - header, - get_bytes_or_bytearray_str(buf), - static_cast(get_bytes_or_bytearray_ln(buf)), - level); - } - - void set_multicast_level(uint8_t level) - { - RF24Network::multicastLevel(level); - } - - uint8_t get_multicast_level() - { - return RF24Network::_multicast_level; - } -#endif // defined (RF24NetworkMulticast) - - std::tuple read(uint16_t maxlen = MAX_PAYLOAD_SIZE) - { - char* buf = new char[maxlen + 1]; - RF24NetworkHeader header; - uint16_t len = RF24Network::read(header, buf, maxlen); - py::bytearray py_ba = py::bytearray(buf, len); - delete[] buf; - return std::tuple(header, py_ba); - } - - bool write(RF24NetworkHeader& header, py::buffer buf, uint16_t writeDirect = NETWORK_AUTO_ROUTING) - { - return RF24Network::write( - header, - get_bytes_or_bytearray_str(buf), - static_cast(get_bytes_or_bytearray_ln(buf)), - writeDirect); - } - - uint16_t get_node_address() - { - return RF24Network::node_address; - } -}; +#include "pyRF24Network.h" PYBIND11_MODULE(rf24_network, m) { diff --git a/src/pyRF24Network.h b/src/pyRF24Network.h new file mode 100644 index 0000000..2aca942 --- /dev/null +++ b/src/pyRF24Network.h @@ -0,0 +1,118 @@ +#include +#include "pyRF24.h" +#include +// #include +// #include + +namespace py = pybind11; + +/* +namespace pybind11 { +namespace detail { + + template + struct type_caster> : list_caster, Type> + { + }; +} // namespace detail +} // namespace pybind11 + +struct RF24NetworkFrameWrapper : public RF24NetworkFrame +{ + RF24NetworkFrameWrapper() : RF24NetworkFrame() + { + } + + RF24NetworkFrameWrapper(RF24NetworkHeader& header, py::object message) + { + RF24NetworkFrame::header = header; + set_message(message); + } + + py::bytearray get_message() + { + char* buf = new char[RF24NetworkFrame::message_size]; + memcpy(reinterpret_cast(buf), RF24NetworkFrame::message_buffer, RF24NetworkFrame::message_size); + py::bytearray py_ba = py::bytearray(buf, RF24NetworkFrame::message_size); + delete[] buf; + return py_ba; + } + + void set_message(py::object message) + { + RF24NetworkFrame::message_size = static_cast(get_bytes_or_bytearray_ln(message)); + memcpy(RF24NetworkFrame::message_buffer, reinterpret_cast(get_bytes_or_bytearray_str(message)), RF24NetworkFrame::message_size); + } +}; +*/ +class RF24NetworkWrapper : public RF24Network +{ +public: + RF24NetworkWrapper(RF24Wrapper& _radio) : RF24Network(static_cast(_radio)) + { + } + + // needed for polymorphic recognition + virtual ~RF24NetworkWrapper() = default; + + uint16_t peek_header(RF24NetworkHeader& header) + { + return RF24Network::peek(header); + } + + std::tuple peek_frame(uint16_t maxlen = MAX_PAYLOAD_SIZE) + { + RF24NetworkHeader header; + maxlen = static_cast(rf24_min(maxlen, RF24Network::peek(header))); + char* buf = new char[maxlen + 1]; + RF24Network::peek(header, buf, maxlen); + py::bytearray py_ba = py::bytearray(buf, maxlen); + delete[] buf; + return std::tuple(header, py_ba); + } + +#if defined(RF24NetworkMulticast) + bool multicast(RF24NetworkHeader header, py::buffer buf, uint8_t level = 7) + { + return RF24Network::multicast( + header, + get_bytes_or_bytearray_str(buf), + static_cast(get_bytes_or_bytearray_ln(buf)), + level); + } + + void set_multicast_level(uint8_t level) + { + RF24Network::multicastLevel(level); + } + + uint8_t get_multicast_level() + { + return RF24Network::_multicast_level; + } +#endif // defined (RF24NetworkMulticast) + + std::tuple read(uint16_t maxlen = MAX_PAYLOAD_SIZE) + { + char* buf = new char[maxlen + 1]; + RF24NetworkHeader header; + uint16_t len = RF24Network::read(header, buf, maxlen); + py::bytearray py_ba = py::bytearray(buf, len); + delete[] buf; + return std::tuple(header, py_ba); + } + + bool write(RF24NetworkHeader& header, py::buffer buf, uint16_t writeDirect = NETWORK_AUTO_ROUTING) + { + return RF24Network::write( + header, + get_bytes_or_bytearray_str(buf), + static_cast(get_bytes_or_bytearray_ln(buf)), + writeDirect); + } + + uint16_t get_node_address() + { + return RF24Network::node_address; + } +};