July 2017 Archives

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).

About Steve Bertrand

user-pic Just Another Perl Hacker