0
sgjava

Using Python, CFFI and libgpiod for cross SBC GPIO access

Recommended Posts

On my journey to finding the best solution for cross SBC/cross language GPIO support I went through the same issues as Python by the C side author did it seems and landed on CFFI. This is faster than ctypes and seem more natural to program with. ctypesgen (the code generator) doen't really support Python 3, so I've hit a dead end there. It seems like JNA is still the best choice for Java bindings, so I'm sticking with that for now. In any event I threw together a button press app that uses CFFI and libgrpiod. I'll be updating my bindings project to support this soon.

 

libgpiod version 1.1.devel
Name: gpiochip1, label: 1f02c00.pinctrl, lines: 32
Press button within 5 seconds
Falling edge timestamp 02/10/2018 19:49:05

"""
Test blocking event using built in button
-------------
Should work on any board with a button built in. Just change chip and line
value as needed.
"""

import sys, time
from argparse import *
from cffi import FFI

parser = ArgumentParser()
parser.add_argument("--chip", help="GPIO chip number (default 1 '/dev/gpiochip1')", type=int, default=1)
parser.add_argument("--line", help="GPIO line number (default 3 button on NanoPi Duo)", type=int, default=3)
args = parser.parse_args()
ffi = FFI()
# Specify each C function, struct and constant you want a Python binding for
# Copy-n-paste with minor edits
ffi.cdef("""
enum {
    GPIOD_LINE_EVENT_RISING_EDGE,
    GPIOD_LINE_EVENT_FALLING_EDGE,
};

struct timespec {
    long tv_sec;
    long tv_nsec;
};

struct gpiod_line {
    unsigned int offset;
    int direction;
    int active_state;
    bool used;
    bool open_source;
    bool open_drain;
    int state;
    bool up_to_date;
    struct gpiod_chip *chip;
    int fd;
    char name[32];
    char consumer[32];
};

struct gpiod_chip {
    struct gpiod_line **lines;
    unsigned int num_lines;
    int fd;
    char name[32];
    char label[32];
};

struct gpiod_line_event {
    struct timespec ts;
    int event_type;
};

const char *gpiod_version_string(void);
struct gpiod_chip *gpiod_chip_open_by_number(unsigned int num);
struct gpiod_line *gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset);
int gpiod_line_request_falling_edge_events(struct gpiod_line *line, const char *consumer);
int gpiod_line_event_wait(struct gpiod_line *line, const struct timespec *timeout);
int gpiod_line_event_read(struct gpiod_line *line, struct gpiod_line_event *event);
void gpiod_line_release(struct gpiod_line *line);
void gpiod_chip_close(struct gpiod_chip *chip);
""")
lib = ffi.dlopen("libgpiod.so")
print "libgpiod version %s" % ffi.string(lib.gpiod_version_string())
gpiod_chip = lib.gpiod_chip_open_by_number(args.chip)
# Make sure GPIO chip opened
if gpiod_chip != ffi.NULL:
    print("Name: %s, label: %s, lines: %d" % (ffi.string(gpiod_chip.name), ffi.string(gpiod_chip.label), gpiod_chip.num_lines))
    gpiod_line = lib.gpiod_chip_get_line(gpiod_chip, args.line)
    # Verify we have line
    if gpiod_line != ffi.NULL:
        consumer = sys.argv[0][:-3]
        if lib.gpiod_line_request_falling_edge_events(gpiod_line, consumer) == 0:
            timespec = ffi.new("struct timespec*")
            timespec.tv_sec = 5
            print("Press button within 5 seconds")
            rc = lib.gpiod_line_event_wait(gpiod_line, timespec)
            if rc == 0:
                print("Timed out")
            elif rc == 1:
                event = ffi.new("struct gpiod_line_event*")
                # Read event off queue
                lib.gpiod_line_event_read(gpiod_line, event)
                if event.event_type == lib.GPIOD_LINE_EVENT_RISING_EDGE:
                    print("Rising edge timestamp %s" % time.strftime('%m/%d/%Y %H:%M:%S', time.localtime(event.ts.tv_sec)))
                else:
                    print("Falling edge timestamp %s" % time.strftime('%m/%d/%Y %H:%M:%S', time.localtime(event.ts.tv_sec)))
            else:
                print("Unable request falling edge for line %d" % args.line)            
        lib.gpiod_line_release(gpiod_line)
    else:
        print("Unable to get line %d" % args.line)
    lib.gpiod_chip_close(gpiod_chip)    
else:
    print("Unable to open chip %d" % args.chip)

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
0