Capture input events via GPIO on BeagleBone black

Beaglebone black gpio input from Elias Bakken on Vimeo.

If you want to listen to button presses or monitor switches on BeagleBone, the best way is not to poll the pins as some have suggested, but instead use the gpio_keys driver to generate an event when a switch changes state, either closes or opens. That way you do not spend any resources checking if something has happened and you also get an event the instance it happens. As an example, I’ll use the Replicape which has six end-stops one for each of the extremes of the three axes x1, x2, y1. y2, z1, z2. If an axis goes beyond it’s normal travel range for any reason, the system should pick this up so the physical equipment is not damaged. It can also be used for automatic calibration and centring of the print head.

To setup a system of six pins configured as inputs, try the following:
Start nano:

nano BB-GPIO-KEYS-00A0.dts

And then copy-paste this:

/dts-v1/;
/plugin/;

/ {
    compatible = "ti,beaglebone", "ti,beaglebone-black";

    /* identification */
    part-number = "BB-GPIO-KEYS";
    version = "00A0";

    fragment@0 {
        target = <&am33xx_pinmux>;
        __overlay__ {
            end_stop_pins: pinmux_end_stop_pins{
                pinctrl-single,pins = <
                    0x090 0x37 // P8_7  = End stop X1  = GPIO2_2
                    0x070 0x37 // P9_11 = End stop Y1  = GPIO0_30
                    0x074 0x37 // P9_13 = End stop Z1  = GPIO0_31
                    0x158 0x37 // P9_18 = End stop Z2  = GPIO0_4
                    0x1AC 0x37 // P9_25 = End stop Y2  = GPIO3_21
                    0x180 0x37 // P9_26 = End stop X2  = GPIO0_14
                >;
            };
        };
    };

    fragment@1 {
        target = <&ocp>;
        __overlay__ {            
            #address-cells = <1>;
            #size-cells = <1>;                            

            gpio_keys {
                compatible = "gpio-keys";
                pinctrl-names = "default";
                pinctrl-0 = <&end_stop_pins>;
                #address-cells = <1>;
                #size-cells = <0>;

                switch_x1 {
                    label = "End-stop-X1";
                    debounce_interval = <50>;
                    linux,code = <1>;
                    gpios = <&gpio3 2 0x5>;
                    gpio-key,wakeup;
                };                
                switch_x2 {
                    label = "End-stop-X2";
                    debounce_interval = <50>;
                    linux,code = <2>;
                    gpios = <&gpio1 14 0x5>;
                    gpio-key,wakeup;
                };
                switch_y1 {
                    label = "End-stop-Y1";
                    debounce_interval = <50>;
                    linux,code = <3>;
                    gpios = <&gpio1 30 0x5>;
                    gpio-key,wakeup;
                };                
                switch_y2 {
                    label = "End-stop-Y2";
                    debounce_interval = <50>;
                    linux,code = <4>;
                    gpios = <&gpio4 21 0x5>;
                    gpio-key,wakeup;
                };                
                switch_z1 {
                    label = "End-stop-Z1";
                    debounce_interval = <50>;
                    linux,code = <5>;
                    gpios = <&gpio1 31 0x5>;
                    gpio-key,wakeup;
                };                
                switch_z2 {
                    label = "End-stop-Z2";
                    debounce_interval = <50>;
                    linux,code = <6>;
                    gpios = <&gpio1 4 0x5>;
                    gpio-key,wakeup;
                };                
            };
        };
    };
};

Most of this should be familiar if you know about device tree overlays. The important thing is the gpio_keys node. Notice the “gpio-keys” compatible directive. This matches the compatible field in the kernel file “drivers/input/keyboard/gpiuo_keys.c” for those who need a point of reference.. For each switch you have, add a new node as seen above. Each switch or button needs a label, an optional debounce interval that will keep the switch from reporting multiple events due to bouncing. The default is 5 ms.

The linux code is the code that gets reported in the event.
The gpios is the bank and pin number. Look in the SRM to find out the right one for the pins you want. Again, there is a gottcha here since the bank number is one more than what’s in the SRM. Look in the above overlay and you will figure it out.
The last argument in the gpio-field of each button is the “flags”. I’ve not found any documentation for this, but is seems bit two is direction and bit one is pullup, so 0×4 + 0×1 = 0×5 is input, pullup. The “gpio-key, wakeup” I think is whether or not linux will go out of sleep mode if an event happens.

Compile it:

dtc -O dtb -o BB-GPIO-KEYS-00A0.dtbo -b 0 -@ BB-GPIO-KEYS-00A0.dts

Move it:

cp BB-GPIO-KEYS-00A0.dtbo /lib/firmware/

Enable it:

echo BB-GPIO-KEYS:00A0 > /sys/devices/bone_capemgr.*/slots

Look for errors:

dmesg

You should now have a new event device file in /dev/input/event1.

A simple way to check that it’s working is to cat the file:

cat /dev/input/event1

And then close the switch. If you see a bunch of digital garble, it’s working as expected :)

If you want a more elaborate scheme, here is a python script that should work:

nano switch_test.py

Input this stuff:

evt_file = open("/dev/input/event1", "rb")
while True:
    evt = evt_file.read(16) # Read the event
    evt_file.read(16)       # Discard the debounce event 
    code = ord(evt[10])
    direction  = "down" if ord(evt[12]) else "up"
    print "Switch "+str(code)+" "+direction

Start the program:

python switch_test.py

And the toggle the switch. You should see something like this:

root@beaglebone:~# python switch_test.py
Switch 2 down
Switch 2 up
Switch 2 down
Switch 2 up
Switch 2 down
Switch 2 up
Switch 2 down
Switch 2 up

For further reading, Derrek Molloy has a great (very extensive) video explaining how to do polled input and output: http://www.youtube.com/watch?v=wui_wU1AeQc

11 thoughts on “Capture input events via GPIO on BeagleBone black

  1. hej,
    thanks for that nice information mate!

    could you explain this part:
    pinctrl-single,pins = ;

    i think 0×37, which is 110111 in binary means: input, pullup, pull enabled, mux-mode 7 (which is gpio mode)
    see this table for explanation, i got it from here: http://stackoverflow.com/questions/12025119/beaglebone-gpio-input-not-working

    Bit 5: 1 – Input, 0 – Output
    Bit 4: 1 – Pull up, 0 – Pull down
    Bit 3: 1 – Pull disabled, 0 – Pull enabled
    Bit 2 \
    Bit 1 |- Mode
    Bit 0 /

    but i dont understand, what this means:

    0×090, 0×070, 0×074, …. this does describe the pinnumber somehow, but i can’t understand in which way, could you explain it please?!

    and i’m not sure about
    “gpios = ;” i think 0×5 stands for something else then pull- and input settings, cause we already did that before in the dts file like i just wrote ….

  2. hej, i found a list with the hex-names of the pinnumbers,
    now i reduced my gpio-input-keys to 2 and changed your section to this:

    fragment@0 {
    target = ;
    __overlay__ {
    end_stop_pins: pinmux_end_stop_pins{
    pinctrl-single,pins = ;
    };
    };
    };

    i also changed the keys to this:

    gpio_keys {
    compatible = “gpio-keys”;
    pinctrl-names = “default”;
    pinctrl-0 = ;
    #address-cells = ;
    #size-cells = ;

    switch_x1 {
    label = “End-stop-X1″;
    debounce_interval = ;
    linux,code = ;
    gpios = ;
    gpio-key,wakeup;
    };
    switch_x2 {
    label = “End-stop-X2″;
    debounce_interval = ;
    linux,code = ;
    gpios = ;
    gpio-key,wakeup;
    };
    };

    but after compiling, copying the firmware and loading it in the slots with echo,
    i can’t get the python-test results i got with your copy-paste-code, i rebooted to
    discard the changes in the device tree i did before (btw: is there a command to remove “dtob files from the slots”?)
    here is what /sys/kernel/debug/gpio shows:

    GPIOs 0-31, gpio:
    gpio-15 (End-stop-X1 ) in hi

    GPIOs 32-63, gpio:
    gpio-33 (End-stop-X2 ) in lo
    gpio-52 (eMMC_RSTn ) out lo
    gpio-53 (beaglebone:green:usr) out lo
    gpio-54 (beaglebone:green:usr) out lo
    gpio-55 (beaglebone:green:usr) out hi
    gpio-56 (beaglebone:green:usr) out lo
    gpio-59 (McASP Clock Enable P) out hi

    what do i also have to change/ did i do wrong,
    thanks in advance!

  3. for some reason i posted crap the last 2 times, the important points in code where just empty,
    maybe i get a reply with this.
    i don’t get any output neither on the event0-device nor by your python script
    when i ground pin 8_15, which is gpio1[15] – ANY IDEAS WHY ?!
    heres my device tree source (which i compiled, put in /lib/firmware and loaded with echoing in the slots)
    dmesg show no errors

    /dts-v1/;
    /plugin/;

    / {
    compatible = “ti,beaglebone”, “ti,beaglebone-black”;

    /* identification */
    part-number = “BB-GPIO-KEYS”;
    version = “00A0″;

    fragment@0 {
    target = ;
    __overlay__ {
    end_stop_pins: pinmux_end_stop_pins{
    pinctrl-single,pins = ;
    };
    };
    };

    fragment@1 {
    target = ;
    __overlay__ {
    #address-cells = ;
    #size-cells = ;

    gpio_keys {
    compatible = “gpio-keys”;
    pinctrl-names = “default”;
    pinctrl-0 = ;
    #address-cells = ;
    #size-cells = ;

    switch_x1 {
    label = “End-stop-X1″;
    debounce_interval = ;
    linux,code = ;
    gpios = ;
    gpio-key,wakeup;
    };
    };
    };
    };
    };

    and here is the output of /sys/kernel/debug/gpio

    GPIOs 0-31, gpio:
    gpio-15 (End-stop-X1 ) in hi

    GPIOs 32-63, gpio:
    gpio-52 (eMMC_RSTn ) out lo
    gpio-53 (beaglebone:green:usr) out lo
    gpio-54 (beaglebone:green:usr) out lo
    gpio-55 (beaglebone:green:usr) out hi
    gpio-56 (beaglebone:green:usr) out lo
    gpio-59 (McASP Clock Enable P) out hi

    GPIOs 64-95, gpio:

    GPIOs 96-127, gpio:

    it’s strange, because gpio1-15 has gpio number (1×32+15=)47 and should therefore not be listed under gpios 0-32, please help me, i gotta get this going, same thing for gpio1-18

    • i found out by catting pinmux-settings with cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pins,
      that i really didn’t change muxmode on p8_15, which is gpio1[15] or gpio #47,
      its still 0×8, which is pull disabled, mode 0

      so i tested the switch without changing my loaded dtob on pin no. 15, which is P9_24
      and voila, i just configured the wrong pin,
      but how do i config p8_15 then?

      • On the line with pinctrl, P8_15 should be 0x3C 0×37.
        On the line with GPIO it should be GPIO2 15.
        Like I said, there is a caveat so the bank 1 becomes GPIO2 etc.

  4. Here is the test code for Python 3 for those testing on Non-Angstrom. Just have to remove the ord() call as the data is already structured as such. I also had to change the file to be event3 as my standard gentoo install already have the other two events allocated to something else.

    evt_file = open(“/dev/input/event1″, “rb”)
    while True:
    evt = evt_file.read(16) # Read the event
    evt_file.read(16) # Discard the debounce event.
    code = evt[10]
    direction = “down” if evt[12] else “up”
    print (“Switch “,str(code),” “,direction)

  5. Thank you for this useful example.
    I have also been struggling to find documentation on the “gpios = ” elements. While X and Y are straightforward, the Z parameter is not documented anywhere. The corresponding enum can be found in /include/linux/of_gpio.h in the kernel source:

    enum of_gpio_flags {
    OF_GPIO_ACTIVE_LOW = 0x1,
    };

    So there is only one bit. I have verified that this inverts the way key states are reported; when it is set, pulling the pin low presses the key, otherwise it releases it. The setting does not affect the internal pullups/downs in any way.

    By the way I have found that evtest is very useful for decoding keyboard events, it turns the “digital garble” into human-readable text. In Ubuntu you can simply apt-get install evtest to get it.

    • Sorry, the second sentence got altered when posting. It should read:
      I have also been struggling to find documentation on the “gpios = ” elements.

        • Haha, yeah I get it! It seems the way this has been solved for the gpio-of-helper is by using keywords such as
          output;
          init-high;
          etc.
          I guess for a key, there is no need for any more options than active high/low.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>