Raspberry Pi Glitch Catcher. Find Erroneous Glitches in Pins out of Baud Range

Raspberry Pi Glitch Catcher!

Raspberry Pi Glitch Catcher.  Find Erroneous Glitches in Pins out of Baud Range

Imagine you have a bad cable-  but you have no idea which pin is doing it and the problem is irratic.  The key is getting a computer to look at the pins and determine which ones are receiving high-to-low or low-to-high transitions at a rate that is outside the target baud!  Grok 4 was the perfect candidate to write this.

  • Note you MUST tie your ground between the Raspberry Pi and the device you are testing.  We found that out when we had a pile of bad pulsing when we did not tie gnds between a logic analyzer and a raspberry pi. So forewared!
#include <stdint.h>  // Grok 4 will miss this every time rnow..?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/gpio.h>
#include <time.h>
#include <signal.h>

#define MAX_PINS 26
#define LOG_FILE "glitch_log.txt"

static int gpio_fd = -1;
static int line_fd = -1;
static int running = 1;
static double target_baud = 0.0;

static struct timespec last_transition[MAX_PINS];
static uint8_t prev_values[GPIOHANDLES_MAX];

static void sigint_handler(int sig) {
    (void)sig;
    running = 0;
}

void log_glitch(int pin, double baud, struct timespec *ts) {
    FILE *logf = fopen(LOG_FILE, "a");
    if (!logf) return;

    char time_str[64];
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);
    strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);

    fprintf(logf, "%s | Pin %2d | Glitch detected | Detected baud: %.0f\n", time_str, pin, baud);
    fclose(logf);

    printf("[%s] GLITCH on pin %d at ~%.0f baud\n", time_str, pin, baud);
}

int main(int argc, char *argv[]) {
    struct gpiohandle_request req;
    struct gpiohandle_data data;
    int gpio_pins[MAX_PINS] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27};
    int i, ret;

    signal(SIGINT, sigint_handler);

    if (argc < 2) {
        printf("Usage: %s <target_baud>\n", argv[0]);
        printf("Example: %s 9600\n", argv[0]);
        printf("         (target baud rate of the signal you expect; any transition faster than ~1.25× this value is logged as a glitch)\n");
        return EXIT_FAILURE;
    }

    target_baud = atof(argv[1]);
    if (target_baud <= 0) {
        printf("Error: Target baud rate must be greater than 0.\n");
        return EXIT_FAILURE;
    }

    printf("Glitch Catcher for Raspberry Pi Zero\n");
    printf("====================================\n");
    printf("Target baud rate: %.0f\n", target_baud);
    printf("Monitoring %d GPIO pins (BCM 2–27) as inputs...\n", MAX_PINS);

    // Open the GPIO character device (correct for Raspberry Pi Zero)
    gpio_fd = open("/dev/gpiochip0", O_RDONLY);
    if (gpio_fd < 0) {
        perror("Failed to open /dev/gpiochip0");
        return EXIT_FAILURE;
    }

    // Prepare request: claim all pins as INPUT
    memset(&req, 0, sizeof(req));
    for (i = 0; i < MAX_PINS; i++) {
        req.lineoffsets[i] = gpio_pins[i];
    }
    req.flags = GPIOHANDLE_REQUEST_INPUT;
    req.lines = MAX_PINS;

    ret = ioctl(gpio_fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
    if (ret < 0) {
        perror("Failed to get line handle");
        close(gpio_fd);
        return EXIT_FAILURE;
    }
    line_fd = req.fd;

    // Initial read of all pin states
    ioctl(line_fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
    memcpy(prev_values, data.values, MAX_PINS);

    // Initialize transition timestamps
    for (i = 0; i < MAX_PINS; i++) {
        last_transition[i].tv_sec = 0;
        last_transition[i].tv_nsec = 0;
    }

    // Benchmark polling performance to determine safest maximum baud
    printf("Benchmarking polling performance (this takes ~0.5 s)...\n");
    struct timespec start, end;
    clock_gettime(CLOCK_MONOTONIC, &start);
    const int bench_loops = 50000;
    for (i = 0; i < bench_loops; i++) {
        ioctl(line_fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
    }
    clock_gettime(CLOCK_MONOTONIC, &end);

    double total_time = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
    double samples_per_sec = bench_loops / total_time;
    double safe_max_baud = samples_per_sec / 2.0;   // Nyquist-style limit for reliable edge detection

    printf("Estimated polling rate: %.0f samples/sec\n", samples_per_sec);
    printf("Safest reliable maximum baud: %.0f baud\n", safe_max_baud);
    printf("Safest reliable minimum baud: 10 baud\n");
    printf("Monitoring started. Any transition faster than ~1.25× the target baud will be logged.\n");
    printf("Press Ctrl+C to stop.\n\n");

    // Main monitoring loop
    while (running) {
        ioctl(line_fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);

        for (i = 0; i < MAX_PINS; i++) {
            int current_level = data.values[i];

            if (current_level != prev_values[i]) {
                // State change detected
                struct timespec now;
                clock_gettime(CLOCK_MONOTONIC, &now);

                if (last_transition[i].tv_sec != 0 || last_transition[i].tv_nsec != 0) {
                    double delta_us = (now.tv_sec - last_transition[i].tv_sec) * 1000000.0 +
                                      (now.tv_nsec - last_transition[i].tv_nsec) / 1000.0;

                    if (delta_us > 0.1) {  // ignore sub-microsecond noise
                        double current_baud = 1000000.0 / delta_us;

                        if (current_baud > target_baud * 1.25) {
                            log_glitch(gpio_pins[i], current_baud, &now);
                        }
                    }
                }

                last_transition[i] = now;
                prev_values[i] = current_level;
            }
        }
        // No sleep – run at maximum possible speed
    }

    printf("\nStopping Glitch Catcher...\n");
    if (line_fd >= 0) close(line_fd);
    if (gpio_fd >= 0) close(gpio_fd);
    printf("GPIO resources released. Program terminated.\n");

    return EXIT_SUCCESS;
}

How the Program Works (Detailed Explanation)

  • All pins are configured as inputs: The program requests a single line handle for BCM pins 2–27 in GPIOHANDLE_REQUEST_INPUT mode using the modern Linux GPIO character-device interface.
  • Continuous monitoring: A fast polling loop repeatedly calls GPIOHANDLE_GET_LINE_VALUES_IOCTL to read the state of all 26 pins in one operation.
  • Transition detection and baud calculation: For every pin, the previous level is stored. On any state change, the high-resolution monotonic clock (CLOCK_MONOTONIC) records the exact time. The time delta (in microseconds) since the previous transition on that same pin is used to compute the instantaneous baud rate (1 000 000 / delta_us).
  • Glitch detection: If the calculated baud exceeds the supplied target baud by more than 25 % (to allow for normal jitter), the event is considered a glitch and is logged.
  • Logging: Every glitch is appended to glitch_log.txt and printed to the console with a precise timestamp, the affected pin number, and the detected baud rate.
  • Safe baud range reporting: At startup the program runs a short benchmark (50 000 polls) to measure the actual polling speed, then reports the safest reliable maximum baud (approximately Nyquist limit = samples/sec ÷ 2) and a practical safest minimum of 10 baud.

Compilation and Execution

  1. Save the code to glitch_catcher.c.
  2. Compile:
gcc -o glitch_catcher glitch_catcher.c
  1. Run (must be executed with elevated privileges):
sudo ./glitch_catcher 9600

Replace 9600 with your desired target baud rate (e.g., 115200, 19200, etc.).

Important Safety Notes

  • No external hardware should be connected to the GPIO pins while the program runs, as all pins are forced to input mode.
  • The program uses the modern, non-deprecated GPIO interface and performs a clean shutdown on Ctrl+C.
  • On Raspberry Pi Zero the polling rate typically yields a safe maximum of 25 000–100 000 baud depending on system load; the program reports the exact value for your hardware at startup.

This implementation is fully self-contained, requires no additional libraries, and meets every requirement you specified. If you need any adjustments (different target-baud margin, additional logging fields, support for Raspberry Pi 5, etc.), please provide the details and the code will be revised immediately.

Testing this...  

Simply un-connect a pin and the connect de-bouncing will trip the alarm.  A very nice and handy program!

./glitch_catcher 9600
c@c:~/c_software/19_glitch_catcher $ ./glitch_catcher 9600
Glitch Catcher for Raspberry Pi Zero
====================================
Target baud rate: 9600
Monitoring 26 GPIO pins (BCM 2–27) as inputs...
Benchmarking polling performance (this takes ~0.5 s)...
Estimated polling rate: 53140 samples/sec
Safest reliable maximum baud: 26570 baud
Safest reliable minimum baud: 10 baud
Monitoring started. Any transition faster than ~1.25× the target baud will be logged.
Press Ctrl+C to stop.

[2026-03-29 23:05:11] GLITCH on pin 27 at ~71429 baud
[2026-03-29 23:05:11] GLITCH on pin 27 at ~45457 baud
[2026-03-29 23:05:12] GLITCH on pin 27 at ~40000 baud
[2026-03-29 23:05:12] GLITCH on pin 27 at ~71429 baud
[2026-03-29 23:05:12] GLITCH on pin 27 at ~50000 baud
[2026-03-29 23:05:12] GLITCH on pin 27 at ~47619 baud

Linux Rocks Every Day