From e9446b286531bd64092da2a02aa1ce3e523f0ef3 Mon Sep 17 00:00:00 2001 From: Carl Date: Mon, 16 Aug 2021 23:52:03 +0200 Subject: [PATCH 01/13] Accept wildcards in mqtt subscription prefix --- core/MyProtocol.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/core/MyProtocol.cpp b/core/MyProtocol.cpp index 8cd13ad88..d8f43128c 100644 --- a/core/MyProtocol.cpp +++ b/core/MyProtocol.cpp @@ -115,10 +115,32 @@ bool protocolMQTT2MyMessage(MyMessage &message, char *topic, uint8_t *payload, message.setSender(GATEWAY_ADDRESS); message.setLast(GATEWAY_ADDRESS); message.setEcho(false); - for (str = strtok_r(topic + strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1, "/", &p); + + // Calculate length of actual prefix. + // The subscription prefix can contain wildcards, so the length of the subscription prefix + // can differ from the length of the actually used prefix. + // e.g. subscription prefix "+/mysensors", actual prefix "foo/mysensors" + uint8_t prefix_length = 0; + // Make a copy of the strings because strtok modifies the string + char topic2[strlen(topic)+1]; + strcpy(topic2,topic); + char *str2, *save_pointer2; + char subscribe_topic[strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1]; + strcpy(subscribe_topic,MY_MQTT_SUBSCRIBE_TOPIC_PREFIX); + char *str3, *save_pointer3; + + for (str2=strtok_r(topic2,"/",&save_pointer2),str3 = strtok_r(subscribe_topic,"/", &save_pointer3); str3 ; + str2=strtok_r(NULL,"/",&save_pointer2),str3=strtok_r(NULL,"/",&save_pointer3)) { + prefix_length += strlen(str2) + 1; // the actual token and the delimiter + // GATEWAY_DEBUG(PSTR("GWT:TPS:prefixlen=%d, toktop=%s, tokpre=%s\n"), prefix_length, str2,str3); + } + // for (str = strtok_r(topic + strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1, "/", &p); + for (str = strtok_r(topic + prefix_length, "/", &p); + str && index < 5; str = strtok_r(NULL, "/", &p), index++ ) { + // GATEWAY_DEBUG(PSTR("GWT:TPS:Index=%d, mqttstr=%s\n"), index, str); switch (index) { case 0: // Node id From 45cb2637b0c4f77755662e61ebbb0f8c87758264 Mon Sep 17 00:00:00 2001 From: Carl Date: Wed, 18 Aug 2021 23:09:59 +0200 Subject: [PATCH 02/13] Accept wildcards in mqtt subscription prefix --- core/MyProtocol.cpp | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/core/MyProtocol.cpp b/core/MyProtocol.cpp index d8f43128c..1f92269a4 100644 --- a/core/MyProtocol.cpp +++ b/core/MyProtocol.cpp @@ -111,36 +111,30 @@ bool protocolMQTT2MyMessage(MyMessage &message, char *topic, uint8_t *payload, const unsigned int length) { char *str, *p; - uint8_t index = 0; + int8_t index = -1; message.setSender(GATEWAY_ADDRESS); message.setLast(GATEWAY_ADDRESS); message.setEcho(false); - // Calculate length of actual prefix. - // The subscription prefix can contain wildcards, so the length of the subscription prefix - // can differ from the length of the actually used prefix. - // e.g. subscription prefix "+/mysensors", actual prefix "foo/mysensors" - uint8_t prefix_length = 0; - // Make a copy of the strings because strtok modifies the string - char topic2[strlen(topic)+1]; - strcpy(topic2,topic); - char *str2, *save_pointer2; - char subscribe_topic[strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1]; - strcpy(subscribe_topic,MY_MQTT_SUBSCRIBE_TOPIC_PREFIX); - char *str3, *save_pointer3; - - for (str2=strtok_r(topic2,"/",&save_pointer2),str3 = strtok_r(subscribe_topic,"/", &save_pointer3); str3 ; - str2=strtok_r(NULL,"/",&save_pointer2),str3=strtok_r(NULL,"/",&save_pointer3)) { - prefix_length += strlen(str2) + 1; // the actual token and the delimiter - // GATEWAY_DEBUG(PSTR("GWT:TPS:prefixlen=%d, toktop=%s, tokpre=%s\n"), prefix_length, str2,str3); - } - // for (str = strtok_r(topic + strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1, "/", &p); - for (str = strtok_r(topic + prefix_length, "/", &p); - + // The subscription prefix can contain wildcards, but only the + wildcard. + // e.g. subscription prefix "+/mysensors", actual prefix "foo/mysensors" + // To fetch only the part after the mqtt prefix, discard as many + // mqtt levels (between / characters) as in the subscription prefix. + // Make a copy of the subscribe prefix because strtok is allowed to change the string. + char subscribeTopicPrefix[strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1]; + strcpy(subscribeTopicPrefix,MY_MQTT_SUBSCRIBE_TOPIC_PREFIX); + char *strPrefix, *savePointerPrefix; + for (str = strtok_r(topic, "/", &p), + strPrefix = strtok_r(subscribeTopicPrefix,"/", &savePointerPrefix); str && index < 5; - str = strtok_r(NULL, "/", &p), index++ + str = strtok_r(NULL, "/", &p), + strPrefix = strtok_r(NULL, "/", &savePointerPrefix) ) { - // GATEWAY_DEBUG(PSTR("GWT:TPS:Index=%d, mqttstr=%s\n"), index, str); + // Increment index (init value is -1) if we have consumed the mqtt subscription prefix + if (!strPrefix) { + index++; + } + //GATEWAY_DEBUG(PSTR("GWT:TPS:Index=%d, mqttstr=%s, prefixtoken=%s\n"), index, str,strPrefix); switch (index) { case 0: // Node id @@ -185,5 +179,5 @@ bool protocolMQTT2MyMessage(MyMessage &message, char *topic, uint8_t *payload, } } // Return true if input valid - return (index == 5); + return (index == 4); } From b36c84663fc5ef2c1672e9d110d990eabcb0af08 Mon Sep 17 00:00:00 2001 From: Carl Date: Wed, 18 Aug 2021 23:09:59 +0200 Subject: [PATCH 03/13] Accept wildcards in mqtt subscription prefix --- core/MyProtocol.cpp | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/core/MyProtocol.cpp b/core/MyProtocol.cpp index d8f43128c..1f92269a4 100644 --- a/core/MyProtocol.cpp +++ b/core/MyProtocol.cpp @@ -111,36 +111,30 @@ bool protocolMQTT2MyMessage(MyMessage &message, char *topic, uint8_t *payload, const unsigned int length) { char *str, *p; - uint8_t index = 0; + int8_t index = -1; message.setSender(GATEWAY_ADDRESS); message.setLast(GATEWAY_ADDRESS); message.setEcho(false); - // Calculate length of actual prefix. - // The subscription prefix can contain wildcards, so the length of the subscription prefix - // can differ from the length of the actually used prefix. - // e.g. subscription prefix "+/mysensors", actual prefix "foo/mysensors" - uint8_t prefix_length = 0; - // Make a copy of the strings because strtok modifies the string - char topic2[strlen(topic)+1]; - strcpy(topic2,topic); - char *str2, *save_pointer2; - char subscribe_topic[strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1]; - strcpy(subscribe_topic,MY_MQTT_SUBSCRIBE_TOPIC_PREFIX); - char *str3, *save_pointer3; - - for (str2=strtok_r(topic2,"/",&save_pointer2),str3 = strtok_r(subscribe_topic,"/", &save_pointer3); str3 ; - str2=strtok_r(NULL,"/",&save_pointer2),str3=strtok_r(NULL,"/",&save_pointer3)) { - prefix_length += strlen(str2) + 1; // the actual token and the delimiter - // GATEWAY_DEBUG(PSTR("GWT:TPS:prefixlen=%d, toktop=%s, tokpre=%s\n"), prefix_length, str2,str3); - } - // for (str = strtok_r(topic + strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1, "/", &p); - for (str = strtok_r(topic + prefix_length, "/", &p); - + // The subscription prefix can contain wildcards, but only the + wildcard. + // e.g. subscription prefix "+/mysensors", actual prefix "foo/mysensors" + // To fetch only the part after the mqtt prefix, discard as many + // mqtt levels (between / characters) as in the subscription prefix. + // Make a copy of the subscribe prefix because strtok is allowed to change the string. + char subscribeTopicPrefix[strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1]; + strcpy(subscribeTopicPrefix,MY_MQTT_SUBSCRIBE_TOPIC_PREFIX); + char *strPrefix, *savePointerPrefix; + for (str = strtok_r(topic, "/", &p), + strPrefix = strtok_r(subscribeTopicPrefix,"/", &savePointerPrefix); str && index < 5; - str = strtok_r(NULL, "/", &p), index++ + str = strtok_r(NULL, "/", &p), + strPrefix = strtok_r(NULL, "/", &savePointerPrefix) ) { - // GATEWAY_DEBUG(PSTR("GWT:TPS:Index=%d, mqttstr=%s\n"), index, str); + // Increment index (init value is -1) if we have consumed the mqtt subscription prefix + if (!strPrefix) { + index++; + } + //GATEWAY_DEBUG(PSTR("GWT:TPS:Index=%d, mqttstr=%s, prefixtoken=%s\n"), index, str,strPrefix); switch (index) { case 0: // Node id @@ -185,5 +179,5 @@ bool protocolMQTT2MyMessage(MyMessage &message, char *topic, uint8_t *payload, } } // Return true if input valid - return (index == 5); + return (index == 4); } From 2cfe8339a37a110caad322b40ffe5e404116ddc9 Mon Sep 17 00:00:00 2001 From: romaricr Date: Tue, 12 Mar 2024 21:30:29 +0100 Subject: [PATCH 04/13] Bug fix for GPIO under recent Linux kernel versions Using libgpiod --- Makefile | 2 +- MyConfig.h | 2 +- configure | 3 +- .../Linux/drivers/core/interrupt.cpp | 102 ++++++++---------- .../Linux/drivers/core/interrupt.h | 3 + 5 files changed, 49 insertions(+), 63 deletions(-) diff --git a/Makefile b/Makefile index 5b2ef8bde..e02134bb5 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ $(ARDUINO): $(ARDUINO_LIB_OBJS) # Gateway Build $(GATEWAY): $(GATEWAY_OBJECTS) $(ARDUINO_LIB_OBJS) - $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) $(ARDUINO_LIB_OBJS) + $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) $(ARDUINO_LIB_OBJS) $(LATE_LDFLAGS) # Include all .d files -include $(DEPS) diff --git a/MyConfig.h b/MyConfig.h index c26ffdde2..4bf753658 100755 --- a/MyConfig.h +++ b/MyConfig.h @@ -442,7 +442,7 @@ * - RF24_PA_MAX = 0dBm */ #ifndef MY_RF24_PA_LEVEL -#define MY_RF24_PA_LEVEL (RF24_PA_HIGH) +#define MY_RF24_PA_LEVEL (RF24_PA_MAX) #endif /** diff --git a/configure b/configure index 2f30e98de..1281ebe3c 100755 --- a/configure +++ b/configure @@ -318,7 +318,7 @@ signing=none signing_request_signatures=false encryption=false -params="SOC CFLAGS CXXFLAGS CPPFLAGS LDFLAGS PREFIX CC CXX ARDUINO_LIB_DIR BUILDDIR BINDIR GATEWAY_DIR INIT_SYSTEM SPI_DRIVER TYPE" +params="SOC CFLAGS CXXFLAGS CPPFLAGS LDFLAGS LATE_LDFLAGS PREFIX CC CXX ARDUINO_LIB_DIR BUILDDIR BINDIR GATEWAY_DIR INIT_SYSTEM SPI_DRIVER TYPE" for opt do if [ "$opt" = "-h" ] || [ "$opt" = "--help" ]; then @@ -707,6 +707,7 @@ fi LDFLAGS="-pthread $LDFLAGS" CPPFLAGS="$CPUFLAGS $CPPFLAGS" +LATE_LDFLAGS="-Wl,-Bstatic -lgpiod -Wl,-Bdynamic" printf " ${OK} CPPFLAGS: $CPPFLAGS\n" printf " ${OK} CXXFLAGS: $CXXFLAGS\n" diff --git a/hal/architecture/Linux/drivers/core/interrupt.cpp b/hal/architecture/Linux/drivers/core/interrupt.cpp index f57c4cb7a..d11920fed 100644 --- a/hal/architecture/Linux/drivers/core/interrupt.cpp +++ b/hal/architecture/Linux/drivers/core/interrupt.cpp @@ -37,6 +37,7 @@ struct ThreadArgs { void (*func)(); int gpioPin; + struct gpiod_line *line; }; volatile bool interruptsEnabled = true; @@ -87,10 +88,10 @@ int piHiPri(const int pri) void *interruptHandler(void *args) { int fd; - struct pollfd polls; char c; struct ThreadArgs *arguments = (struct ThreadArgs *)args; int gpioPin = arguments->gpioPin; + struct gpiod_line *line = arguments->line; void (*func)() = arguments->func; delete arguments; @@ -101,28 +102,26 @@ void *interruptHandler(void *args) return NULL; } - // Setup poll structure - polls.fd = fd; - polls.events = POLLPRI | POLLERR; - while (1) { // Wait for it ... - int ret = poll(&polls, 1, -1); + // New version + int ret = gpiod_line_event_wait(line, NULL); if (ret < 0) { logError("Error waiting for interrupt: %s\n", strerror(errno)); break; } - // Do a dummy read to clear the interrupt - // A one character read appars to be enough. - if (lseek (fd, 0, SEEK_SET) < 0) { - logError("Interrupt handler error: %s\n", strerror(errno)); - break; - } - if (read (fd, &c, 1) < 0) { - logError("Interrupt handler error: %s\n", strerror(errno)); - break; + struct gpiod_line_event event; + if (gpiod_line_event_read(line, &event) == 0) { + if (event.event_type == GPIOD_LINE_EVENT_RISING_EDGE) { + logInfo("RISING Edge\n"); + } else { + logInfo("FALLING Edge\n"); + } } + // Call user function. + logError("Calling user function\n"); + pthread_mutex_lock(&intMutex); if (interruptsEnabled) { pthread_mutex_unlock(&intMutex); @@ -131,6 +130,8 @@ void *interruptHandler(void *args) pthread_mutex_unlock(&intMutex); } } + // Adding gpiod closing instructions + gpiod_line_release(line); close(fd); @@ -153,72 +154,53 @@ void attachInterrupt(uint8_t gpioPin, void (*func)(), uint8_t mode) usleep(1000); } - // Export pin for interrupt - if ((fd = fopen("/sys/class/gpio/export", "w")) == NULL) { - logError("attachInterrupt: Unable to export pin %d for interrupt: %s\n", gpioPin, strerror(errno)); - exit(1); - } - fprintf(fd, "%d\n", gpioPin); - fclose(fd); + char *chipname = "gpiochip0"; + unsigned int line_num = gpioPin; + struct gpiod_line_event event; + struct gpiod_chip *chip; + struct gpiod_line *line; - // Wait a bit the system to create /sys/class/gpio/gpio - usleep(1000); - snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/direction", gpioPin) ; - if ((fd = fopen (fName, "w")) == NULL) { - logError("attachInterrupt: Unable to open GPIO direction interface for pin %d: %s\n", - gpioPin, strerror(errno)); - exit(1) ; + chip = gpiod_chip_open_by_name(chipname); + if (!chip) { + logError("Open chip failed\n"); + exit(1); } - fprintf(fd, "in\n") ; - fclose(fd) ; - - snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/edge", gpioPin) ; - if ((fd = fopen(fName, "w")) == NULL) { - logError("attachInterrupt: Unable to open GPIO edge interface for pin %d: %s\n", gpioPin, - strerror(errno)); - exit(1) ; + + line = gpiod_chip_get_line(chip, line_num); + if (!line) { + logError("Get line failed\n"); + exit(1); } switch (mode) { case CHANGE: - fprintf(fd, "both\n"); + gpiod_line_request_both_edges_events(line, "gpiointerrupt"); break; case FALLING: - fprintf(fd, "falling\n"); + gpiod_line_request_falling_edge_events(line, "gpiointerrupt"); break; case RISING: - fprintf(fd, "rising\n"); + gpiod_line_request_rising_edge_events(line, "gpiointerrupt"); break; case NONE: - fprintf(fd, "none\n"); break; default: logError("attachInterrupt: Invalid mode\n"); - fclose(fd); return; } - fclose(fd); if (sysFds[gpioPin] == -1) { - snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/value", gpioPin); - if ((sysFds[gpioPin] = open(fName, O_RDONLY)) < 0) { + if ((sysFds[gpioPin] = gpiod_line_event_get_fd(line)) < 0) { logError("Error reading pin %d: %s\n", gpioPin, strerror(errno)); exit(1); } } - // Clear any initial pending interrupt - ioctl(sysFds[gpioPin], FIONREAD, &count); - for (int i = 0; i < count; ++i) { - if (read(sysFds[gpioPin], &c, 1) == -1) { - logError("attachInterrupt: failed to read pin status: %s\n", strerror(errno)); - } - } - struct ThreadArgs *threadArgs = new struct ThreadArgs; threadArgs->func = func; threadArgs->gpioPin = gpioPin; + threadArgs->line = line; // Create a thread passing the pin and function pthread_create(threadIds[gpioPin], NULL, interruptHandler, (void *)threadArgs); @@ -239,13 +221,13 @@ void detachInterrupt(uint8_t gpioPin) sysFds[gpioPin] = -1; } - FILE *fp = fopen("/sys/class/gpio/unexport", "w"); - if (fp == NULL) { - logError("Unable to unexport pin %d for interrupt\n", gpioPin); - exit(1); - } - fprintf(fp, "%d", gpioPin); - fclose(fp); +// FILE *fp = fopen("/sys/class/gpio/unexport", "w"); +// if (fp == NULL) { +// logError("Unable to unexport pin %d for interrupt\n", gpioPin); +// exit(1); +// } +// fprintf(fp, "%d", gpioPin); +// fclose(fp); } void interrupts() diff --git a/hal/architecture/Linux/drivers/core/interrupt.h b/hal/architecture/Linux/drivers/core/interrupt.h index b1589251b..b382e31ba 100644 --- a/hal/architecture/Linux/drivers/core/interrupt.h +++ b/hal/architecture/Linux/drivers/core/interrupt.h @@ -24,6 +24,9 @@ #include +// Ajout RRO +#include + #define CHANGE 1 #define FALLING 2 #define RISING 3 From 3dba00fd7a8050225e4e8278f7364c7ef2375d93 Mon Sep 17 00:00:00 2001 From: romaricr Date: Sat, 16 Mar 2024 16:49:55 +0100 Subject: [PATCH 05/13] Updated for style and minors non functional changes --- MyConfig.h | 2 +- hal/architecture/Linux/drivers/core/interrupt.cpp | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/MyConfig.h b/MyConfig.h index 4bf753658..c26ffdde2 100755 --- a/MyConfig.h +++ b/MyConfig.h @@ -442,7 +442,7 @@ * - RF24_PA_MAX = 0dBm */ #ifndef MY_RF24_PA_LEVEL -#define MY_RF24_PA_LEVEL (RF24_PA_MAX) +#define MY_RF24_PA_LEVEL (RF24_PA_HIGH) #endif /** diff --git a/hal/architecture/Linux/drivers/core/interrupt.cpp b/hal/architecture/Linux/drivers/core/interrupt.cpp index d11920fed..6fc50fe3f 100644 --- a/hal/architecture/Linux/drivers/core/interrupt.cpp +++ b/hal/architecture/Linux/drivers/core/interrupt.cpp @@ -131,7 +131,7 @@ void *interruptHandler(void *args) } } // Adding gpiod closing instructions - gpiod_line_release(line); + gpiod_line_release(line); close(fd); @@ -220,14 +220,6 @@ void detachInterrupt(uint8_t gpioPin) close(sysFds[gpioPin]); sysFds[gpioPin] = -1; } - -// FILE *fp = fopen("/sys/class/gpio/unexport", "w"); -// if (fp == NULL) { -// logError("Unable to unexport pin %d for interrupt\n", gpioPin); -// exit(1); -// } -// fprintf(fp, "%d", gpioPin); -// fclose(fp); } void interrupts() From f157ed03048705d629d078fe3e421926cc77d2f7 Mon Sep 17 00:00:00 2001 From: romaricr Date: Sat, 16 Mar 2024 16:51:23 +0100 Subject: [PATCH 06/13] Minor changes --- hal/architecture/Linux/drivers/core/interrupt.h | 1 - 1 file changed, 1 deletion(-) diff --git a/hal/architecture/Linux/drivers/core/interrupt.h b/hal/architecture/Linux/drivers/core/interrupt.h index b382e31ba..d503fbcd9 100644 --- a/hal/architecture/Linux/drivers/core/interrupt.h +++ b/hal/architecture/Linux/drivers/core/interrupt.h @@ -24,7 +24,6 @@ #include -// Ajout RRO #include #define CHANGE 1 From fea7b0d4e8f1920be9e7efffa9f04225c21d6fa5 Mon Sep 17 00:00:00 2001 From: Carl Date: Sun, 24 Mar 2024 16:57:46 +0100 Subject: [PATCH 07/13] Makefile.inc under version control --- MySensors-Arduino.iml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 MySensors-Arduino.iml diff --git a/MySensors-Arduino.iml b/MySensors-Arduino.iml new file mode 100644 index 000000000..48426fcce --- /dev/null +++ b/MySensors-Arduino.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file From 29ecc913eeeb85afca328efa0060cd4a990421a3 Mon Sep 17 00:00:00 2001 From: Carl Date: Thu, 30 Jan 2025 23:02:49 +0100 Subject: [PATCH 08/13] GPIO issues --- MyConfig.h | 2 +- hal/architecture/Linux/drivers/core/GPIO.cpp | 196 ++++++------- .../Linux/drivers/core/GPIO_experiment.cpp | 266 ++++++++++++++++++ 3 files changed, 349 insertions(+), 115 deletions(-) create mode 100644 hal/architecture/Linux/drivers/core/GPIO_experiment.cpp diff --git a/MyConfig.h b/MyConfig.h index c26ffdde2..13e41d8fc 100755 --- a/MyConfig.h +++ b/MyConfig.h @@ -754,7 +754,7 @@ * @brief %RFM69 Network ID. Use the same for all nodes that will talk to each other. */ #ifndef MY_RFM69_NETWORKID -#define MY_RFM69_NETWORKID (100) +define MY_RFM69_NETWORKID (197) #endif /** diff --git a/hal/architecture/Linux/drivers/core/GPIO.cpp b/hal/architecture/Linux/drivers/core/GPIO.cpp index eb371fb25..7f00e5507 100644 --- a/hal/architecture/Linux/drivers/core/GPIO.cpp +++ b/hal/architecture/Linux/drivers/core/GPIO.cpp @@ -24,63 +24,53 @@ #include #include #include +#include #include "log.h" - +// Chip 0 on older Pi models, chip 4 on Pi 5. +#define CHIP "/dev/gpiochip0" // Declare a single default instance GPIOClass GPIO = GPIOClass(); GPIOClass::GPIOClass() { - FILE *f; - DIR* dp; - char file[64]; - - dp = opendir("/sys/class/gpio"); - if (dp == NULL) { - logError("Could not open /sys/class/gpio directory"); - exit(1); - } + struct gpiochip_info chip_info; + struct gpiod_line_request handle_request; - lastPinNum = 0; - while (true) { - dirent *de = readdir(dp); - if (de == NULL) { - break; - } + int file_descriptor = open(CHIP, O_RDONLY); - if (strncmp("gpiochip", de->d_name, 8) == 0) { - snprintf(file, sizeof(file), "/sys/class/gpio/%s/base", de->d_name); - f = fopen(file, "r"); - int base; - if (fscanf(f, "%d", &base) == EOF) { - logError("Failed to open %s\n", file); - base = 0; - } - fclose(f); - - snprintf(file, sizeof(file), "/sys/class/gpio/%s/ngpio", de->d_name); - f = fopen(file, "r"); - int ngpio; - if (fscanf(f, "%d", &ngpio) == EOF) { - logError("Failed to open %s\n", file); - ngpio = 0; - } - fclose(f); - - int max = ngpio + base - 1; - if (lastPinNum < max) { - lastPinNum = max; - } - } + if (file_descriptor < 0) { + printf("Failed opening GPIO chip.\n"); + return 1; } - closedir(dp); - exportedPins = new uint8_t[lastPinNum + 1]; + res = ioctl(file_descriptor, GPIO_GET_CHIPINFO_IOCTL, &chip_info); - for (int i = 0; i < lastPinNum + 1; ++i) { - exportedPins[i] = 0; + if (res < 0) { + printf("Failed getting chip information.\n"); + close(file_descriptor); + return 1; + } + + + printf("GPIO chip information:\n"); + printf("name: %s\n",chip_info.name); + printf("label: %s\n",chip_info.label); + printf("lines: %i\n", chip_info.lines); + + for (int i = 0; i < chip_info.lines; i++) { + + struct gpioline_info line_info; + + line_info.line_offset = i; + + if (ioctl(file_descriptor, GPIO_GET_LINEINFO_IOCTL, &line_info) < 0) { + printf("Failed getting line %i info.\n", i); + } else { + printf("%d %s\n",i,line_info.name); + } } + } GPIOClass::GPIOClass(const GPIOClass& other) @@ -93,103 +83,81 @@ GPIOClass::GPIOClass(const GPIOClass& other) } } +// destructor GPIOClass::~GPIOClass() { - FILE *f; + close(handle_request.fd); - for (int i = 0; i < lastPinNum + 1; ++i) { - if (exportedPins[i]) { - f = fopen("/sys/class/gpio/unexport", "w"); - fprintf(f, "%d\n", i); - fclose(f); - } - } + // Tell the thread to finish + // TERM = 1; + + // Give it time + sleep(1); + + // Close resources + close(file_descriptor); + + // Rejoin main thread with finished thread + pthread_join(reader_thread,NULL); + + return 0; - delete [] exportedPins; } void GPIOClass::pinMode(uint8_t pin, uint8_t mode) { - FILE *f; + // Request handle on writing line. Many can be requested instead of one. - if (pin > lastPinNum) { - return; - } - - f = fopen("/sys/class/gpio/export", "w"); - fprintf(f, "%d\n", pin); - fclose(f); - - int counter = 0; - char file[128]; - sprintf(file, "/sys/class/gpio/gpio%d/direction", pin); - - while ((f = fopen(file,"w")) == NULL) { - // Wait 10 seconds for the file to be accessible if not open on first attempt - sleep(1); - counter++; - if (counter > 10) { - logError("Could not open /sys/class/gpio/gpio%u/direction", pin); - exit(1); - } - } - if (mode == INPUT) { - fprintf(f, "in\n"); - } else { - fprintf(f, "out\n"); - } + handle_request.lineoffsets[0] = pin; + handle_request.flags = + (mode==INPUT?GPIOHANDLE_REQUEST_INPUT:GPIOHANDLE_REQUEST_OUTPUT; + //| GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; + handle_request.lines = 1; - exportedPins[pin] = 1; + res = ioctl(file_descriptor, GPIO_GET_LINEHANDLE_IOCTL, &handle_request); - fclose(f); + if (res < 0) { + printf("Failed requesting write handle.\n"); + close(file_descriptor); + return 1; + } } void GPIOClass::digitalWrite(uint8_t pin, uint8_t value) { - FILE *f; - char file[128]; - if (pin > lastPinNum) { - return; - } - if (0 == exportedPins[pin]) { - pinMode(pin, OUTPUT); - } + // Request handle on writing line. Many can be requested instead of one. - sprintf(file, "/sys/class/gpio/gpio%d/value", pin); - f = fopen(file, "w"); + handle_request.lineoffsets[0] = WRITE_GPIO; + handle_request.flags = GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; + handle_request.lines = 1; - if (value == 0) { - fprintf(f, "0\n"); - } else { - fprintf(f, "1\n"); - } + gpiod_line_value line_value = (value==0?GPIOD_LINE_VALUE_INACTIVE:GPIOD_LINE_VALUE_ACTIVE); + + res = gpio_line_request_set_value(handle_request,0,line_value); - fclose(f); + if (res < 0) { + printf("Failed requesting write handle.\n"); + return 1; + } } uint8_t GPIOClass::digitalRead(uint8_t pin) { - FILE *f; - char file[128]; - if (pin > lastPinNum) { - return 0; - } - if (0 == exportedPins[pin]) { - pinMode(pin, INPUT); - } + // Request handle on line. Many can be requested instead of one. + + handle_request.lineoffsets[0] = pin; + handle_request.flags = GPIOHANDLE_REQUEST_INPUT; + handle_request.lines = 1; - sprintf(file, "/sys/class/gpio/gpio%d/value", pin); - f = fopen(file, "r"); + res = gpiod_line_request_get_value(handle_request,0); - int i; - if (fscanf(f, "%d", &i) == EOF) { - logError("digitalRead: failed to read pin %u\n", pin); - i = 0; + if (res < 0) { + printf("Failed requesting write handle.\n"); + return 1; } - fclose(f); - return i; + return res; } uint8_t GPIOClass::digitalPinToInterrupt(uint8_t pin) diff --git a/hal/architecture/Linux/drivers/core/GPIO_experiment.cpp b/hal/architecture/Linux/drivers/core/GPIO_experiment.cpp new file mode 100644 index 000000000..aa1fc1969 --- /dev/null +++ b/hal/architecture/Linux/drivers/core/GPIO_experiment.cpp @@ -0,0 +1,266 @@ +// GPIO sample program: sends data on GPIO 17, receives on GPIO 27. +// Public domain. + +#include // everything about GPIOs +#include // ioctl() +#include // open(), O_RDONLY +#include // close() +#include // printf() +#include // poll() +#include // threading + +// Chip 0 on older Pi models, chip 4 on Pi 5. +#define CHIP "/dev/gpiochip0" + +// GPIO line numbers. +#define WRITE_GPIO 17 +#define READ_GPIO 27 + +// Termination flag. +// This is not an example of good coding style. + +volatile int TERM = 0; + +// Data structures from Linux GPIO libs. +// These here are global because a thread needs them. +// This is not an example of good coding style. + +struct gpioevent_request event_request; + +struct pollfd poll_file_descriptor; + + +void *reader(void *arg) +{ + + // Elevate reader thread priority and use a FIFO scheduler. + + pthread_t thread = pthread_self(); + const struct sched_param sparam = { .sched_priority = 99, }; + int res = pthread_setschedparam(thread, SCHED_FIFO, &sparam); + if (res != 0) { + printf("Failed to elevate reader thread priority!\n"); + } + + + struct gpioevent_data event_data; + + poll_file_descriptor.fd = event_request.fd; + poll_file_descriptor.events = POLLIN; + + // Some variables for evaluating throughput + + __u64 first_event_timestamp = 0; + __u64 last_event_timestamp = 0; + __u64 count_rising = 0; + __u64 count_falling = 0; + + // Receive loop + + while (TERM == 0) { + + int poll_result = poll(&poll_file_descriptor, 1, 1); // time out after 1 milliseconds + + if (poll_result == 0) { + // printf("Poll timeout.\n"); + continue; + } + + if (poll_result < 0) { + // printf("Poll error.\n"); + continue; + } + + if (poll_result > 1) { + // printf("Multiple events per poll.\n"); + } + + // The "revents" field counts returned events. + // The "POLLIN" constant seems to be a bitmask. + + if (poll_file_descriptor.revents & POLLIN) { + + int read_result = read(poll_file_descriptor.fd, &event_data, sizeof(event_data)); + + if (read_result == -1) { + // printf("Read error.\n"); + continue; + } + + if (event_data.id == GPIOEVENT_EVENT_RISING_EDGE) { + count_rising++; + // printf("Rising edge at %llu.\n", event_data.timestamp); + } else if (event_data.id == GPIOEVENT_EVENT_FALLING_EDGE) { + count_falling++; + // printf("Falling edge at %llu.\n",event_data.timestamp); + } else { + // printf("Some other event?\n"); + } + + if (first_event_timestamp == 0) { + first_event_timestamp = event_data.timestamp; + } else { + last_event_timestamp = event_data.timestamp; + } + } + } + + printf("Received %llu rising and %llu falling edges.\n",count_rising,count_falling); + + __u64 duration = last_event_timestamp - first_event_timestamp; + double seconds = ((double) duration / (double) 1000000000); + + printf("Total duration %llu ns (%f s).\n",duration,seconds); + + __u64 nanos_per_edge = duration / (count_rising + count_falling); + + printf("Average %llu ns (%llu microseconds) per edge.\n",nanos_per_edge,(nanos_per_edge/1000)); + + __u64 per_second = count_rising / seconds; + + printf("Rising edge frequency %llu Hz.\n",per_second); + + close(poll_file_descriptor.fd); + + return 0; +} + + +int main(int argc, char * const *argv) +{ + + int res; // various call results + + // Elevate main thread priority and use a FIFO scheduler. + + const struct sched_param sparam = { .sched_priority = 99, }; + res = sched_setscheduler(0, SCHED_FIFO, &sparam); + if (res != 0) { + printf("Failed to elevate main thread priority!\n"); + } + + // Data structures from Linux GPIO libs. + + struct gpiochip_info chip_info; + struct gpiohandle_request handle_request; + + + int file_descriptor = open(CHIP, O_RDONLY); + + if (file_descriptor < 0) { + printf("Failed opening GPIO chip.\n"); + return 1; + } + + res = ioctl(file_descriptor, GPIO_GET_CHIPINFO_IOCTL, &chip_info); + + if (res < 0) { + printf("Failed getting chip information.\n"); + close(file_descriptor); + return 1; + } + + + printf("GPIO chip information:\n"); + printf("name: %s\n",chip_info.name); + printf("label: %s\n",chip_info.label); + printf("lines: %i\n", chip_info.lines); + + for (int i = 0; i < chip_info.lines; i++) { + + struct gpioline_info line_info; + + line_info.line_offset = i; + + if (ioctl(file_descriptor, GPIO_GET_LINEINFO_IOCTL, &line_info) < 0) { + printf("Failed getting line %i info.\n", i); + } else { + printf("%d %s\n",i,line_info.name); + } + } + + + // Request events on the reading line. + + event_request.lineoffset = READ_GPIO; + event_request.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES; + event_request.handleflags = GPIOHANDLE_REQUEST_INPUT | GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; + + res = ioctl(file_descriptor, GPIO_GET_LINEEVENT_IOCTL, &event_request); + + if (res < 0) { + printf("Failed requesting events.\n"); + close(file_descriptor); + return 1; + } + + + // Request handle on writing line. Many can be requested instead of one. + + handle_request.lineoffsets[0] = WRITE_GPIO; + handle_request.flags = GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; + handle_request.lines = 1; + + res = ioctl(file_descriptor, GPIO_GET_LINEHANDLE_IOCTL, &handle_request); + + if (res < 0) { + printf("Failed requesting write handle.\n"); + close(file_descriptor); + return 1; + } + + // Start a reader thread + pthread_t reader_thread; + pthread_create(&reader_thread, NULL, &reader, NULL); + + + // Data handle for writing + struct gpiohandle_data hdata; + + // Time variables for sleeping a short interval. + struct timespec ts, tr; + ts.tv_sec = 0; + + printf("------------ now writing and reading -------------------\n"); + + // Write something out ad hope the reader reads it + for (int i = 0; i < 1000; i++) { + + hdata.values[0] = 1; + res = ioctl(handle_request.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &hdata); + if (res == -1) { + printf("Failed setting line value.\n"); + } + + // Try to sleep for 5000 nanoseconds. + // In reality, due to call latencies, you end up sleeping longer. + + ts.tv_nsec = 5000; + nanosleep(&ts, &tr); + + hdata.values[0] = 0; + res = ioctl(handle_request.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &hdata); + if (res == -1) { + printf("Failed setting line value.\n"); + } + + ts.tv_nsec = 5000; + nanosleep(&ts, &tr); + } + + close(handle_request.fd); + + // Tell the thread to finish + TERM = 1; + + // Give it time + sleep(1); + + // Close resources + close(file_descriptor); + + // Rejoin main thread with finished thread + pthread_join(reader_thread,NULL); + + return 0; +} From 55ab7b504baf34d7f1941dd96b43664094047d33 Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 4 Feb 2025 22:38:03 +0100 Subject: [PATCH 09/13] Revert "GPIO issues" This reverts commit 29ecc913eeeb85afca328efa0060cd4a990421a3. --- MyConfig.h | 2 +- hal/architecture/Linux/drivers/core/GPIO.cpp | 196 +++++++------ .../Linux/drivers/core/GPIO_experiment.cpp | 266 ------------------ 3 files changed, 115 insertions(+), 349 deletions(-) delete mode 100644 hal/architecture/Linux/drivers/core/GPIO_experiment.cpp diff --git a/MyConfig.h b/MyConfig.h index 13e41d8fc..c26ffdde2 100755 --- a/MyConfig.h +++ b/MyConfig.h @@ -754,7 +754,7 @@ * @brief %RFM69 Network ID. Use the same for all nodes that will talk to each other. */ #ifndef MY_RFM69_NETWORKID -define MY_RFM69_NETWORKID (197) +#define MY_RFM69_NETWORKID (100) #endif /** diff --git a/hal/architecture/Linux/drivers/core/GPIO.cpp b/hal/architecture/Linux/drivers/core/GPIO.cpp index 7f00e5507..eb371fb25 100644 --- a/hal/architecture/Linux/drivers/core/GPIO.cpp +++ b/hal/architecture/Linux/drivers/core/GPIO.cpp @@ -24,53 +24,63 @@ #include #include #include -#include #include "log.h" -// Chip 0 on older Pi models, chip 4 on Pi 5. -#define CHIP "/dev/gpiochip0" + // Declare a single default instance GPIOClass GPIO = GPIOClass(); GPIOClass::GPIOClass() { - struct gpiochip_info chip_info; - struct gpiod_line_request handle_request; - - - int file_descriptor = open(CHIP, O_RDONLY); - - if (file_descriptor < 0) { - printf("Failed opening GPIO chip.\n"); - return 1; + FILE *f; + DIR* dp; + char file[64]; + + dp = opendir("/sys/class/gpio"); + if (dp == NULL) { + logError("Could not open /sys/class/gpio directory"); + exit(1); } - res = ioctl(file_descriptor, GPIO_GET_CHIPINFO_IOCTL, &chip_info); - - if (res < 0) { - printf("Failed getting chip information.\n"); - close(file_descriptor); - return 1; - } - - - printf("GPIO chip information:\n"); - printf("name: %s\n",chip_info.name); - printf("label: %s\n",chip_info.label); - printf("lines: %i\n", chip_info.lines); - - for (int i = 0; i < chip_info.lines; i++) { + lastPinNum = 0; - struct gpioline_info line_info; - - line_info.line_offset = i; + while (true) { + dirent *de = readdir(dp); + if (de == NULL) { + break; + } - if (ioctl(file_descriptor, GPIO_GET_LINEINFO_IOCTL, &line_info) < 0) { - printf("Failed getting line %i info.\n", i); - } else { - printf("%d %s\n",i,line_info.name); + if (strncmp("gpiochip", de->d_name, 8) == 0) { + snprintf(file, sizeof(file), "/sys/class/gpio/%s/base", de->d_name); + f = fopen(file, "r"); + int base; + if (fscanf(f, "%d", &base) == EOF) { + logError("Failed to open %s\n", file); + base = 0; + } + fclose(f); + + snprintf(file, sizeof(file), "/sys/class/gpio/%s/ngpio", de->d_name); + f = fopen(file, "r"); + int ngpio; + if (fscanf(f, "%d", &ngpio) == EOF) { + logError("Failed to open %s\n", file); + ngpio = 0; + } + fclose(f); + + int max = ngpio + base - 1; + if (lastPinNum < max) { + lastPinNum = max; + } } } + closedir(dp); + exportedPins = new uint8_t[lastPinNum + 1]; + + for (int i = 0; i < lastPinNum + 1; ++i) { + exportedPins[i] = 0; + } } GPIOClass::GPIOClass(const GPIOClass& other) @@ -83,81 +93,103 @@ GPIOClass::GPIOClass(const GPIOClass& other) } } -// destructor GPIOClass::~GPIOClass() { - close(handle_request.fd); - - // Tell the thread to finish - // TERM = 1; - - // Give it time - sleep(1); - - // Close resources - close(file_descriptor); + FILE *f; - // Rejoin main thread with finished thread - pthread_join(reader_thread,NULL); - - return 0; + for (int i = 0; i < lastPinNum + 1; ++i) { + if (exportedPins[i]) { + f = fopen("/sys/class/gpio/unexport", "w"); + fprintf(f, "%d\n", i); + fclose(f); + } + } + delete [] exportedPins; } void GPIOClass::pinMode(uint8_t pin, uint8_t mode) { - // Request handle on writing line. Many can be requested instead of one. - - handle_request.lineoffsets[0] = pin; - handle_request.flags = - (mode==INPUT?GPIOHANDLE_REQUEST_INPUT:GPIOHANDLE_REQUEST_OUTPUT; - //| GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; - handle_request.lines = 1; + FILE *f; - res = ioctl(file_descriptor, GPIO_GET_LINEHANDLE_IOCTL, &handle_request); + if (pin > lastPinNum) { + return; + } - if (res < 0) { - printf("Failed requesting write handle.\n"); - close(file_descriptor); - return 1; + f = fopen("/sys/class/gpio/export", "w"); + fprintf(f, "%d\n", pin); + fclose(f); + + int counter = 0; + char file[128]; + sprintf(file, "/sys/class/gpio/gpio%d/direction", pin); + + while ((f = fopen(file,"w")) == NULL) { + // Wait 10 seconds for the file to be accessible if not open on first attempt + sleep(1); + counter++; + if (counter > 10) { + logError("Could not open /sys/class/gpio/gpio%u/direction", pin); + exit(1); + } } + if (mode == INPUT) { + fprintf(f, "in\n"); + } else { + fprintf(f, "out\n"); + } + + exportedPins[pin] = 1; + + fclose(f); } void GPIOClass::digitalWrite(uint8_t pin, uint8_t value) { + FILE *f; + char file[128]; - // Request handle on writing line. Many can be requested instead of one. - - handle_request.lineoffsets[0] = WRITE_GPIO; - handle_request.flags = GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; - handle_request.lines = 1; - - gpiod_line_value line_value = (value==0?GPIOD_LINE_VALUE_INACTIVE:GPIOD_LINE_VALUE_ACTIVE); + if (pin > lastPinNum) { + return; + } + if (0 == exportedPins[pin]) { + pinMode(pin, OUTPUT); + } - res = gpio_line_request_set_value(handle_request,0,line_value); + sprintf(file, "/sys/class/gpio/gpio%d/value", pin); + f = fopen(file, "w"); - if (res < 0) { - printf("Failed requesting write handle.\n"); - return 1; + if (value == 0) { + fprintf(f, "0\n"); + } else { + fprintf(f, "1\n"); } + + fclose(f); } uint8_t GPIOClass::digitalRead(uint8_t pin) { + FILE *f; + char file[128]; - // Request handle on line. Many can be requested instead of one. - - handle_request.lineoffsets[0] = pin; - handle_request.flags = GPIOHANDLE_REQUEST_INPUT; - handle_request.lines = 1; + if (pin > lastPinNum) { + return 0; + } + if (0 == exportedPins[pin]) { + pinMode(pin, INPUT); + } - res = gpiod_line_request_get_value(handle_request,0); + sprintf(file, "/sys/class/gpio/gpio%d/value", pin); + f = fopen(file, "r"); - if (res < 0) { - printf("Failed requesting write handle.\n"); - return 1; + int i; + if (fscanf(f, "%d", &i) == EOF) { + logError("digitalRead: failed to read pin %u\n", pin); + i = 0; } - return res; + fclose(f); + return i; } uint8_t GPIOClass::digitalPinToInterrupt(uint8_t pin) diff --git a/hal/architecture/Linux/drivers/core/GPIO_experiment.cpp b/hal/architecture/Linux/drivers/core/GPIO_experiment.cpp deleted file mode 100644 index aa1fc1969..000000000 --- a/hal/architecture/Linux/drivers/core/GPIO_experiment.cpp +++ /dev/null @@ -1,266 +0,0 @@ -// GPIO sample program: sends data on GPIO 17, receives on GPIO 27. -// Public domain. - -#include // everything about GPIOs -#include // ioctl() -#include // open(), O_RDONLY -#include // close() -#include // printf() -#include // poll() -#include // threading - -// Chip 0 on older Pi models, chip 4 on Pi 5. -#define CHIP "/dev/gpiochip0" - -// GPIO line numbers. -#define WRITE_GPIO 17 -#define READ_GPIO 27 - -// Termination flag. -// This is not an example of good coding style. - -volatile int TERM = 0; - -// Data structures from Linux GPIO libs. -// These here are global because a thread needs them. -// This is not an example of good coding style. - -struct gpioevent_request event_request; - -struct pollfd poll_file_descriptor; - - -void *reader(void *arg) -{ - - // Elevate reader thread priority and use a FIFO scheduler. - - pthread_t thread = pthread_self(); - const struct sched_param sparam = { .sched_priority = 99, }; - int res = pthread_setschedparam(thread, SCHED_FIFO, &sparam); - if (res != 0) { - printf("Failed to elevate reader thread priority!\n"); - } - - - struct gpioevent_data event_data; - - poll_file_descriptor.fd = event_request.fd; - poll_file_descriptor.events = POLLIN; - - // Some variables for evaluating throughput - - __u64 first_event_timestamp = 0; - __u64 last_event_timestamp = 0; - __u64 count_rising = 0; - __u64 count_falling = 0; - - // Receive loop - - while (TERM == 0) { - - int poll_result = poll(&poll_file_descriptor, 1, 1); // time out after 1 milliseconds - - if (poll_result == 0) { - // printf("Poll timeout.\n"); - continue; - } - - if (poll_result < 0) { - // printf("Poll error.\n"); - continue; - } - - if (poll_result > 1) { - // printf("Multiple events per poll.\n"); - } - - // The "revents" field counts returned events. - // The "POLLIN" constant seems to be a bitmask. - - if (poll_file_descriptor.revents & POLLIN) { - - int read_result = read(poll_file_descriptor.fd, &event_data, sizeof(event_data)); - - if (read_result == -1) { - // printf("Read error.\n"); - continue; - } - - if (event_data.id == GPIOEVENT_EVENT_RISING_EDGE) { - count_rising++; - // printf("Rising edge at %llu.\n", event_data.timestamp); - } else if (event_data.id == GPIOEVENT_EVENT_FALLING_EDGE) { - count_falling++; - // printf("Falling edge at %llu.\n",event_data.timestamp); - } else { - // printf("Some other event?\n"); - } - - if (first_event_timestamp == 0) { - first_event_timestamp = event_data.timestamp; - } else { - last_event_timestamp = event_data.timestamp; - } - } - } - - printf("Received %llu rising and %llu falling edges.\n",count_rising,count_falling); - - __u64 duration = last_event_timestamp - first_event_timestamp; - double seconds = ((double) duration / (double) 1000000000); - - printf("Total duration %llu ns (%f s).\n",duration,seconds); - - __u64 nanos_per_edge = duration / (count_rising + count_falling); - - printf("Average %llu ns (%llu microseconds) per edge.\n",nanos_per_edge,(nanos_per_edge/1000)); - - __u64 per_second = count_rising / seconds; - - printf("Rising edge frequency %llu Hz.\n",per_second); - - close(poll_file_descriptor.fd); - - return 0; -} - - -int main(int argc, char * const *argv) -{ - - int res; // various call results - - // Elevate main thread priority and use a FIFO scheduler. - - const struct sched_param sparam = { .sched_priority = 99, }; - res = sched_setscheduler(0, SCHED_FIFO, &sparam); - if (res != 0) { - printf("Failed to elevate main thread priority!\n"); - } - - // Data structures from Linux GPIO libs. - - struct gpiochip_info chip_info; - struct gpiohandle_request handle_request; - - - int file_descriptor = open(CHIP, O_RDONLY); - - if (file_descriptor < 0) { - printf("Failed opening GPIO chip.\n"); - return 1; - } - - res = ioctl(file_descriptor, GPIO_GET_CHIPINFO_IOCTL, &chip_info); - - if (res < 0) { - printf("Failed getting chip information.\n"); - close(file_descriptor); - return 1; - } - - - printf("GPIO chip information:\n"); - printf("name: %s\n",chip_info.name); - printf("label: %s\n",chip_info.label); - printf("lines: %i\n", chip_info.lines); - - for (int i = 0; i < chip_info.lines; i++) { - - struct gpioline_info line_info; - - line_info.line_offset = i; - - if (ioctl(file_descriptor, GPIO_GET_LINEINFO_IOCTL, &line_info) < 0) { - printf("Failed getting line %i info.\n", i); - } else { - printf("%d %s\n",i,line_info.name); - } - } - - - // Request events on the reading line. - - event_request.lineoffset = READ_GPIO; - event_request.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES; - event_request.handleflags = GPIOHANDLE_REQUEST_INPUT | GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; - - res = ioctl(file_descriptor, GPIO_GET_LINEEVENT_IOCTL, &event_request); - - if (res < 0) { - printf("Failed requesting events.\n"); - close(file_descriptor); - return 1; - } - - - // Request handle on writing line. Many can be requested instead of one. - - handle_request.lineoffsets[0] = WRITE_GPIO; - handle_request.flags = GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; - handle_request.lines = 1; - - res = ioctl(file_descriptor, GPIO_GET_LINEHANDLE_IOCTL, &handle_request); - - if (res < 0) { - printf("Failed requesting write handle.\n"); - close(file_descriptor); - return 1; - } - - // Start a reader thread - pthread_t reader_thread; - pthread_create(&reader_thread, NULL, &reader, NULL); - - - // Data handle for writing - struct gpiohandle_data hdata; - - // Time variables for sleeping a short interval. - struct timespec ts, tr; - ts.tv_sec = 0; - - printf("------------ now writing and reading -------------------\n"); - - // Write something out ad hope the reader reads it - for (int i = 0; i < 1000; i++) { - - hdata.values[0] = 1; - res = ioctl(handle_request.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &hdata); - if (res == -1) { - printf("Failed setting line value.\n"); - } - - // Try to sleep for 5000 nanoseconds. - // In reality, due to call latencies, you end up sleeping longer. - - ts.tv_nsec = 5000; - nanosleep(&ts, &tr); - - hdata.values[0] = 0; - res = ioctl(handle_request.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &hdata); - if (res == -1) { - printf("Failed setting line value.\n"); - } - - ts.tv_nsec = 5000; - nanosleep(&ts, &tr); - } - - close(handle_request.fd); - - // Tell the thread to finish - TERM = 1; - - // Give it time - sleep(1); - - // Close resources - close(file_descriptor); - - // Rejoin main thread with finished thread - pthread_join(reader_thread,NULL); - - return 0; -} From 6f42fbf06c5fbbf4df17be6766adb34f9a5be925 Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 4 Feb 2025 22:42:41 +0100 Subject: [PATCH 10/13] GPIO issue 1567 --- configure | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/configure b/configure index 1281ebe3c..f2ffaff8c 100755 --- a/configure +++ b/configure @@ -577,10 +577,9 @@ if [ -z "${SOC}" ]; then info=($(detect_machine)) SOC=${info[0]} TYPE=${info[1]} - CPU=${info[2]} printf " ${OK} machine detected: SoC=${SOC}, Type=${TYPE}, CPU=${CPU}.\n" fi - +CPU=$(eval "uname -m 2>/dev/null") if [ -z "${CPUFLAGS}" ]; then CPUFLAGS=$(gcc_cpu_flags "${SOC}" "${CPU}") fi From 031957d31bf202ca329281c79d1da49b322c0ca9 Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 4 Feb 2025 22:58:21 +0100 Subject: [PATCH 11/13] Revert "Minor changes" This reverts commit f157ed03048705d629d078fe3e421926cc77d2f7. --- hal/architecture/Linux/drivers/core/interrupt.h | 1 + 1 file changed, 1 insertion(+) diff --git a/hal/architecture/Linux/drivers/core/interrupt.h b/hal/architecture/Linux/drivers/core/interrupt.h index d503fbcd9..b382e31ba 100644 --- a/hal/architecture/Linux/drivers/core/interrupt.h +++ b/hal/architecture/Linux/drivers/core/interrupt.h @@ -24,6 +24,7 @@ #include +// Ajout RRO #include #define CHANGE 1 From 3d4709539fe1f1155fe43341759607ec7c07319c Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 4 Feb 2025 22:58:21 +0100 Subject: [PATCH 12/13] Revert "Updated for style and minors non functional changes" This reverts commit 3dba00fd7a8050225e4e8278f7364c7ef2375d93. --- MyConfig.h | 2 +- hal/architecture/Linux/drivers/core/interrupt.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/MyConfig.h b/MyConfig.h index c26ffdde2..4bf753658 100755 --- a/MyConfig.h +++ b/MyConfig.h @@ -442,7 +442,7 @@ * - RF24_PA_MAX = 0dBm */ #ifndef MY_RF24_PA_LEVEL -#define MY_RF24_PA_LEVEL (RF24_PA_HIGH) +#define MY_RF24_PA_LEVEL (RF24_PA_MAX) #endif /** diff --git a/hal/architecture/Linux/drivers/core/interrupt.cpp b/hal/architecture/Linux/drivers/core/interrupt.cpp index 6fc50fe3f..d11920fed 100644 --- a/hal/architecture/Linux/drivers/core/interrupt.cpp +++ b/hal/architecture/Linux/drivers/core/interrupt.cpp @@ -131,7 +131,7 @@ void *interruptHandler(void *args) } } // Adding gpiod closing instructions - gpiod_line_release(line); + gpiod_line_release(line); close(fd); @@ -220,6 +220,14 @@ void detachInterrupt(uint8_t gpioPin) close(sysFds[gpioPin]); sysFds[gpioPin] = -1; } + +// FILE *fp = fopen("/sys/class/gpio/unexport", "w"); +// if (fp == NULL) { +// logError("Unable to unexport pin %d for interrupt\n", gpioPin); +// exit(1); +// } +// fprintf(fp, "%d", gpioPin); +// fclose(fp); } void interrupts() From 76beb1f39daeb4819bd992783df67c9f52a4587e Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 4 Feb 2025 22:58:21 +0100 Subject: [PATCH 13/13] Revert "Bug fix for GPIO under recent Linux kernel versions" This reverts commit 2cfe8339a37a110caad322b40ffe5e404116ddc9. --- Makefile | 2 +- MyConfig.h | 2 +- configure | 3 +- .../Linux/drivers/core/interrupt.cpp | 102 ++++++++++-------- .../Linux/drivers/core/interrupt.h | 3 - 5 files changed, 63 insertions(+), 49 deletions(-) diff --git a/Makefile b/Makefile index e02134bb5..5b2ef8bde 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ $(ARDUINO): $(ARDUINO_LIB_OBJS) # Gateway Build $(GATEWAY): $(GATEWAY_OBJECTS) $(ARDUINO_LIB_OBJS) - $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) $(ARDUINO_LIB_OBJS) $(LATE_LDFLAGS) + $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) $(ARDUINO_LIB_OBJS) # Include all .d files -include $(DEPS) diff --git a/MyConfig.h b/MyConfig.h index 4bf753658..c26ffdde2 100755 --- a/MyConfig.h +++ b/MyConfig.h @@ -442,7 +442,7 @@ * - RF24_PA_MAX = 0dBm */ #ifndef MY_RF24_PA_LEVEL -#define MY_RF24_PA_LEVEL (RF24_PA_MAX) +#define MY_RF24_PA_LEVEL (RF24_PA_HIGH) #endif /** diff --git a/configure b/configure index f2ffaff8c..730734cda 100755 --- a/configure +++ b/configure @@ -318,7 +318,7 @@ signing=none signing_request_signatures=false encryption=false -params="SOC CFLAGS CXXFLAGS CPPFLAGS LDFLAGS LATE_LDFLAGS PREFIX CC CXX ARDUINO_LIB_DIR BUILDDIR BINDIR GATEWAY_DIR INIT_SYSTEM SPI_DRIVER TYPE" +params="SOC CFLAGS CXXFLAGS CPPFLAGS LDFLAGS PREFIX CC CXX ARDUINO_LIB_DIR BUILDDIR BINDIR GATEWAY_DIR INIT_SYSTEM SPI_DRIVER TYPE" for opt do if [ "$opt" = "-h" ] || [ "$opt" = "--help" ]; then @@ -706,7 +706,6 @@ fi LDFLAGS="-pthread $LDFLAGS" CPPFLAGS="$CPUFLAGS $CPPFLAGS" -LATE_LDFLAGS="-Wl,-Bstatic -lgpiod -Wl,-Bdynamic" printf " ${OK} CPPFLAGS: $CPPFLAGS\n" printf " ${OK} CXXFLAGS: $CXXFLAGS\n" diff --git a/hal/architecture/Linux/drivers/core/interrupt.cpp b/hal/architecture/Linux/drivers/core/interrupt.cpp index d11920fed..f57c4cb7a 100644 --- a/hal/architecture/Linux/drivers/core/interrupt.cpp +++ b/hal/architecture/Linux/drivers/core/interrupt.cpp @@ -37,7 +37,6 @@ struct ThreadArgs { void (*func)(); int gpioPin; - struct gpiod_line *line; }; volatile bool interruptsEnabled = true; @@ -88,10 +87,10 @@ int piHiPri(const int pri) void *interruptHandler(void *args) { int fd; + struct pollfd polls; char c; struct ThreadArgs *arguments = (struct ThreadArgs *)args; int gpioPin = arguments->gpioPin; - struct gpiod_line *line = arguments->line; void (*func)() = arguments->func; delete arguments; @@ -102,26 +101,28 @@ void *interruptHandler(void *args) return NULL; } + // Setup poll structure + polls.fd = fd; + polls.events = POLLPRI | POLLERR; + while (1) { // Wait for it ... - // New version - int ret = gpiod_line_event_wait(line, NULL); + int ret = poll(&polls, 1, -1); if (ret < 0) { logError("Error waiting for interrupt: %s\n", strerror(errno)); break; } - struct gpiod_line_event event; - if (gpiod_line_event_read(line, &event) == 0) { - if (event.event_type == GPIOD_LINE_EVENT_RISING_EDGE) { - logInfo("RISING Edge\n"); - } else { - logInfo("FALLING Edge\n"); - } + // Do a dummy read to clear the interrupt + // A one character read appars to be enough. + if (lseek (fd, 0, SEEK_SET) < 0) { + logError("Interrupt handler error: %s\n", strerror(errno)); + break; + } + if (read (fd, &c, 1) < 0) { + logError("Interrupt handler error: %s\n", strerror(errno)); + break; } - // Call user function. - logError("Calling user function\n"); - pthread_mutex_lock(&intMutex); if (interruptsEnabled) { pthread_mutex_unlock(&intMutex); @@ -130,8 +131,6 @@ void *interruptHandler(void *args) pthread_mutex_unlock(&intMutex); } } - // Adding gpiod closing instructions - gpiod_line_release(line); close(fd); @@ -154,53 +153,72 @@ void attachInterrupt(uint8_t gpioPin, void (*func)(), uint8_t mode) usleep(1000); } - char *chipname = "gpiochip0"; - unsigned int line_num = gpioPin; - struct gpiod_line_event event; - struct gpiod_chip *chip; - struct gpiod_line *line; - - - chip = gpiod_chip_open_by_name(chipname); - if (!chip) { - logError("Open chip failed\n"); + // Export pin for interrupt + if ((fd = fopen("/sys/class/gpio/export", "w")) == NULL) { + logError("attachInterrupt: Unable to export pin %d for interrupt: %s\n", gpioPin, strerror(errno)); exit(1); } + fprintf(fd, "%d\n", gpioPin); + fclose(fd); - line = gpiod_chip_get_line(chip, line_num); - if (!line) { - logError("Get line failed\n"); - exit(1); + // Wait a bit the system to create /sys/class/gpio/gpio + usleep(1000); + + snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/direction", gpioPin) ; + if ((fd = fopen (fName, "w")) == NULL) { + logError("attachInterrupt: Unable to open GPIO direction interface for pin %d: %s\n", + gpioPin, strerror(errno)); + exit(1) ; + } + fprintf(fd, "in\n") ; + fclose(fd) ; + + snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/edge", gpioPin) ; + if ((fd = fopen(fName, "w")) == NULL) { + logError("attachInterrupt: Unable to open GPIO edge interface for pin %d: %s\n", gpioPin, + strerror(errno)); + exit(1) ; } switch (mode) { case CHANGE: - gpiod_line_request_both_edges_events(line, "gpiointerrupt"); + fprintf(fd, "both\n"); break; case FALLING: - gpiod_line_request_falling_edge_events(line, "gpiointerrupt"); + fprintf(fd, "falling\n"); break; case RISING: - gpiod_line_request_rising_edge_events(line, "gpiointerrupt"); + fprintf(fd, "rising\n"); break; case NONE: + fprintf(fd, "none\n"); break; default: logError("attachInterrupt: Invalid mode\n"); + fclose(fd); return; } + fclose(fd); if (sysFds[gpioPin] == -1) { - if ((sysFds[gpioPin] = gpiod_line_event_get_fd(line)) < 0) { + snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/value", gpioPin); + if ((sysFds[gpioPin] = open(fName, O_RDONLY)) < 0) { logError("Error reading pin %d: %s\n", gpioPin, strerror(errno)); exit(1); } } + // Clear any initial pending interrupt + ioctl(sysFds[gpioPin], FIONREAD, &count); + for (int i = 0; i < count; ++i) { + if (read(sysFds[gpioPin], &c, 1) == -1) { + logError("attachInterrupt: failed to read pin status: %s\n", strerror(errno)); + } + } + struct ThreadArgs *threadArgs = new struct ThreadArgs; threadArgs->func = func; threadArgs->gpioPin = gpioPin; - threadArgs->line = line; // Create a thread passing the pin and function pthread_create(threadIds[gpioPin], NULL, interruptHandler, (void *)threadArgs); @@ -221,13 +239,13 @@ void detachInterrupt(uint8_t gpioPin) sysFds[gpioPin] = -1; } -// FILE *fp = fopen("/sys/class/gpio/unexport", "w"); -// if (fp == NULL) { -// logError("Unable to unexport pin %d for interrupt\n", gpioPin); -// exit(1); -// } -// fprintf(fp, "%d", gpioPin); -// fclose(fp); + FILE *fp = fopen("/sys/class/gpio/unexport", "w"); + if (fp == NULL) { + logError("Unable to unexport pin %d for interrupt\n", gpioPin); + exit(1); + } + fprintf(fp, "%d", gpioPin); + fclose(fp); } void interrupts() diff --git a/hal/architecture/Linux/drivers/core/interrupt.h b/hal/architecture/Linux/drivers/core/interrupt.h index b382e31ba..b1589251b 100644 --- a/hal/architecture/Linux/drivers/core/interrupt.h +++ b/hal/architecture/Linux/drivers/core/interrupt.h @@ -24,9 +24,6 @@ #include -// Ajout RRO -#include - #define CHANGE 1 #define FALLING 2 #define RISING 3