From 75c4305b654093d0aebe3e7bb25ab19ffa0fbf14 Mon Sep 17 00:00:00 2001 From: David Momenso Date: Thu, 16 Apr 2026 19:36:53 -0300 Subject: [PATCH 1/7] Add support for libgpiod v2 --- binding.gyp | 16 +++-- package-lock.json | 4 +- src/abstract-gpio.cpp | 155 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 150 insertions(+), 25 deletions(-) diff --git a/binding.gyp b/binding.gyp index f666188..79a118b 100755 --- a/binding.gyp +++ b/binding.gyp @@ -3,7 +3,8 @@ { "variables": { "dht_verbose%": "false", - "use_libgpiod%" : "false" + "use_libgpiod%" : "false", + "libgpiod_version%": "= 2 ? 'GPIOD_V2' : 'GPIOD_V1' } catch(e) { 'GPIOD_V1' }\")" }, "target_name": "node_dht_sensor", "sources": [ @@ -11,7 +12,7 @@ "src/node-dht-sensor.cpp", "src/dht-sensor.cpp", "src/util.cpp", - "src/abstract-gpio.cpp", + "src/abstract-gpio.cpp" ], "include_dirs": [ " Date: Sat, 18 Apr 2026 00:20:32 +0100 Subject: [PATCH 2/7] Documentation update & code cleanup --- README.md | 89 +++++++++++++++++++++---------------------- src/abstract-gpio.cpp | 79 ++++++++++---------------------------- 2 files changed, 64 insertions(+), 104 deletions(-) diff --git a/README.md b/README.md index 2a575d2..64aa067 100755 --- a/README.md +++ b/README.md @@ -7,30 +7,38 @@ A simple node.js module for reading temperature and relative humidity using a co [![npm](https://img.shields.io/npm/dm/node-dht-sensor.svg)](https://www.npmjs.com/package/node-dht-sensor) [![LICENSE](https://img.shields.io/github/license/momenso/node-dht-sensor.svg)](https://github.com/momenso/node-dht-sensor/blob/master/LICENSE) -## Installation +## Installation & Hardware Requirements -```shell session -$ npm install node-dht-sensor +For most standard Linux systems and older Raspberry Pi models (Pi 1 through 4), you can install the module directly via npm: + +```sh +npm install node-dht-sensor ``` -### Installing on Raspberry Pi 5 (libgpiod requirement) +### Modern Raspberry Pi (Pi 5 / Debian 12+) — libgpiod + +When running `node-dht-sensor` on a modern architecture like the Raspberry Pi 5, the legacy BCM2835 library is physically incompatible with the new RP1 southbridge chip. You **must** compile the module using `libgpiod`. -When running node-dht-sensor on a Raspberry Pi 5 (or newer), you must install libgpiod (and its development headers) before you build. If you try to install the module with --use_libgpiod=true without having libgpiod-dev installed, the build will fail. +**`libgpiod` v2 is the highly preferred library.** The v2 API allows this module to use an `OPEN_DRAIN` configuration, enabling fast, user-space polling of the sensor. -For Raspberry Pi OS (Debian-based), use: +Before installing the module, you must install the `libgpiod` development headers and `pkg-config`. Our build system uses `pkg-config` to auto-detect whether your OS provides `libgpiod` v1 or v2 at build time and compiles the correct C++ code paths automatically. + +For Debian-based systems (like Raspberry Pi OS): ```sh sudo apt-get update -sudo apt-get install -y libgpiod-dev +sudo apt-get install -y libgpiod-dev pkg-config ``` -After installing libgpiod-dev, build node-dht-sensor with: +Then, explicitly tell npm to use the libgpiod API during installation: ```sh npm install node-dht-sensor --use_libgpiod=true ``` -> Note: Specifying --use_libgpiod=true compiles and links against libgpiod for GPIO access, because the BCM2835 library does not work on Raspberry Pi 5’s architecture. If you omit --use_libgpiod=true, node-dht-sensor defaults to using BCM2835, which is compatible with older Raspberry Pi models. +### Older Raspberry Pi (Pi 1 through Pi 4) — BCM2835 + +If you omit the `--use_libgpiod=true` flag, `node-dht-sensor` defaults to using the direct-memory BCM2835 library. This bypasses the Linux kernel entirely for nanosecond-level read speeds and is highly recommended for older boards where the CPU manages the GPIO pins directly. ## Usage @@ -186,72 +194,63 @@ sensor.read(22, 4, function(err, temperature, humidity) { And the result will always be the configured readout value defined at initialization. -```shell session -$ node examples/fake-test.js +```sh +node examples/fake-test.js temp: 21.0°C, humidity: 60.0% -$ node examples/fake-test.js +node examples/fake-test.js temp: 21.0°C, humidity: 60.0% ``` You can find a complete source code example in [examples/fake-test.js](https://github.com/momenso/node-dht-sensor/blob/master/examples/fake-test.js). -### Reference for building from source +## Manual Compilation & Debugging -Standard node-gyp commands are used to build the module. So, just make sure you have node and node-gyp as well as the Broadcom library to build the project. +Standard node-gyp commands are used to build the module. So, just make sure you have `node` and `node-gyp`, and the appropriate C++ libraries (`bcm2835` or `libgpiod-dev`) to build the project. -1. In case, you don't have node-gyp, install it first: +1. In case you don't have node-gyp, install it first: - ```shell session - $ sudo npm install -g node-gyp - $ sudo update-alternatives --install /usr/bin/node-gyp node-gyp /opt/node-v10.15.3-linux-armv7l/bin/node-gyp 1 + ```sh + sudo npm install -g node-gyp ``` -2. Generate the configuration files +2. To configure and build the component manually with libgpiod auto-detection enabled: - ```shell session - $ node-gyp configure + ```sh + node-gyp configure --use_libgpiod=true + node-gyp build ``` -3. Build the component - ```shell session - $ node-gyp build - ``` - -### Tracing and Debugging +## Tracing and Debugging Verbose output from the module can be enabled by specifying the `--dht_verbose=true` flag when installing the node via npm. -```shell session -$ npm install node-dht-sensor --dht_verbose=true +```sh +npm install node-dht-sensor --dht_verbose=true ``` -if you are interested in enabling trace when building directly from source you can enable the `-Ddht_verbose` flag when running node-gyp configure. +If you are interested in enabling trace when building directly from source you can enable the `-Ddht_verbose` flag when running node-gyp configure. -```shell session -$ node-gyp configure -- -Ddht_verbose=true +```sh +node-gyp configure -- -Ddht_verbose=true ``` -### Appendix A: Quick Node.js installation guide +## Appendix A: Quick Node.js installation guide -There are many ways you can get Node.js installed on your Raspberry Pi. Here is just one way you can do it. +There are many ways you can get Node.js installed on your Raspberry Pi. For modern installations (including Raspberry Pi 5), the officially recommended approach is using the NodeSource package repository. -```shell session -$ wget https://nodejs.org/dist/v14.15.4/node-v14.15.4-linux-armv7l.tar.xz -$ tar xvfJ node-v14.15.4-linux-armv7l.tar.xz -$ sudo mv node-v14.15.4-linux-armv7l /opt -$ sudo update-alternatives --install /usr/bin/node node /opt/node-v14.15.4-linux-armv7l/bin/node 1 -$ sudo update-alternatives --set node /opt/node-v14.15.4-linux-armv7l/bin/node -$ sudo update-alternatives --install /usr/bin/npm npm /opt/node-v14.15.4-linux-armv7l/bin/npm 1 +```sh +curl -fsSL [https://deb.nodesource.com/setup_20.x](https://deb.nodesource.com/setup_20.x) | sudo -E bash - +sudo apt-get install -y nodejs ``` -Please note that you may have to use armv6l instead of arm7l if you have an early Raspberry Pi model. +_(This automatically installs the correct architecture build (e.g., `arm64`/`aarch64` for Pi 5 or `armhf` for older models) and includes npm)._ -### References +## References [1]: Node.js download - https://nodejs.org/en/download/ [2]: BCM2835 - http://www.airspayce.com/mikem/bcm2835/ -[3]: Node.js native addon build tool - https://github.com/TooTallNate/node-gyp +[3]: libgpiod - https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/ -[4]: GPIO: Raspbery Pi Models A and B - https://www.raspberrypi.org/documentation/usage/gpio/ +[4]: Node.js native addon build tool - https://github.com/TooTallNate/node-gyp diff --git a/src/abstract-gpio.cpp b/src/abstract-gpio.cpp index 2a140bd..f7bf872 100644 --- a/src/abstract-gpio.cpp +++ b/src/abstract-gpio.cpp @@ -18,10 +18,8 @@ static GpioDirection lastDirection[MAX_GPIO_PIN_NUMBER + 1]; static gpiod_chip *theChip = NULL; #ifdef GPIOD_V2 -// V2 objects static gpiod_line_request *requests[MAX_GPIO_PIN_NUMBER + 1]; #else -// V1 objects static gpiod_line *lines[MAX_GPIO_PIN_NUMBER + 1]; #endif #endif @@ -72,20 +70,18 @@ bool gpioInitialize() break; } } - file.close(); } if (isPi5) { #ifdef USE_LIBGPIOD - + #ifdef GPIOD_V2 - // libgpiod v2 requires paths instead of names. + // libgpiod v2 requires paths instead of names. // On the Pi 5, the main header is typically on gpiochip4 (RP1 southbridge) theChip = gpiod_chip_open("/dev/gpiochip4"); if (theChip == NULL) { - // Fallback in case the user has a custom overlay or different mapping theChip = gpiod_chip_open("/dev/gpiochip0"); } #else @@ -104,13 +100,13 @@ bool gpioInitialize() #ifdef VERBOSE puts("libgpiod initialized."); #endif - + #ifdef GPIOD_V2 std::fill(requests, requests + MAX_GPIO_PIN_NUMBER + 1, (gpiod_line_request*) NULL); #else std::fill(lines, lines + MAX_GPIO_PIN_NUMBER + 1, (gpiod_line*) NULL); #endif - + std::fill(lastDirection, lastDirection + MAX_GPIO_PIN_NUMBER + 1, GPIO_UNSET); useGpiod = true; std::atexit(gpiodCleanUp); @@ -141,14 +137,10 @@ static gpiod_line_request* getLineRequest(int pin) { if (requests[pin] == NULL) { - printf("[DEBUG] Initializing line %d as OPEN_DRAIN...\n", pin); - gpiod_line_settings *settings = gpiod_line_settings_new(); - gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT); gpiod_line_settings_set_drive(settings, GPIOD_LINE_DRIVE_OPEN_DRAIN); - // Float high by default - gpiod_line_settings_set_output_value(settings, GPIOD_LINE_VALUE_ACTIVE); + gpiod_line_settings_set_output_value(settings, GPIOD_LINE_VALUE_ACTIVE); gpiod_line_config *line_cfg = gpiod_line_config_new(); unsigned int offset = pin; @@ -158,18 +150,12 @@ static gpiod_line_request* getLineRequest(int pin) gpiod_request_config_set_consumer(req_cfg, "node-dht-sensor"); requests[pin] = gpiod_chip_request_lines(theChip, req_cfg, line_cfg); - - if (requests[pin] == NULL) { - printf("[DEBUG] FAILED to request line %d as OPEN_DRAIN! The RP1 kernel driver might reject this mode.\n", pin); - } else { - printf("[DEBUG] SUCCESS: Line %d requested with OPEN_DRAIN.\n", pin); - } gpiod_request_config_free(req_cfg); gpiod_line_config_free(line_cfg); gpiod_line_settings_free(settings); - - lastDirection[pin] = GPIO_IN; + + lastDirection[pin] = GPIO_IN; } return requests[pin]; @@ -180,8 +166,6 @@ static gpiod_line* getLine(int pin, GpioDirection direction) if (lines[pin] == NULL) { lines[pin] = gpiod_chip_get_line(theChip, pin); - - // Request for the first time if (direction == GPIO_IN) { gpiod_line_request_input(lines[pin], "node-dht-sensor"); @@ -194,7 +178,6 @@ static gpiod_line* getLine(int pin, GpioDirection direction) } else if (lastDirection[pin] != direction) { - // Reconfigure in v1 (fast) if (direction == GPIO_IN) { gpiod_line_set_direction_input(lines[pin]); @@ -205,7 +188,6 @@ static gpiod_line* getLine(int pin, GpioDirection direction) } lastDirection[pin] = direction; } - return lines[pin]; } #endif @@ -217,22 +199,21 @@ void gpioWrite(int pin, GpioPinState state) if (useGpiod) { #ifdef USE_LIBGPIOD - #ifdef GPIOD_V2 - int ret = gpiod_line_request_set_value(getLineRequest(pin), pin, state == GPIO_LOW ? GPIOD_LINE_VALUE_INACTIVE : GPIOD_LINE_VALUE_ACTIVE); - if (ret < 0) { - static bool writeErr = false; - if (!writeErr) { - printf("[DEBUG] gpioWrite FAILED on pin %d! Return code: %d\n", pin, ret); - writeErr = true; - } - } + gpiod_line_request_set_value( + getLineRequest(pin), + pin, + state == GPIO_LOW ? + GPIOD_LINE_VALUE_INACTIVE : GPIOD_LINE_VALUE_ACTIVE + ); lastDirection[pin] = GPIO_OUT; #else - gpiod_line_set_value(getLine(pin, GPIO_OUT), state == GPIO_LOW ? 0 : 1); - #endif - + gpiod_line_set_value( + getLine(pin, GPIO_OUT), + state == GPIO_LOW ? 0 : 1 + ); #endif + #endif // USE_LIBGPIOD } else { @@ -250,38 +231,18 @@ GpioPinState gpioRead(int pin) if (useGpiod) { #ifdef USE_LIBGPIOD - #ifdef GPIOD_V2 - // Let the line float HIGH if (lastDirection[pin] == GPIO_OUT) { - int ret = gpiod_line_request_set_value(getLineRequest(pin), pin, GPIOD_LINE_VALUE_ACTIVE); - if (ret < 0) { - static bool floatErr = false; - if (!floatErr) { - printf("[DEBUG] Failed to float pin %d HIGH before read! Return code: %d\n", pin, ret); - floatErr = true; - } - } + gpiod_line_request_set_value(getLineRequest(pin), pin, GPIOD_LINE_VALUE_ACTIVE); lastDirection[pin] = GPIO_IN; } - int val = gpiod_line_request_get_value(getLineRequest(pin), pin); - if (val < 0) { - static bool readErr = false; - if (!readErr) { - printf("[DEBUG] gpioRead FAILED! gpiod_line_request_get_value returned %d for pin %d\n", val, pin); - readErr = true; - } - return GPIO_LOW; - } - return val == GPIOD_LINE_VALUE_INACTIVE ? GPIO_LOW : GPIO_HIGH; #else return gpiod_line_get_value(getLine(pin, GPIO_IN)) == 0 ? GPIO_LOW : GPIO_HIGH; #endif - - #endif + #endif // USE_LIBGPIOD } else { From 1f94d18928073d87a6481a20faae13bd818d85ed Mon Sep 17 00:00:00 2001 From: David Momenso Date: Sat, 18 Apr 2026 01:06:23 +0100 Subject: [PATCH 3/7] Add default variables for libgpiod and verbosity in binding.gyp --- binding.gyp | 58 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/binding.gyp b/binding.gyp index 79a118b..19746c9 100755 --- a/binding.gyp +++ b/binding.gyp @@ -1,39 +1,55 @@ { + "variables": { + "use_libgpiod%": "false", + "dht_verbose%": "false", + "libgpiod_version": "= 2 ? '2' : '1'); } catch(e) { console.log('1'); }\")" + }, "targets": [ { - "variables": { - "dht_verbose%": "false", - "use_libgpiod%" : "false", - "libgpiod_version%": "= 2 ? 'GPIOD_V2' : 'GPIOD_V1' } catch(e) { 'GPIOD_V1' }\")" - }, "target_name": "node_dht_sensor", "sources": [ "src/bcm2835/bcm2835.c", - "src/node-dht-sensor.cpp", "src/dht-sensor.cpp", + "src/node-dht-sensor.cpp", "src/util.cpp", "src/abstract-gpio.cpp" ], - "include_dirs": [ - " Date: Sat, 18 Apr 2026 15:09:58 +0100 Subject: [PATCH 4/7] Refactor GPIO handling for libgpiod v2 compatibility and improve error handling --- src/abstract-gpio.cpp | 94 ++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/src/abstract-gpio.cpp b/src/abstract-gpio.cpp index f7bf872..ff3d633 100644 --- a/src/abstract-gpio.cpp +++ b/src/abstract-gpio.cpp @@ -155,7 +155,10 @@ static gpiod_line_request* getLineRequest(int pin) gpiod_line_config_free(line_cfg); gpiod_line_settings_free(settings); - lastDirection[pin] = GPIO_IN; + if (requests[pin] != NULL) + { + lastDirection[pin] = GPIO_OUT; + } } return requests[pin]; @@ -163,29 +166,20 @@ static gpiod_line_request* getLineRequest(int pin) #else static gpiod_line* getLine(int pin, GpioDirection direction) { - if (lines[pin] == NULL) + // For libgpiod < 1.5 compatibility, release and re-request to change direction + if (lines[pin] != NULL && lastDirection[pin] != direction) { - lines[pin] = gpiod_chip_get_line(theChip, pin); - if (direction == GPIO_IN) - { - gpiod_line_request_input(lines[pin], "node-dht-sensor"); - } - else - { - gpiod_line_request_output(lines[pin], "node-dht-sensor", 0); - } - lastDirection[pin] = direction; + gpiod_line_release(lines[pin]); + lines[pin] = NULL; } - else if (lastDirection[pin] != direction) + + if (lines[pin] == NULL) { - if (direction == GPIO_IN) - { - gpiod_line_set_direction_input(lines[pin]); - } - else - { - gpiod_line_set_direction_output(lines[pin], 0); - } + lines[pin] = gpiod_chip_get_line(theChip, pin); + if (lines[pin] == NULL) return NULL; + + if (direction == GPIO_IN) { gpiod_line_request_input(lines[pin], "node-dht-sensor"); } + else { gpiod_line_request_output(lines[pin], "node-dht-sensor", 0); } lastDirection[pin] = direction; } return lines[pin]; @@ -200,20 +194,20 @@ void gpioWrite(int pin, GpioPinState state) { #ifdef USE_LIBGPIOD #ifdef GPIOD_V2 - gpiod_line_request_set_value( - getLineRequest(pin), - pin, - state == GPIO_LOW ? - GPIOD_LINE_VALUE_INACTIVE : GPIOD_LINE_VALUE_ACTIVE - ); - lastDirection[pin] = GPIO_OUT; + gpiod_line_request *req = getLineRequest(pin); + if (req != NULL) + { + gpiod_line_request_set_value(req, pin, state == GPIO_LOW ? GPIOD_LINE_VALUE_INACTIVE : GPIOD_LINE_VALUE_ACTIVE); + lastDirection[pin] = GPIO_OUT; + } #else - gpiod_line_set_value( - getLine(pin, GPIO_OUT), - state == GPIO_LOW ? 0 : 1 - ); + gpiod_line *line = getLine(pin, GPIO_OUT); + if (line != NULL) + { + gpiod_line_set_value(line, state == GPIO_LOW ? 0 : 1); + } + #endif #endif - #endif // USE_LIBGPIOD } else { @@ -232,17 +226,43 @@ GpioPinState gpioRead(int pin) { #ifdef USE_LIBGPIOD #ifdef GPIOD_V2 + gpiod_line_request *req = getLineRequest(pin); + if (req == NULL) + { + return GPIO_HIGH; // Failsafe on hardware lock + } + if (lastDirection[pin] == GPIO_OUT) { - gpiod_line_request_set_value(getLineRequest(pin), pin, GPIOD_LINE_VALUE_ACTIVE); + gpiod_line_request_set_value(req, pin, GPIOD_LINE_VALUE_ACTIVE); lastDirection[pin] = GPIO_IN; } - int val = gpiod_line_request_get_value(getLineRequest(pin), pin); + + int val = gpiod_line_request_get_value(req, pin); + + // Explicitly handle read error (-1) by returning idle line state + if (val == -1) + { + return GPIO_HIGH; + } return val == GPIOD_LINE_VALUE_INACTIVE ? GPIO_LOW : GPIO_HIGH; + #else - return gpiod_line_get_value(getLine(pin, GPIO_IN)) == 0 ? GPIO_LOW : GPIO_HIGH; + gpiod_line *line = getLine(pin, GPIO_IN); + if (line == NULL) + { + return GPIO_HIGH; // Failsafe + } + + int val = gpiod_line_get_value(line); + if (val == -1) + { + return GPIO_HIGH; + } + return val == 0 ? GPIO_LOW : GPIO_HIGH; + + #endif #endif - #endif // USE_LIBGPIOD } else { From 2cb0cf65e5465fb424cc74c16a7487674c6bcd70 Mon Sep 17 00:00:00 2001 From: David Momenso Date: Sat, 18 Apr 2026 15:22:11 +0100 Subject: [PATCH 5/7] Implement findPi5GpioChip function for improved GPIO chip detection on Raspberry Pi 5 --- src/abstract-gpio.cpp | 67 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/src/abstract-gpio.cpp b/src/abstract-gpio.cpp index ff3d633..419f33c 100644 --- a/src/abstract-gpio.cpp +++ b/src/abstract-gpio.cpp @@ -8,6 +8,7 @@ #include #ifdef USE_LIBGPIOD +#include #include #endif @@ -49,6 +50,59 @@ static void gpiodCleanUp() #endif } +#ifdef USE_LIBGPIOD +static gpiod_chip* findPi5GpioChip() +{ + gpiod_chip* chip = NULL; + + // 1. Safely enumerate up to 10 chips looking for the RP1 southbridge + for (int i = 0; i < 10; i++) + { + char path[32]; + snprintf(path, sizeof(path), "/dev/gpiochip%d", i); + + #ifdef GPIOD_V2 + chip = gpiod_chip_open(path); + if (chip != NULL) + { + gpiod_chip_info* info = gpiod_chip_get_info(chip); + if (info != NULL) + { + const char* label = gpiod_chip_info_get_label(info); + bool is_rp1 = (label != NULL && strstr(label, "pinctrl-rp1") != NULL); + gpiod_chip_info_free(info); + + if (is_rp1) return chip; + } + gpiod_chip_close(chip); + } + #else + chip = gpiod_chip_open(path); + if (chip != NULL) + { + const char* label = gpiod_chip_label(chip); + if (label != NULL && strstr(label, "pinctrl-rp1") != NULL) + { + return chip; + } + gpiod_chip_close(chip); + } + #endif + } + + // 2. Fallback if the RP1 label changes in future kernels + #ifdef GPIOD_V2 + chip = gpiod_chip_open("/dev/gpiochip4"); + if (chip == NULL) chip = gpiod_chip_open("/dev/gpiochip0"); + #else + chip = gpiod_chip_open_by_name("gpiochip4"); + if (chip == NULL) chip = gpiod_chip_open_by_name("gpiochip0"); + #endif + + return chip; +} +#endif + bool gpioInitialize() { bool isPi5 = false; @@ -77,21 +131,12 @@ bool gpioInitialize() { #ifdef USE_LIBGPIOD - #ifdef GPIOD_V2 - // libgpiod v2 requires paths instead of names. - // On the Pi 5, the main header is typically on gpiochip4 (RP1 southbridge) - theChip = gpiod_chip_open("/dev/gpiochip4"); - if (theChip == NULL) { - theChip = gpiod_chip_open("/dev/gpiochip0"); - } - #else - theChip = gpiod_chip_open_by_name("gpiochip0"); - #endif + theChip = findPi5GpioChip(); if (theChip == NULL) { #ifdef VERBOSE - puts("libgpiod initialization failed."); + puts("libgpiod initialization failed: Could not find RP1 or fallback chips."); #endif return false; } From 418f9b2ba3ed2a6e08289b5e88a00f9ac8782bda Mon Sep 17 00:00:00 2001 From: David Momenso Date: Sat, 18 Apr 2026 15:22:41 +0100 Subject: [PATCH 6/7] Fix missing newline at end of binding.gyp file --- binding.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding.gyp b/binding.gyp index 19746c9..25fae36 100755 --- a/binding.gyp +++ b/binding.gyp @@ -53,4 +53,4 @@ ] } ] -} \ No newline at end of file +} From 1ac1cba75119afddd50505df08ebe475b5ab9ef6 Mon Sep 17 00:00:00 2001 From: David Momenso Date: Sat, 18 Apr 2026 16:02:10 +0100 Subject: [PATCH 7/7] Fix inconsistent #ifdef conditionals --- src/abstract-gpio.cpp | 253 +++++++++++++++++------------------------- 1 file changed, 103 insertions(+), 150 deletions(-) diff --git a/src/abstract-gpio.cpp b/src/abstract-gpio.cpp index 419f33c..4b1a21c 100644 --- a/src/abstract-gpio.cpp +++ b/src/abstract-gpio.cpp @@ -1,7 +1,6 @@ #include "abstract-gpio.h" #include -#include "bcm2835/bcm2835.h" #include #include #include @@ -10,24 +9,22 @@ #ifdef USE_LIBGPIOD #include #include +#else +#include "bcm2835/bcm2835.h" #endif -static bool useGpiod = false; -static GpioDirection lastDirection[MAX_GPIO_PIN_NUMBER + 1]; - #ifdef USE_LIBGPIOD static gpiod_chip *theChip = NULL; +static GpioDirection lastDirection[MAX_GPIO_PIN_NUMBER + 1]; #ifdef GPIOD_V2 static gpiod_line_request *requests[MAX_GPIO_PIN_NUMBER + 1]; #else static gpiod_line *lines[MAX_GPIO_PIN_NUMBER + 1]; #endif -#endif static void gpiodCleanUp() { - #ifdef USE_LIBGPIOD for (int i = 1; i <= MAX_GPIO_PIN_NUMBER; ++i) { #ifdef GPIOD_V2 @@ -47,15 +44,13 @@ static void gpiodCleanUp() { gpiod_chip_close(theChip); } - #endif } -#ifdef USE_LIBGPIOD -static gpiod_chip* findPi5GpioChip() +static gpiod_chip* findGpioChip() { gpiod_chip* chip = NULL; - // 1. Safely enumerate up to 10 chips looking for the RP1 southbridge + // 1. Safely enumerate up to 10 chips looking for the RP1 southbridge (Pi 5) for (int i = 0; i < 10; i++) { char path[32]; @@ -90,7 +85,7 @@ static gpiod_chip* findPi5GpioChip() #endif } - // 2. Fallback if the RP1 label changes in future kernels + // 2. Fallback for older Pi's using libgpiod or default setups #ifdef GPIOD_V2 chip = gpiod_chip_open("/dev/gpiochip4"); if (chip == NULL) chip = gpiod_chip_open("/dev/gpiochip0"); @@ -101,81 +96,6 @@ static gpiod_chip* findPi5GpioChip() return chip; } -#endif - -bool gpioInitialize() -{ - bool isPi5 = false; - std::ifstream file("/proc/cpuinfo"); - std::string line; - - if (file.is_open()) - { - std::regex pattern(R"(Model\s+:\s+Raspberry Pi (\d+))"); - std::smatch match; - - while (std::getline(file, line)) - { - if (std::regex_search(line, match, pattern) && std::stoi(match[1]) > 4) - { - #ifdef USE_LIBGPIOD - isPi5 = true; - #endif - break; - } - } - file.close(); - } - - if (isPi5) - { - #ifdef USE_LIBGPIOD - - theChip = findPi5GpioChip(); - - if (theChip == NULL) - { - #ifdef VERBOSE - puts("libgpiod initialization failed: Could not find RP1 or fallback chips."); - #endif - return false; - } - else - { - #ifdef VERBOSE - puts("libgpiod initialized."); - #endif - - #ifdef GPIOD_V2 - std::fill(requests, requests + MAX_GPIO_PIN_NUMBER + 1, (gpiod_line_request*) NULL); - #else - std::fill(lines, lines + MAX_GPIO_PIN_NUMBER + 1, (gpiod_line*) NULL); - #endif - - std::fill(lastDirection, lastDirection + MAX_GPIO_PIN_NUMBER + 1, GPIO_UNSET); - useGpiod = true; - std::atexit(gpiodCleanUp); - return true; - } - #endif - } - else if (!bcm2835_init()) - { - #ifdef VERBOSE - puts("BCM2835 initialization failed."); - #endif - return false; - } - else - { - #ifdef VERBOSE - puts("BCM2835 initialized."); - #endif - return true; - } -} - -#ifdef USE_LIBGPIOD #ifdef GPIOD_V2 static gpiod_line_request* getLineRequest(int pin) @@ -211,7 +131,6 @@ static gpiod_line_request* getLineRequest(int pin) #else static gpiod_line* getLine(int pin, GpioDirection direction) { - // For libgpiod < 1.5 compatibility, release and re-request to change direction if (lines[pin] != NULL && lastDirection[pin] != direction) { gpiod_line_release(lines[pin]); @@ -229,93 +148,127 @@ static gpiod_line* getLine(int pin, GpioDirection direction) } return lines[pin]; } -#endif +#endif // GPIOD_V2 +// --- bcm2835 State --- +#else +static GpioDirection lastDirection[MAX_GPIO_PIN_NUMBER + 1]; #endif // USE_LIBGPIOD -void gpioWrite(int pin, GpioPinState state) +// --- Public API --- + +bool gpioInitialize() { - if (useGpiod) +#ifdef USE_LIBGPIOD + theChip = findGpioChip(); + + if (theChip == NULL) + { + #ifdef VERBOSE + puts("libgpiod initialization failed: Could not find RP1 or fallback chips."); + #endif + return false; + } + else { - #ifdef USE_LIBGPIOD + #ifdef VERBOSE + puts("libgpiod initialized."); + #endif + #ifdef GPIOD_V2 - gpiod_line_request *req = getLineRequest(pin); - if (req != NULL) - { - gpiod_line_request_set_value(req, pin, state == GPIO_LOW ? GPIOD_LINE_VALUE_INACTIVE : GPIOD_LINE_VALUE_ACTIVE); - lastDirection[pin] = GPIO_OUT; - } + std::fill(requests, requests + MAX_GPIO_PIN_NUMBER + 1, (gpiod_line_request*) NULL); #else - gpiod_line *line = getLine(pin, GPIO_OUT); - if (line != NULL) - { - gpiod_line_set_value(line, state == GPIO_LOW ? 0 : 1); - } + std::fill(lines, lines + MAX_GPIO_PIN_NUMBER + 1, (gpiod_line*) NULL); #endif + + std::fill(lastDirection, lastDirection + MAX_GPIO_PIN_NUMBER + 1, GPIO_UNSET); + std::atexit(gpiodCleanUp); + return true; + } +#else + if (!bcm2835_init()) + { + #ifdef VERBOSE + puts("BCM2835 initialization failed."); #endif + return false; } else { - if (lastDirection[pin] != GPIO_OUT) - { - bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP); - lastDirection[pin] = GPIO_OUT; - } - bcm2835_gpio_write(pin, state == GPIO_LOW ? LOW : HIGH); + #ifdef VERBOSE + puts("BCM2835 initialized."); + #endif + std::fill(lastDirection, lastDirection + MAX_GPIO_PIN_NUMBER + 1, GPIO_UNSET); + return true; } +#endif } -GpioPinState gpioRead(int pin) +void gpioWrite(int pin, GpioPinState state) { - if (useGpiod) +#ifdef USE_LIBGPIOD + #ifdef GPIOD_V2 + gpiod_line_request *req = getLineRequest(pin); + if (req != NULL) { - #ifdef USE_LIBGPIOD - #ifdef GPIOD_V2 - gpiod_line_request *req = getLineRequest(pin); - if (req == NULL) - { - return GPIO_HIGH; // Failsafe on hardware lock - } + gpiod_line_request_set_value(req, pin, state == GPIO_LOW ? GPIOD_LINE_VALUE_INACTIVE : GPIOD_LINE_VALUE_ACTIVE); + lastDirection[pin] = GPIO_OUT; + } + #else + gpiod_line *line = getLine(pin, GPIO_OUT); + if (line != NULL) + { + gpiod_line_set_value(line, state == GPIO_LOW ? 0 : 1); + } + #endif +#else + if (lastDirection[pin] != GPIO_OUT) + { + bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP); + lastDirection[pin] = GPIO_OUT; + } + bcm2835_gpio_write(pin, state == GPIO_LOW ? LOW : HIGH); +#endif +} - if (lastDirection[pin] == GPIO_OUT) - { - gpiod_line_request_set_value(req, pin, GPIOD_LINE_VALUE_ACTIVE); - lastDirection[pin] = GPIO_IN; - } +GpioPinState gpioRead(int pin) +{ +#ifdef USE_LIBGPIOD + #ifdef GPIOD_V2 + gpiod_line_request *req = getLineRequest(pin); + if (req == NULL) return GPIO_HIGH; - int val = gpiod_line_request_get_value(req, pin); + if (lastDirection[pin] == GPIO_OUT) + { + gpiod_line_request_set_value(req, pin, GPIOD_LINE_VALUE_ACTIVE); + lastDirection[pin] = GPIO_IN; + } - // Explicitly handle read error (-1) by returning idle line state - if (val == -1) - { - return GPIO_HIGH; - } - return val == GPIOD_LINE_VALUE_INACTIVE ? GPIO_LOW : GPIO_HIGH; + int val = gpiod_line_request_get_value(req, pin); - #else - gpiod_line *line = getLine(pin, GPIO_IN); - if (line == NULL) - { - return GPIO_HIGH; // Failsafe - } + if (val == -1) + { + return GPIO_HIGH; + } + return val == GPIOD_LINE_VALUE_INACTIVE ? GPIO_LOW : GPIO_HIGH; - int val = gpiod_line_get_value(line); - if (val == -1) - { - return GPIO_HIGH; - } - return val == 0 ? GPIO_LOW : GPIO_HIGH; + #else + gpiod_line *line = getLine(pin, GPIO_IN); + if (line == NULL) return GPIO_HIGH; - #endif - #endif + int val = gpiod_line_get_value(line); + if (val == -1) + { + return GPIO_HIGH; } - else + return val == 0 ? GPIO_LOW : GPIO_HIGH; + #endif +#else + if (lastDirection[pin] != GPIO_IN) { - if (lastDirection[pin] != GPIO_IN) - { - bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT); - lastDirection[pin] = GPIO_IN; - } - return bcm2835_gpio_lev(pin) == LOW ? GPIO_LOW : GPIO_HIGH; + bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT); + lastDirection[pin] = GPIO_IN; } + return bcm2835_gpio_lev(pin) == LOW ? GPIO_LOW : GPIO_HIGH; +#endif }