GPIO the Hard Way

I struggled with the title for this one as I figured if I put /sys/class/anything in the title – everyone would run away as I usually do.

This entry came about because of my utter annoyance that so many little Linux SBCs out there come with ZERO or very little support for GPIO. Well, all of that is changing…

Let’s take as a working example the NEO2 from FriendlyArm (and I’m not speaking out of turn – they know my views about not supporting this stuff).  Lovely little board – and today I was playing with my extremely un-elegant power supply backup solution (see previous blogs) and realised I had no idea how to access the ports.  It looks great in the advert:

  • GPIO1: 2.54mm pitch 24 pin-header, compatible with Raspberry Pi’s GPIO pin1 – pin 24. It includes UART, SPI, I2C, IO etc
  • GPIO2: 2.54mm pitch 12 pin-header. It includes USB, IR receiver, I2S, IO etc

Lovely – grab your favourite Raspberry Pi utility – I like GPIO – the command line utility – or maybe the nodes in Node-Red – and your off and playing with GPIO.

I’m joking of course – none of that stuff works on these other boards though one or two have “equivalents”!  Sometimes the manufacturer provides utilities. I remember one provided some C code – lovely – then told me it only works for ROOT users… you know – the user you are not supposed to be !!! I can only get ONE of the four serial ports to actually work in Node-Red even thought it reports they are there (not permissions – tried that).

Anyway, recently I sat down with a large glass of artificial strawberry drink and started reading.

There is a directory in most of these boards in Linux called /sys/class/gpio

So I went looking – sure enough the NEO2 had this – and a couple of files and 2 shortcuts to GPIO that meant absolutely nothing to me.

So here’s what I’ve learned – just by messing about mind you.

You have to expose the GPIO – so let’s take GPIO0 as a working example.

As root user:

echo 0 > /sys/class/gpio/export

This made a GPIO0 magically appear.

echo out > /sys/class/gpio/gpio0/direction

This made the GPIO an output

echo 1 > /sys/class/gpio/gpio0/value

This made the output go high.

Sounds simple – but which I/O – I made 2 variations of that last command – one with 1, one with 0 – and played them back and forth while sticking a LED on the various potential IO pins on the NEO2. Voila – a lot simpler than I was expecting it turns out that GPIO0 was PA6 on the NEO2.

Now I was getting excited – but here’s the thing – I needed to control this output from Node-Red – and my Node-Red is user PI and you can’t DO this command as user Pi. You could use SUDO but SUDO doesn’t work with echo.

pi@neo2:~$ echo 1 >  /sys/class/gpio/gpio0/value
-bash: /sys/class/gpio/gpio0/value: Permission denied

Arggghh. But simple? Use Sudo

pi@neo2:~$ sudo echo 1 >  /sys/class/gpio/gpio0/value
-bash: /sys/class/gpio/gpio0/value: Permission denied

Aaarrrrrggghhh.

So I spoke to my friend Antonio and he came up with TEE.

echo 1 | sudo tee /sys/class/gpio/gpio0/value

Aaaaaah. WORKS – so now I put this into an EXEC node in Node-Red and tested – WORKS.

Exec node[10]

As you can imagine the next step would be to waste hours trying to find out which ports you can use – but then I realised that at least in this case  – FriendlyArm supply the Linux GPIO names! I tried GPIO2 – bingo! Nothing is permanent – so a little code in Node-Red would need to check if a port had been used before – and set it up as an output first if not – that’s easy – a lookup table for meaningful names and a bit of substitution in a function would also be easy.

Could I get this to work with inputs? SURE – almost the same starting with “in” instead of “out” assuming you’ve already exposed gpio0…

echo in | sudo tee /sys/class/gpio/gpio0/direction

The next part was sheer guesswork

cat /sys/class/gpio/gpio0/value

Sure enough – this returned the value of that port – either 1 or 0. Would it work in Node-Red EXEC?

exec node[6]

YUP! Works a treat. So you could write something for this outside of Node-Red but the latter is a nice friendly environment – and the use of a global would let you check if the input or output was already in use – this is readily adapted to other gpio-software-challenged boards.

This is course is trivial – it does not answer how to use peripheral features such as I2c etc. but if you’ve been struggling just to get the very basic input and output – perhaps that part of your struggle is now over.

Someday soon I hope to have proper drivers for the ports on this and other devices – but then someday soon I hope to win the pools. Best not wait around too long for either.

Meanwhile I have my input and now I can set about detecting low power – and shutting the device down gracefully – just need to write a little code to set up  that input automatically.

So – first things first – here’s a setup I’ve made for the NEO2 – a simple text file called “pins.def” (for want of a better name) in the /home/pi/.node-red directory (for want of a better place).

{ “0”:”in”,”203″:”in”,”6″:”out”,”67″:”out”}

A simple piece of json defining the ports I’m interested in – and their direction.

I discovered that if I sent the two commands separately there was no guarantee that they would get there in the right order – so I joined them together… for example for GPIO0

echo 0 | sudo tee /sys/class/gpio/export; echo in | sudo tee /sys/class/gpio/gpio0/direction

And here is my test rig in Node-Red

Tesrts

One of those ports – GPIO6 is an output and is a good test…(pin12 on the dual connector on the NEO2).

Simple enough – on power up the injector node fires off anything to the yellow function node which sets up the ports by exporting them and setting their direction.

var fs = global.get(“fs”);
var fl=”/home/pi/.node-red/pins.def”;

var pins=JSON.parse(fs.readFileSync(fl).toString());
for (var port in pins) {
if (pins.hasOwnProperty(port)) {
msg.payload=port + ” | sudo tee /sys/class/gpio/export; “;
msg.payload+=”echo ” + pins[port] + ”  | sudo tee /sys/class/gpio/gpio” + port + “/direction”; node.send(msg);
}
}                   

Insert your own error stuff as desired.

That’s it – the tests simply fire off the right info as described above to the EXEC node – I’ve tested this from reboot – it works.

Now – remember the point of this – I wanted to be able to check a port to see if I should gracefully power down the Pi if the port was low? Well, I can check that every 10 seconds like this.

tests

Of COURSE I can make this more graceful. So every 10 seconds – I trigger the cat command on GPIO0 – RBE merely outputs only on change – probably not needed.. then a simple function returns ONLY if the output is HIGH…. and shuts down the NEO2.

Easy – you could adapt this to any setup.

And here is a new project where this is in use – https://tech.scargill.net/uninterruptible-supplies/

Facebooktwitterpinterestlinkedin

20 thoughts on “GPIO the Hard Way

  1. Thanks to the information here and https://github.com/friendlyarm/WiringNP/blob/master/gpio/gpio.c was able to get a relay programmatically working on a NanoPC-T2 with Qt C++ and Debian. This could apply to other uses and similar boards, such as LEDs and the NanoPi-2.

    I am using GPIOB31 or pin 15. So as John Czaia points out above, you must first get the table. It generated 32 for B (the ‘B’ is from GPIOB31), so add 31 (also from GPIOB31) = 63. This is the value that must be “exported” with something like: echo 63 > export (in /sys/class/gpio). Once that value is exported, another device will appear: gpio63.

    Also as pointed out above, once exported, it is volatile. In other words, gpio63 will disappear after a reboot (or an unexport, ie: echo 63 > unexport). Exporting (and unexporting) must be done as root so that becomes a problem because my application could not initialize or export the pin. So it has to been done at the system level on boot.

    So I created an executable script file named gpioexport in /etc/init.d. This may not be completely accurate but it works. The contents are:

    #!/bin/bash
    # /etc/init.d/gpioexport
    # initializes pin 15 or GPIOB31 on NanoPC-T2 for relay functionality
    # direction and dis/enable set by application
    # create symlink S99gpioexport in /etc/rc5.d with:
    # sudo ln -s /etc/init.d/gpioexport /etc/rc5.d/S99gpioexport
    # then add to run level with:
    # sudo update-rc.d gpioexport defaults

    ### BEGIN INIT INFO
    # Provides: gpioexport
    # Required-Start: $remote-fs $syslog
    # Required-Stop: $remote-fs $syslog
    # Default-Start: 5
    # Default-Stop:
    # Short-Description: un/exports gpio pin
    ### END INIT INFO

    case “$1” in
    start)
    echo 63 | tee /sys/class/gpio/export
    echo “GPIO exported”
    ;;
    stop)
    echo 63 | tee /sys/class/gpio/unexport
    echo “GPIO unexported”
    ;;
    *)
    echo “Usage: /etc/init.d/gpioexport {start|stop}”
    exit 1
    ;;
    esac
    exit 0

    Notice the trick using “tee”, which I first learned about here, then discovered more information why a simple “echo 63 > export” will not work. Also, once the update-rc.d is performed, the system will assign its own value from the S99.

    So the last things to do are open the exposed port, set the direction, “in” or “out”, and then value “0” (off) or “1” (on) in the application with something like:

    if ((fd = fopen(“/sys/class/gpio/gpio63/direction”, “w”)) != NULL)
    fprintf(fd, “out”); // set direction out
    if ((fd = fopen(“/sys/class/gpio/gpio63/value”, “w”)) != NULL)
    fprintf(fd, “1”); // enable

    Of course, there may be some disagreeable programming practices in that but they are not the focus of this discussion.

    There are other options available in gpio63 but direction and value are all I needed for my purposes.

    1. good 🙂
      so, to check which devices are created, IF they are…
      grep gpio /proc/devices
      then do this:
      ls -l /dev/WHAT_YOU_HAVE_FOUND_BEFORE
      these will be your gpio devices, and in case are root:root we can just create an ad hoc group or just have them have group “pi”

      1. Alight – as it is Sunday I’ll bite…

        The first command yields:

        “254 gpiochip”

        without the quotes. What does that mean exactly?

        pi@neo2:~/.node-red$ sudo ls -l /dev/gpiochip
        ls: cannot access ‘/dev/gpiochip’: No such file or directory

        1. I think you might have to try doing your echo magic first since you’ve already said that they are in-memory only (reset on reboot).

          All I did on the Pi was:

          ls -la /sys/class/gpio

          which shows you the user and group ownership as well as other details.

          1. Well, as the SUDO method now works so well and can be called from Node-Red I’m not too worried about reducing this to non-SUDO.

  2. Or in alphabetical order to make life easier..

    adm
    audio
    avahi
    backup
    bin
    bluetooth
    cdrom
    colord
    crontab
    daemon
    dialout
    dip
    disk
    fa
    fax
    floppy
    games
    gnats
    input
    irc
    kmem
    list
    lp
    mail
    man
    messagebus
    mosquitto
    netdev
    news
    nogroup
    operator
    pi
    plugdev
    proxy
    root
    sambashare
    sasl
    scanner
    shadow
    src
    ssh
    ssl-cert
    staff
    sudo
    sys
    systemd-bus-proxy
    systemd-journal
    systemd-network
    systemd-resolve
    systemd-timesync
    tape
    tty
    users
    utmp
    uucp
    video
    voice
    winbindd_priv
    www-data

  3. Thanks for the feedback guys – in the case of the NEO2 – as mentioned elsewhere I found a version of WIRINGPI (hence GPIO) that works – but of course you have to figure out the pins by trial and error. More importantly however I think I may have convinced FriendyArm of the importance of releasing a version of WIRINGPI with a board rather than putting it out there with no info. If they follow up on this we’ll not have to do this experimenting in future and hopefully those programs will also allow easy access to serial and other facilities – which really, I don’t think is too much to expect. Fingers crossed.

    But to develop this – on the NEO2 there does not appear to be a group called GPIO. Root is a member of root and the total groups out there appear to be…

    root
    daemon
    bin
    sys
    adm
    tty
    disk
    lp
    mail
    news
    uucp
    man
    proxy
    kmem
    dialout
    fax
    voice
    cdrom
    floppy
    tape
    sudo
    audio
    dip
    www-data
    backup
    operator
    list
    irc
    src
    gnats
    shadow
    utmp
    video
    sasl
    plugdev
    staff
    games
    users
    nogroup
    input
    systemd-journal
    systemd-timesync
    systemd-network
    systemd-resolve
    systemd-bus-proxy
    pi
    netdev
    messagebus
    ssh
    bluetooth
    scanner
    colord
    fa
    ssl-cert
    avahi
    crontab
    sambashare
    winbindd_priv
    mosquitto

  4. Regarding permissions. A quick check on my Pi shows that ownership of those virtual files and folders belongs to root/gpio.

    Which means that you only need to add the user running Node-RED to the gpio group and you shouldn’t need any messing with sudo!

    Obviously, other SBC’s may have different ownership but hopefully they have something similar.

  5. Thanks for this, Pete!

    Very informative and I was finally able to use the GPIO pins on the NanoPi2 Fire for the shutdown button I have on all other Pis.

    Being a complete Linux noob even after reading your article, I still had a hard time understanding how the GPIO’s are addressed on the NanoPi2 Fire. I could not toggle anything.
    After quite some googling I found a forum entry (You posted there too) where someone haphazardly explained how this works (while still being partly wrong and making killer typos).

    Below table is essential when using echo commands to toggle/set/get GPIOs, sorted and added letters in front for easier reference:
    A nxp-gpio.0: 0
    B nxp-gpio.1: 32
    C nxp-gpio.2: 64
    D nxp-gpio.3: 96
    E nxp-gpio.4: 128
    F nxp-gpio.5: 160

    You can get this via:
    cd /sys/class/gpio
    for i in gpiochip* ; do echo `cat $i/label`: `cat $i/base` ; done

    Example: A GPIO pin labeled GPIOC8, one would expect this to be echo 8 > … but in reality it is: echo 72 > …
    So if the letter behind GPIO is C for instance, take the GPIO number and add (in this case) 64, and one has the correct GPIO number for the echo commands.

    Once I understood this, and it worked, everything else was much easier. After being able to use NodeRed to manipulate GPIOs I even managed to get the Matrix-Python code to run, which in contrast to this method uses PIN numbers instead of the odd GPIO math. Now boots with the system and with one button press I can shut down the NanoPi2 Fire.

    One thing I still don’t get is what does PA stand for? I sometimes see it that someone says: “This is on PA6.” And how is it counted, straight down, unlike the pin numbering on the data sheets, which go left right, or …?

  6. The observant reader may note that some posts have disappeared including a couple of my own. That is because I saw the beginnings of one of those endless “my language is better than yours” discussions which usually devolve into arguments.

  7. I set tabs to 4 spaces in vim, even for other languages then python.

    For the readability perspective, I don´t see any reason to keep braces, they are an annoyance to figure out when they start and when they end, plus you can make a oneliner and have unreadable code, which is a nightmare for human readers, not so much for machines.

    1. I completely agree on the point of python being odd and a time sink due to it’s design around whitespace / indentation significance. But in those cases where you absolutely must do something with it, my personal recommendation would be to use this: https://www.jetbrains.com/pycharm/ – it takes a lot of pain out of the mundane and what would be 100% obvious in most other languages. You can even line step debug with it – what a time to be alive!

      1. Refresh the blog entry – it is mutating – got all the GPIO working – now added a little cheap OLED display – which I have to say looks very nice. Will add links etc.

    2. If you mean curly braces – absolutely – and that would be made even better by removing case-sensitivity (I’ve probably just started WW3).

  8. technically, /sys (as /proc) is not just a directory but a “virtual filesystem”… as /dev is an interface to devices, both /sys and /proc are interfaces to the kernel itself and let you tune it or read some values… it’s virtual as it’s volatile, created on the fly during boot and destroyed on shutdown, so NOT persistent…

    1. Easy to add persistence via Node-Red and thanks for spotting the deliberate mistakes – hopefully now all gone. I have an input and an output working no problem. Knowledge is a wonderful thing.

Comments are closed.