How the RPi::WiringPi distribution is tested

So I'm gearing up to write a "Howto Raspberry Pi with Perl" multi-chapter tutorial, and as I finalize a few last things, I thought I'd put together how all of the software is laid out and is continuously tested.

The RPi::WiringPi distribution is essentially a class that provides access to external sub distributions, and provides several benefits such as maintaining a registry of in-use GPIO pins, and ensuring your Pi is cleaned up back to default on exit, or if an error or signal is caught. The sub modules do none of those things.

I use the automation and dispatching capabilities of my Test::BrewBuild software to handle the test management. This software runs your unit tests on any/all Perlbrew and/or Berrybrew installed instances, with the ability to dispatch your test requests over the network to remote machines. I'm not getting into those details, just know I use the bbtester binary to listen for test requests, and the bbdispatch binary to send them, both on the same Raspberry Pi hardware.

So, I have my base hardware platform layout (full image here, real-world pic here):

RPi::WiringPi test platform

I'll explain a couple of things. First, there are no "sensors" or the like being tested. I test the core aspects that do sensor work.

I'll start from top-left to top-right, circle down to bottom-right, then go back left. Top-right is an Arduino Uno. It's sole purpose is to test the I2C communication functionality, which is provided by the RPi::I2C distribution. There's a basic hardware-emulation Arduino sketch that presents a hardware-like situation, to allow testing of base I2C functions.

Next we have the Pi. I don't have the pins connected exactly like this, but this was the easiest method to portray it. Below to the bottom right, there's a 74HC595 shift register, which effectively turns three GPIO pins into eight, and can be cascaded four times, for a total of 32 extra GPIO using only three physical GPIO on the board. I have only one here for testing.

To the left of that is an MCP4922 Digital-to-Analog converter (DAC), which takes a single digital input, and translates that into two analog outputs. RPi::WiringPi provides the RPi::MCP4922 object for this.

Then we've got an MCP3008 Analog-to-Digital converter (ADC), which does the opposite of the DAC, it provides eight analog inputs that can be read through a single digital channel. RPi::WiringPi handles this via the RPi::ADC::MCP3008 distribution.

Then, next to that, the blue "breakout" is another ADC, the ADS1115 by Adafruit, which has four analog inputs (compared to the MCP3008 above which has eight). This is an I2C device, so it provides me further testing in that manner, and its also used for reading Pulse Width Modulation (PWM) testing from the Pi itself, and for the servo() method built into RPi::WiringPi. It's fronted by the RPi::ADC::ADS distribution.

To the breadboard left of the ADS1115 is a two row, 16 column LCD, managed by RPi::LCD. This distribution can handle both the 16x2 and 20x4 models. The purpose of this hardware is to test the output, but also displays overall test output provided by the Test::BrewBuild distribution, as it has a small hack in it specifically to do this not just for the RPi::WiringPi dist, but any distribution you send to the Test::BrewBuild tester.

Now, to how things are tested. First, I start the tester in auto mode:

bbtester start -a

That puts the test listener in the background as a service (although this is Unix, it also works on Windows), then I bbdispatch an automated test run to that tester, specifying the rpi-wiringpi Github repository in the most simplistic of ways (putting this in crontab is the way to go). I've got a single Perl instance installed, and the result line will repeat continuously when run from the CLI (default is wait 60 seconds between run checks). In the bbtester start line above, it will check Github to see if any new commits have been pushed, and only run the tests if there's been an update:

bbdispatch -t localhost -r stevieb9/rpi-wiringpi -a
5.24.1 :: PASS

...install a couple more perls on the Pi:

brewbuild -i 5.26.0,5.16.3

Stop the dispatcher, and restart it:

bbdispatch -t localhost -r stevieb9/rpi-wiringpi -a

5.16.3 :: PASS
5.24.1 :: PASS
5.26.0 :: PASS

The Test::BrewBuild software is much more capable than these simple examples, but I digress. What I will say that running bbdispatch with the --rpi flag allows you to test any distribution (that's available on Github), and have your output displayed on an LCD screen (including date/time, PASS/FAIL, run count as well as the commit sum).

I run this setup 24 hours a day, seven days a week, and against eight versions of Perl, the Pi hardware test platform can do about 16-20 passes an hour (in a mode that tests regardless if new commits have been pushed).

Both the bbtester and bbdispatch software have extensive logging, and both can be run in "foreground" mode, particularly for troubleshooting. Here's a full-blown example of the dispatcher output, and the tester output.

I have not enabled debug levels for the actual brewbuild command which is the command that actually does the testing (called from within bbtester), as this output will be long enough for this example. In this run, I'm using the --csum flag to bbtester, so it tests on each pass, even if a new commit hasn't been pushed.

The bbdispatch debug output:

bbdispatch -t localhost -r stevieb9/rpi-wiringpi -a -d 7 --rpi

[2017-08-17 18:29:44.231][lvl 5][Dispatch.new] instantiating new object
[2017-08-17 18:29:44.232][lvl 5][Dispatch.auto] Commencing auto run dispatch sequence
[2017-08-17 18:29:44.233][lvl 7][Dispatch.auto] continuous integration mode enabled
[2017-08-17 18:29:44.233][lvl 6][Dispatch.auto] COMMENCING RUN: 1

[2017-08-17 18:29:44.233][lvl 7][Dispatch.dispatch] working on testers: localhost
[2017-08-17 18:29:44.234][lvl 5][Dispatch.dispatch] configured localhost with port 7800
[2017-08-17 18:29:44.235][lvl 7][Dispatch._fork] spinning up tester: localhost
[2017-08-17 18:29:44.241][lvl 7][Dispatch._fork.localhost] tester localhost socket created ok
[2017-08-17 18:29:44.242][lvl 7][Dispatch._fork.localhost] syn "localhost" sent
[2017-08-17 18:29:44.243][lvl 7][Dispatch._fork.localhost] ack "localhost" received
[2017-08-17 18:29:44.243][lvl 7][Dispatch._fork.localhost] sent command: brewbuild
[2017-08-17 18:29:44.244][lvl 7][Dispatch._fork.localhost] received "ok"
[2017-08-17 18:29:44.245][lvl 5][Dispatch._fork.localhost] repo was sent in, and set to: https://github.com/stevieb9/rpi-wiringpi
[2017-08-17 18:29:44.245][lvl 6][Dispatch._fork.localhost] dispatching out to and waiting for tester: 'localhost'...
[2017-08-17 18:30:28.997][lvl 7][Dispatch._fork.localhost] tester work has concluded
[2017-08-17 18:30:29.288][lvl 5][Dispatch._fork] tester: localhost finished
[2017-08-17 18:30:29.290][lvl 7][Dispatch.dispatch] returning results if available...
5.24.1 :: PASS
[2017-08-17 18:30:29.290][lvl 5][Dispatch.auto] auto run status: PASS
[2017-08-17 18:30:29.291][lvl 7][Dispatch.auto] RPi LCD test result output enabled
[2017-08-17 18:30:30.211][lvl 6][Dispatch.auto] auto run complete. Sleeping for 60 seconds, then restarting if more runs required

Here's the bbtester output:

bbtester --fg --stdout -a -c -d 7

[2017-08-17 18:29:34.312][lvl 7][Tester] logging to STDOUT
[2017-08-17 18:29:34.313][lvl 5][Tester.new] instantiating new Test::BrewBuild::Tester object
[2017-08-17 18:29:34.313][lvl 7][Tester.new] args:
[2017-08-17 18:29:34.313][lvl 7][Tester.new] 
auto: 1
stdout: 1
debug: 7
csum: 1

[2017-08-17 18:29:34.315][lvl 6][Tester.listen] successfully created network socket on IP 0.0.0.0 and port 7800
[2017-08-17 18:29:34.316][lvl 7][Tester.listen] 0.0.0.0 now accepting incoming connections
[2017-08-17 18:29:34.316][lvl 7][Tester.listen] work dir is: /home/pi/brewbuild
[2017-08-17 18:29:34.317][lvl 7][Tester.listen] chdir to work dir: /home/pi/brewbuild
[2017-08-17 18:29:34.317][lvl 7][Tester.listen] TESTER: 0.0.0.0 PLATFORM: armv7l-linux
[2017-08-17 18:29:34.317][lvl 7][Tester.listen] waiting for a connection...

[2017-08-17 18:29:44.242][lvl 7][Tester.listen] received ack: localhost
[2017-08-17 18:29:44.243][lvl 7][Tester.listen] returned ack: localhost
[2017-08-17 18:29:44.243][lvl 7][Tester.listen] received cmd: brewbuild
[2017-08-17 18:29:44.244][lvl 7][Tester.listen] sending 'ok'
[2017-08-17 18:29:44.246][lvl 7][Tester.listen] received repo: https://github.com/stevieb9/rpi-wiringpi
[2017-08-17 18:29:44.246][lvl 5][Git.new] instantiating new object
[2017-08-17 18:29:44.247][lvl 6][Git.git] git command set to 'git'
[2017-08-17 18:29:44.247][lvl 7][Tester.listen] using Git: git
[2017-08-17 18:29:44.247][lvl 7][Tester.listen] before all checks, repo set to https://github.com/stevieb9/rpi-wiringpi
[2017-08-17 18:29:44.248][lvl 6][Git.name] converting repository link to repo name
[2017-08-17 18:29:44.248][lvl 6][Git.name] repo link converted to rpi-wiringpi
[2017-08-17 18:29:44.248][lvl 7][Tester.listen] chdir to: /home/pi/brewbuild/rpi-wiringpi
[2017-08-17 18:29:44.249][lvl 7][Tester.listen] repo rpi-wiringpi exists
[2017-08-17 18:29:44.249][lvl 6][Tester.listen] in auto mode
[2017-08-17 18:29:44.259][lvl 6][Git.link] found https://github.com/stevieb9/rpi-wiringpi for the repo
[2017-08-17 18:29:44.260][lvl 7][Git.status] checking git status
[2017-08-17 18:29:44.280][lvl 6][Git.link] found https://github.com/stevieb9/rpi-wiringpi for the repo
[2017-08-17 18:29:44.281][lvl 6][Git.revision.revision] initiating git revision
[2017-08-17 18:29:44.281][lvl 6][Git.revision] local: 'rev-parse HEAD' sent
[2017-08-17 18:29:44.291][lvl 5][Git.revision] commit checksum: b21c66e27e3972eacee1acb9a71def478c66b869
[2017-08-17 18:29:44.301][lvl 6][Git.link] found https://github.com/stevieb9/rpi-wiringpi for the repo
[2017-08-17 18:29:44.301][lvl 6][Git.revision.revision] initiating git revision
[2017-08-17 18:29:44.301][lvl 6][Git.revision] remote: 'ls-remote https://github.com/stevieb9/rpi-wiringpi' sent
[2017-08-17 18:29:44.946][lvl 5][Git.revision] commit checksum: b21c66e27e3972eacee1acb9a71def478c66b869
[2017-08-17 18:29:44.946][lvl 7][Tester.listen] 
Git check:
    status: 1
    local:  b21c66e27e3972eacee1acb9a71def478c66b869
    remote: b21c66e27e3972eacee1acb9a71def478c66b869
[2017-08-17 18:29:44.947][lvl 7][Tester.listen] commit checksums are equal; no need to pull
[2017-08-17 18:29:44.949][lvl 5][Tester.listen] COMMENCING TEST RUN; no args (default)
[2017-08-17 18:29:44.954][lvl 7][Tester.listen] executing test()
[2017-08-17 18:30:28.994][lvl 7][Tester.listen] chdir to: /home/pi/brewbuild/rpi-wiringpi/bblog
[2017-08-17 18:30:28.994][lvl 7][Tester.listen] no log files generated, nothing to process
[2017-08-17 18:30:28.995][lvl 7][Tester.listen] chdir to: /home/pi/brewbuild/rpi-wiringpi
[2017-08-17 18:30:28.995][lvl 7][Tester.listen] removing log dir: /home/pi/brewbuild/rpi-wiringpi/bblog
[2017-08-17 18:30:28.996][lvl 5][Tester.listen] storing and sending results back to dispatcher
[2017-08-17 18:30:28.999][lvl 7][Tester.listen] work dir is: /home/pi/brewbuild
[2017-08-17 18:30:28.999][lvl 7][Tester.listen] chdir to work dir: /home/pi/brewbuild
[2017-08-17 18:30:29.000][lvl 7][Tester.listen] TESTER: 0.0.0.0 PLATFORM: armv7l-linux
[2017-08-17 18:30:29.001][lvl 7][Tester.listen] waiting for a connection...

Of course, normally output isn't displayed in this regard, but that's a general overview of how my RPi::WiringPi collection of distributions is tested all day, every day, on my custom CI test platform.

Here's the full list of RPI:: related distributions:

  • RPi::ADC::ADS provides access to the ADS1xxx family of analog to digital converters
  • RPi::ADC::MCP3008 provides access to MCP300x series analog to digital converters
  • RPi::BMP180 easy reading of the x80 series barometric pressure sensors
  • RPi::Const standard RPi and other typical constant names
  • RPi::DAC::MCP4922 access to the MCP4xxx series digital to analog converters
  • RPi::DHT11 provides sampled and converted results from DHTxx hygrometer/temperature sensors
  • RPi::MCP4xxxx provides control over the MCP4xxxx series digital potentiometers
  • RPi::HCRS04 read distances with this ultrasonic distance sensor
  • RPi::I2C access devices through I2C
  • RPi::LCD control 16x2 or 20x4 LCD screens
  • RPi::Pin complete management of GPIO pins
  • RPi::Serial read/write to/from the serial interface
  • RPi::SPI comms with devices on the Serial Peripheral Interface

Here's a pic of what the hardware looks like. I was ready to get boards printed, but had to back out at the last minute, so it's still on a breadboard for now.

berrybrew, the Perlbrew for Windows, updated to v1.15

I've released berrybrew version 1.15.

All of the credit for this update goes to PETERCJ who went far above and beyond with this PR. It's a pleasure to have him contributing to this (or any) project.

The significant change here is the addition of the use command. I feel that this addition adds tremendous value to the software, and makes it much easier to use.

This allows you to temporarily switch to one of your other installed Perl instances either in the same command window or new ones without switching your currently switched to one. Here are some examples:

Temporarily use a different installed version, in the same window. Typing exit will return you to your previous environment within the same window:

c:\>berrybrew use 5.10.1_32
Perl-5.10.1_32
==============
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.


<berrybrew use 5.10.1_32>: run "exit" leave this environment
c:\>

Calling exit returns you to your previous environment within the same CLI window.

You can also use multiple versions within the same window. Using this method will cascade the instances:

c:\>berrybrew use 5.10.1_32,5.8.9_32
Perl-5.10.1_32
==============
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.


<berrybrew use 5.10.1_32>: run "exit" leave this environment
c:\>exit

Exiting <berrybrew use 5.10.1_32>

Perl-5.8.9_32
==============
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.


<berrybrew use 5.8.9_32>: run "exit" leave this environment
c:\>exit

Exiting <berrybrew use 5.8.9_32>


c:\>

Let's actually pop up a couple of new windows instead of using the one you're currently using:

c:\>berrybrew use --win 5.8.9_32,5.10.1_32
berrybrew use 5.8.9_32: spawned in new command window
berrybrew use 5.10.1_32: spawned in new command window

c:\>

# first new window

<berrybrew use Perl-5.8.9_32>
c:\>

# second new window

<berrybrew use Perl-5.10.1_32>
c:\>

I can't exactly show you the output without a screenshot, but I'm sure you get the gist. You can continue working in your main window, but execute things in the other two who have the respective version of Perl loaded and ready to go.

Because I took over maintainership of David Farrell's berrybrew repository when I first took over the project, it has been brought up to v1.15 as well.

Peter, I can't thank you enough for all of the hard work you've put into this. I mean, not just the new feature, but cleaning up erroneous whitespace, completely enhancing the whole test framework, updating the docs and meticulously adding tests while fully and completely explaining everything clearly. Cheers!

Reading/writing Arduino pins over I2C with Perl

Today, loosely inspired by this thread over on Perlmonks, I'm going to show how to set up an Arduino (Uno in this test case) with a pseudo-register that allows toggling one if its digital pins on and off, and another pseudo-register to read an analog pin that the digital pin is connected to, over I2C. Because it's digital to analog, the only possible values of the analog read will be 0 (off) or 1023 (full on, ie. 5v). This is an exceptionally basic example, but with some thought, one can imagine the possibilities (read/write EEPROM, set PWM etc etc).

We'll then use RPi::I2C to toggle the digital pin and read the analog pin over the I2C bus. Note I'm not using the encompassing RPi::WiringPi distribution in this case. The benefit to using that is to clean up Raspberry Pi's GPIO pins, which we aren't using any. In fact, any Linux device with I2C can be used for this example, I just so happen to be using one of my Pi 3 boards.

First, the simple Arduino sketch. All I2C devices require putting themselves on the "wire" with a unique address. I'm using 0x04, which is how the Pi will identify the Arduino on the I2C bus.

#include <Wire.h>

// Arduino I2C address

#define SLAVE_ADDR  0x04

// pseudo register addresses

#define READ_A0     0x05
#define WRITE_D2    0x0A

uint8_t reg = 0;

void read_analog_pin (){

    switch (reg){
        case READ_A0: {
            __read_analog(A0);
            break;
        }
    }
}

void __read_analog (int pin){

    int val = analogRead(pin);

    uint8_t buf[2];

    // reverse endian so we're little endian going out

    buf[0] = (val >> 8) & 0xFF;
    buf[1] = val & 0xFF;

    Wire.write(buf, 2);
}

void write_digital_pin (int num_bytes){

    reg = Wire.read(); // global register addr

    while(Wire.available()){

        uint8_t state = Wire.read();

        switch (reg){
            case WRITE_D2: {
                digitalWrite(2, state);
                break;
            }
        }
    }
}

void setup(){
    Wire.begin(SLAVE_ADDR);

    // set up the I2C callbacks

    Wire.onReceive(write_digital_pin);
    Wire.onRequest(read_analog_pin);

    // set up the pins

    pinMode(2, OUTPUT);
    pinMode(A0, INPUT);
}

void loop(){
    delay(10000);
}

Now, I'll show a simple script that loops 10 times, toggling the digital pin then displaying the value from the analog pin. Arudino's Wire library sends data a byte at a time, so we have to do some bit manipulation to turn the two bytes returned in the read_block() call back together into a single 16-bit integer (I wrote the merge() sub to handle this job).

use warnings;
use strict;

use RPi::Const qw(:all);
use RPi::I2C;

use constant {
    ARDUINO_ADDR    => 0x04,
    READ_REGISTER   => 0x05,
    WRITE_REGISTER  => 0x0A,
};

my $device = RPi::I2C->new(ARDUINO_ADDR);

for (0..9){
    my (@bytes_read, $value);

    $device->write_byte(HIGH, WRITE_REGISTER);

    @bytes_read = $device->read_block(2, READ_REGISTER);
    $value = merge(@bytes_read);

    print "$value\n"; # 1023

    $device->write_byte(LOW, WRITE_REGISTER);

    @bytes_read = $device->read_block(2, READ_REGISTER);
    $value = merge(@bytes_read);

    print "$value\n"; # 0
}

sub merge {
    return ($_[0] << 8) & 0xFF00 | ($_[1] & 0xFF);
}

Output:

1023
0
1023
0
1023
0
1023
0
1023
0
1023
0
1023
0
1023
0
1023
0
1023
0

I must acknowledge Slava Volkov (SVOLKOV) for the actual XS code. Most of the low-level hardware code I've been working on over the last year has been wrapping C/C++ libraries, a decent chunk of it has had me following datasheets to write my own, but in this case, I bit the whole XS file from Device::I2C and just presented a new Perl face to it so it fit in under the RPi::WiringPi umbrella. It just worked.

berrybrew, the Perlbrew for Windows, updated to v1.13

I've released berrybrew version 1.13.

The most notable change is the addition of the register command, which allows you to copy/move existing Strawberry Perl portable editions from your local/remote systems and have them operate as custom instances within berrybrew. Before registering a new instance:

> berrybrew available

The following Strawberry Perls are available:

    5.26.0_64
    5.26.0_64_PDL
    5.26.0_32
    5.24.1_64       [installed] *
    5.24.1_64_PDL
    5.24.1_32
    5.22.3_64
    5.22.3_64_PDL
    5.22.3_32
    5.20.3_64
    5.20.3_64_PDL
    5.20.3_32
    5.18.4_64
    5.18.4_32
    5.16.3_64
    5.16.3_32
    5.14.4_64
    5.14.4_32
    5.12.3_32
    5.10.1_32
    5.8.9_32

* Currently using

Now, copy your new instance into the instance directory (by default c:/berrybrew), then register it. I've used new_instance as the example instance directory in this case:

> berrybrew register new_instance

> berrybrew available

The following Strawberry Perls are available:

    5.26.0_64
    5.26.0_64_PDL
    5.26.0_32
    5.24.1_64       [installed] *
    5.24.1_64_PDL
    5.24.1_32
    5.22.3_64
    5.22.3_64_PDL
    5.22.3_32
    5.20.3_64
    5.20.3_64_PDL
    5.20.3_32
    5.18.4_64
    5.18.4_32
    5.16.3_64
    5.16.3_32
    5.14.4_64
    5.14.4_32
    5.12.3_32
    5.10.1_32
    5.8.9_32
    new_instance    [custom] [installed]

* Currently using

That instance can now be used under berrybrew.

Other notable changes:

  • pryrt/PETERCJ sent in a very large pull request that did a sweeping overhaul on the unit test framework which makes it much more flexible and reliable (thanks!)
  • added new exception handling for certain edge cases
  • documentation updates
  • closing of several minor issues

Here's the full list of available commands:

> berrybrew

berrybrew <command> [subcommand] [option]

available   List available Strawberry Perl versions and which are installed
clean *     Remove all temporary berrybrew files
clone       Make a complete copy of a Perl installation
config      Add berrybrew to your PATH
exec *      Run a command for every installed Strawberry Perl
fetch       Update the list of Strawberry Perl instances available
install     Download, extract and install a Strawberry Perl
off         Disable berrybrew perls (use 'switch' to re-enable)
register    Manually register a custom installation directory
remove      Uninstall a Strawberry Perl
switch      Switch to use a different Strawberry Perl
unconfig    Remove berrybrew from PATH
upgrade     Performs a safe upgrade. Requires Git installed
help        Display this help screen
license     Show berrybrew license
version     Displays the version


* - view subcommand details with 'berrybrew <command> help'

Because I took over maintanership, I will be updating dnmfarrell's berrybrew repo in the coming days to bring it up to speed.

Using a controllerless servo on the Raspberry Pi with Perl

I put out a notice not long ago that I was contemplating writing a guide to using the Raspberry PI with Perl.

One person who pointed out one minor mistake of mine with follow up with some other questions, asked about how to run a servo without needing a controller board. I realized that I hadn't exposed a couple of functions in the core WiringPi::API distribution that allowed a user to configure the PWM frequency, which is required as the Pi default doesn't play nice with typical servos.

The default PWM base frequency on a Pi is 19.2MHz, which is then divided by the clock signal (default: 32) and the PWM range (0-1023). So to get the default operating frequency:

# base   range   clck     operational freq
19.2e6 / 1024  /  32   ==     586Hz

To get this down to 50Hz required for a typical servo, I bumped up the range to 2000 (nice round number), and then just bounced around with the clock signal divider until I hit 50:

19.2e6 / 2000 / 192 == 50Hz

To be honest, I found the formula online, but then read through the datasheet for the Pi, and went on my way to not just copy and paste, but figure out exactly what frequency meant, what the divisors meant and then felt comfortable knowing exactly how PWM works ;)

So, for full left, the servo requires a pulse of 50Hz for ~1ms (PWM 50), centre is ~1.5ms (PWM 150) and full right is ~2.5ms (PWM 250). My servo required me to tweak these numbers a tiny bit to get the full 180 degree motion.

Anyway, to some code. I've commented the code as to what's happening and when, but an overall is that when started, the servo will go full-left, wait a sec, then swing from left-to-right, then back right-to-left until a SIGINT (CTRL-C) is caught, at which time, it puts the servo back to left position, then puts the pin back to INPUT mode so that if a different software is run after, the pin won't still be in PWM mode.

Unfortunately, at this time, we still require sudo for PWM functionality. It's being looked at. It's the only thing left that requires root.

use warnings;
use strict;

use RPi::WiringPi;
use RPi::WiringPi::Constant qw(:all);

die "need root!\n" if $> != 0;

use constant {
    LEFT    => 60,
    RIGHT   => 255,
    CENTRE  => 150,
    PIN     => 18,
    DIVISOR => 192,
    RANGE   => 2000,
    DELAY   => 0.001,
};

# set up a signal handler for CTRL-C

my $run = 1;
$SIG{INT} = sub {
    $run = 0;
};

# create the Pi object

my $pi = RPi::WiringPi->new;

# create a signal pin, set mode to PWM output

my $s = $pi->pin(PIN);
$s->mode(PWM_OUT);

# configure PWM to 50Hz for the servo

$pi->pwm_mode(PWM_MODE_MS);
$pi->pwm_clock(DIVISOR);
$pi->pwm_range(RANGE);

# set the servo to left max

$s->pwm(LEFT);

sleep 1;

while ($run){
    for (LEFT .. RIGHT){
        # sweep all the way left to right
        $s->pwm($_);
        select(undef, undef, undef, DELAY);
    }

    sleep 1;

    for (reverse LEFT .. RIGHT){
        # sweep all the way right to left
        $s->pwm($_);
        select(undef, undef, undef, DELAY);
    }

    sleep 1;
}

# set the pin back to INPUT

$s->pwm(LEFT);
$s->mode(INPUT);

There were three distributions that required updates for this to work, the last one (RPi::WiringPi) isn't yet up to the CPAN as I'm making slight other changes to it, but it can be found on Github. One of the updates will be the ability to get a servo "object" (ie my $servo = $pi->servo($pin_num);), that'll take care of all of the frequency stuff in the background, then put it all back to default state when your program exits.

Note that the Pi may struggle to power the servo and it may cause a low-voltage situation, so it's best you power your 5v servo from an alternate source (I just happen to have a few powered-up Arduino's nearby all with 5v pins accessible). Also note that even though the +/- of the servo is 5v, you can safely connect the signal pin on it to the 3.3v GPIO on the Pi as on the servo, the pin is input only (ie. it doesn't feed back to the Pi).