Wednesday, 22 July 2015

Raspberry Pi Flavoured Shenanigans

The raspberry Pi folks make so much mention of Python that you might think support for other languages might be wanting.

I certainly had difficulty persuading Perl to control the GPIO pins.  I tried using the Perl bindings to WiringPi, but it seems that the underlying WiringPi library has changed since the Perl wrapper stuff was written, and now it either doesn't work, or segfaults.  The actual C library is fine, as I proved to myself during my testing phase; but I'm always a little bit wary of stuff going on fire when I try to program it in C.

I might redo it myself, if nobody else does, but I really wanted to get my Raspberry Pi up and running as quickly as possible.

Fortunately, there's a trick we can use:  Inline::C.  This is a Perl module that does some high-level magic and lets you call functions in a C library as though they were Perl functions.

Here's my version of a "Knight Rider" display using eight LEDs:

#!/usr/bin/perl -w
use strict;

package JKLMwiringPi;
use strict;

use Inline C => Config =>
    ENABLE => AUTOWRAP =>
    LIBS => "-lwiringPi ";

use Inline C => <<"--END-C--";
    int  wiringPiSetup() ;
    void pinMode(int pin, int mode) ;
    int  digitalRead(int pin) ;
    void digitalWrite(int pin, int value) ;
    void delay(unsigned int howLong);
--END-C--

1;

package Main;
use strict;
$| = 1;

my ($pin, $state, $wait, $array_ref, @array, $button);

#  Each element is an array reference constructor:
#  [ pin to affect, state to set it to, milliseconds to wait ]
my @seq = ([0, 1, 500], [1, 1, 500], [2, 1, 0], [0, 0, 500], [3, 1, 0], [1, 0, 500],
           [4, 1, 0], [2, 0, 500], [5, 1, 0], [3, 0, 500], [6, 1, 0], [4, 0, 500],
           [7, 1, 0], [5, 0, 500], [6, 0, 500], [7, 0, 1500],
           [7, 1, 500], [6, 1, 500], [5, 1, 0], [7, 0, 500], [4, 1, 0], [6, 0, 500],
           [3, 1, 0], [5, 0, 500], [2, 1, 0], [4, 0, 500], [1, 1, 0], [3, 0, 500],
           [0, 1, 0], [2, 0, 500], [1, 0, 500], [0, 0, 1500]);

#  Initialise GPIO
my $success = JKLMwiringPi::wiringPiSetup;
die "Something wrong with JKLMwiringPi::wiringPiSetup"          if $success < 0;

#  Set ports 0 - 7 as outputs
foreach $pin(0 .. 7) {
    JKLMwiringPi::pinMode $pin, 1;
};

#  Set port 8 as an input, note this channel has a pull-up resistor
JKLMwiringPi::pinMode 8, 0;

#  Main loop:  Keep going around as long as button is unpressed  (due to wiring,
#  button shows 0 when pressed)

while ($button = (JKLMwiringPi::digitalRead 8)) {
    #  Circulate the array @seq by shifting an item from the beginning and pushing
    #  the same value back onto the end
    push @seq, $array_ref = shift @seq;
    #  The value is an array reference; let's fetch it and open it up
    ($pin, $state, $wait) = @{$array_ref};
    print "Turning pin $pin ";
    print $state ? "on" : "off";
    
    JKLMwiringPi::digitalWrite $pin, $state;
    JKLMwiringPi::delay $wait * .15;

    print "\n";
};

#  Tidy up after ourselves, leaving all outputs off

foreach $pin(0 .. 7) {
    JKLMwiringPi::digitalWrite $pin, 0;
};

exit 0; 

And here's a video of it in action:

We create a package called JKLMwiringPi. This defines functions wiringPiSetup(), pinMode(), digitalRead(), digitalWrite() and delay() which simply call the same-named C functions in the wiringPi library. Then we use pins 0 - 7 as outputs to drive LEDs, and pin 8 as an input to read a button which, when pushed, will turn off all the outputs and exit from the program.

Inside the main loop (which terminates when the value read from the switch is 0; the way it is wired means the switch reads 1 while not pressed), we keep shifting an item from the beginning of the array @seq and then pushing it back onto the end. What we shifted off is an array reference with three elements: a pin to write, the state to set it to and a delay. So we write the value to the pin and wait for the delay period (scaled appropriately).

This code is still a bit ugly, but we can improve that next time by creating some proper methods within the JKLMWiringPi package.

No comments:

Post a Comment