Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine

Cool Uses for Perl

( #1044=superdoc: print w/replies, xml ) Need Help??

This section is the place to post your general code offerings -- everything from one-liners to full-blown frameworks and apps.

Human-visible light levels - Adafruit Breakout board with I2C
1 direct reply — Read more / Contribute
by anita2R
on Feb 12, 2017 at 14:52

    AdaFruit sells a 'breakout' board for the dual-sensor TLS2561 Light-to-Digital Converter. The board provides i2c communicatiion and 3.3 or 5 volt operation.

    Adafruit provides software suitable for the Arduino and the sensor manufacturer provides some pseudo-code and code suitable for microprocessors. As a Raspberry Pi user I needed a Linux solution, so I have produced a short Perl script to obtain light levels in Lux, from the board.

    The TLS2561 uses 2 sensors, one of which only responds to infrared, so making it possible to get an approximation of human-visible light levels, by subtraction the infrared value from the infrared + visible value, (plus some mathematical manipulations).

    Before using the script, the user needs to replace <username> and <group> with their own username and group as the script must be called as root or using sudo, and once the i2c object is created the script falls back to the user's permissions.

    The script can be called 'as is' and default values will be used, or parameters can be passed which change the sensor sensitivity and the integration (sensing) time which affects the available range from the chip's two ADC's.

    The sensitivity -s parameter takes values of 0 or 1 (normal or x16 sensitivity).
    The integration -i parameter takes values of 0, 1 or 2 (13.7mS, 101mS or 402mS).
    If the script is called with the 'verbose' -v parameter on its own or with -s &/or -i, additional information is printed, including the raw sensor values.

    The math for doing the Lux calculations comes from the TLS2561 datasheet, which hopefully I have interpreted correctly! As I don't have a Lux meter, I can't be sure, but the results under different lighting conditions appear 'reasonable'.

    Script -

    Sample calls:
    Standard sensitivity and shortest sampling duration:
    sudo -s 0 -i 0
    x16 sensitivity and longest sampling duration:
    sudo -s 1 -i 2
    Use default or last applied settings and get some additional feedback
    sudo -v

    Example output with -v
    sudo -s 1 -i 1 -v

    Timing register (old): 00010010 Timing Register (new): 00010001 Delay: 115 mS Channel 0 raw value: 4514 Channel 1 raw value: 298 Saturation value: 37177 Channel 0 scaled value: 17944 Channel 1 scaled value: 1184 Ratio is: 0.066 ******************************** * Visible light level: 520 lux * ********************************

    The breakout board from AdaFruit also includes an interrupt pin, but I have not programmed for its use, and the pin does not need to be connected. Also the adjacent 3vo pin can be left disconnected - the supply goes to the Vin pin.

    Leaving the 'Addr' pin disconnected selects the default 0x39 device address on the i2c bus.

    Any suggestions for improvements in the code or the math would be welcome.

Github repo with plenty of module examples
2 direct replies — Read more / Contribute
by neilwatson
on Feb 10, 2017 at 09:37
english numbers to chinese numbers
3 direct replies — Read more / Contribute
by changlehu
on Feb 08, 2017 at 02:13
    # script_name: numbers_to_chinese
    # usage: perl 30806 
    # result: 三万零八百零六
    my %cn_numbers=(
    my $number=shift;
    my $cn_number;
    if ($number>=10000){
    	my $n=$number/10000;
    	$number=$number % 10000;
    if ($number>=1000){
    	my $n=$number/1000;
    	$number=$number % 1000;
    if ($number>=100){
    	my $n=$number/100;
    	$number=$number % 100;
    if ($number>=10){
    	my $n=$number/10;
    	$number=$number % 10;
    if ($number>0){
    if ($cn_number=~/万/ && $cn_number!~/千/){
    if ($cn_number=~/千/ && $cn_number!~/百/){
    if ($cn_number=~/百/ && $cn_number!~/十/){
    print "$cn_number\n";
Back up all of your Github repos
1 direct reply — Read more / Contribute
by stevieb
on Feb 03, 2017 at 14:34

    For my next adventure, I'm going to write a distribution that allows a user to easily back up all of their Github repos, issues, PRs, wikis etc, but since hearing about the near disaster at Gitlab, I thought I'd expedite writing this one.

    I've put together a quick script just so I could begin getting used to the various APIs I'd need, and am sharing it here in the event someone else finds it useful.

    Currently, it'll clone all repositories under the $user variable, and will skip over repos you've forked (ie. only your own true repos will be included). Remove the if (! exists $content->{parent}) check to also back up the repos you've forked.

    There's a blurb below the code for users who require a proxy. Also note that Github limits the number of API calls an unauthorized user can make, so you'll likely want to create a Github token.

    use warnings; use strict; use Data::Dumper; use Git::Repository; use File::Copy qw(move); use File::Path qw(rmtree); use Pithub; my $user = 'stevieb9'; my $token = '...'; my $bak = 'backups'; my $stg = 'backups.stg'; my $gh = Pithub->new( user => $user, token => $token ); my $result = $gh->repos->list(user => $user); my @repos; while (my $row = $result->next){ print "$row->{name}\n"; push @repos, $row->{name}; } # prepare the staging dir if (-d $stg){ rmtree $stg or die $!; mkdir $stg or die $!; } for (@repos){ my $content = $gh->repos() ->get(user => $user, repo => $_) ->content; if (! exists $content->{parent}){ print "backing up $content->{full_name}...\n"; Git::Repository->run( clone => $content->{clone_url} => "$stg/$content->{name}", { quiet => 1} ); } } # move staging into the backup dir if (-d $bak){ rmtree $bak or die $!; } move $stg, $bak or die $!;

    That'll create a backups.stg dir in your current working directory, clone all of your repos into it, then when done, moves the staging dir over top of a backups dir.

    If you are behind a proxy server, you'll need to add the following lines, and change your Pithub call to new():

    use LWP::UserAgent; my $ua = LWP::UserAgent->new; $ua->env_proxy; my $gh = Pithub->new( ua => $ua, # <-- add this user => $user, token => $token );


    backing up stevieb9/app-envcontrol... backing up stevieb9/app-rpi-envui... backing up stevieb9/async-event-interval... backing up stevieb9/berrybrew... backing up stevieb9/bit-manip... backing up stevieb9/bit-manip-pp... backing up stevieb9/business-isp... backing up stevieb9/cgi-application-plugin-pagebuilder... backing up stevieb9/cisco-acl-count... backing up stevieb9/config... backing up stevieb9/csharp-template... backing up stevieb9/devel-examine-subs... backing up stevieb9/devel-trace-method... backing up stevieb9/devel-trace-subs... backing up stevieb9/dht11-env-control... backing up stevieb9/dnmfarrell-berrybrew... backing up stevieb9/file-edit-portable... backing up stevieb9/File-Portable... backing up stevieb9/freeradius-database... backing up stevieb9/github-backup... backing up stevieb9/mock-sub... backing up stevieb9/netstat-session-stats... backing up stevieb9/nginx-autoconfig... backing up stevieb9/p5-app-copyrightimage... backing up stevieb9/p5-brew-build-script... backing up stevieb9/p5-hook-output-tiny... backing up stevieb9/p5-logging-simple... backing up stevieb9/p5-net-ping... backing up stevieb9/p5-plugin-simple... backing up stevieb9/p5-rpi-dht11-envcontrol... backing up stevieb9/p5-rpi-wiringpi-constant... backing up stevieb9/p5-test-brewbuild-plugin-testagainst... backing up stevieb9/p5-test-brewbuild-revdeps... backing up stevieb9/patches... backing up stevieb9/perl6-log-simple... backing up stevieb9/rpi-adc-ads... backing up stevieb9/rpi-adc-ads_pp... backing up stevieb9/rpi-adc-mcp3008... backing up stevieb9/rpi-bmp180... backing up stevieb9/rpi-dac-mcp4922... backing up stevieb9/rpi-dht11... backing up stevieb9/rpi-digipot-mcp4xxxx... backing up stevieb9/rpi-hcsr04... backing up stevieb9/rpi-shiftreg-sn74hc595... backing up stevieb9/rpi-spi... backing up stevieb9/rpi-wiringpi... backing up stevieb9/scripts... backing up stevieb9/sftp-log-format... backing up stevieb9/sftp-user... backing up stevieb9/svn-repl-wrapper... backing up stevieb9/tcpdump-pcap-sort... backing up stevieb9/test-brewbuild... backing up stevieb9/test-fail... backing up stevieb9/wiringpi-api... backing up stevieb9/wrap-sub... backing up stevieb9/xen-conf-generate...

    update: s/anauthorized/unauthorized/

Pass a Perl aref to C, work on it, and get it back as a Perl array
2 direct replies — Read more / Contribute
by stevieb
on Jan 27, 2017 at 11:00

    ...and how to use the generated XS code in your own module.

    This is a tutorial as much as it is a request for guidance from experienced XS/C/perlguts folks, as TIMTOWTDI, and in this case, likely, a better way (like working on the array reference directly, which I've yet to figure out how).

    This will show you how to pass a Perl array reference (aref) into a C function, convert the aref into a C array, work on it, then push it back onto the stack so the C function returns it as a Perl array (actually a list, but I digress).

    It'll also show that although we bite off of Inline::C, the XS code it generates can be used in your distribution, even without the end-user needing Inline installed.

    First, straight to the code. Comments inline for what's happening (or, at least, what I think is happening... feedback welcomed):

    use warnings; use strict; use feature 'say'; use Inline 'Noclean'; use Inline 'C'; my $aref = [qw(1 2 3 4 5)]; # overwrite the existing aref to minimize memory # usage. Create a new array if you need the existing # one intact @$aref = aref_to_array($aref); say $_ for @$aref; __END__ __C__ void aref_to_array(SV* aref){ // check if the param is an array reference... // die() if not if (! SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV){ croak("not an aref\n"); } // convert the array reference into a Perl array AV* chars = (AV*)SvRV(aref); // allocate for a C array, with the same number of // elements the Perl array has unsigned char buf[av_len(chars)+1]; // convert the Perl array to a C array int i; for (i=0; i<sizeof(buf); i++){ SV** elem = av_fetch(chars, i, 0); buf[i] = (unsigned char)SvNV(*elem); } // prepare the stack inline_stack_vars; inline_stack_reset; int x; for (x=0; x<sizeof(buf); x++){ // extract elem, do stuff with it, // then push to stack char* elem = buf[x]; elem++; // the sv_2mortal() rectifies refcount issues, // and ensures there's no memory leak inline_stack_push(sv_2mortal(newSViv(elem))); } // done! inline_stack_done; }

    We now get an _Inline directory created within the current working directory, which has a build/ dir and then a sub directory (or multiple, just look at the one with the most recent timestamp). Peek in there, and you'll see a file with an .xs extention. This is the file you want if you want to include your work into a real Perl distribution. This essentially allows one to utilize my favourite feature of Inline::C, which is to build XS code for us, without having to know any XS (or little XS) at all.

    After I run the above example, I get this in the XS file (my comments removed):

    #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "INLINE.h" void aref_to_array(SV* aref){ if (! SvROK(aref) || SvTYPE(SvRV(aref)) != SVt_PVAV){ croak("not an aref\n"); } AV* chars = (AV*)SvRV(aref); unsigned char buf[av_len(chars)+1]; int i; for (i=0; i<sizeof(buf); i++){ SV** elem = av_fetch(chars, i, 0); buf[i] = (unsigned char)SvNV(*elem); } inline_stack_vars; inline_stack_reset; int x; for (x=0; x<sizeof(buf); x++){ char* elem = buf[x]; elem++; inline_stack_push(sv_2mortal(newSViv(elem))); } inline_stack_done; } MODULE = c_and_back_pl_f8ff PACKAGE = main PROTOTYPES: DISABLE void aref_to_array (aref) SV * aref PREINIT: I32* temp; PPCODE: temp = PL_markstack_ptr++; aref_to_array(aref); if (PL_markstack_ptr != temp) { /* truly void, because dXSARGS not invoked */ PL_markstack_ptr = temp; XSRETURN_EMPTY; /* return empty stack */ } /* must have used dXSARGS; list context implied */ return; /* assume stack size is correct */

    To note is the following line:

    MODULE = c_and_back_pl_f8ff PACKAGE = main

    That dictates the name of the module you're creating the XS for. You'll want to change it to something like:

    MODULE = My::Module PACKAGE = My::Module

    ...then put that file in the root of your distribution, and add, into your distribution's primary .pm module file:

    require XSLoader; XSLoader::load('My::Module', $VERSION);

    Normally, the #include INLINE.h can be removed, but because we're using some Inline functionality, we need to grab a copy of INLINE.h from somewhere and copy it into the root directory of our distribution so that everything compiles nicely. There's always a copy of it in the _Inline/build/* directory mentioned above. Providing this header file will allow users of your distribution that don't have Inline::C installed to use your module as if they did have it.

Raspberry Pi with analog inputs/outputs, driven by Perl
No replies — Read more | Post response
by stevieb
on Jan 25, 2017 at 23:01

    Well, all of the learning and testing I've done with C, XS, managing bits, reading and understanding hardware datatsheets etc in the last few months is really starting to pay off, with a lot of kudos going out to many Monks here for providing guidance and help with my questions.

    We now have reliable, working Perl code to output and receive input analog signals on the Raspberry Pi. This example uses an MCP41010 digital potentiometer for the analog out, and an ADC1015 analog to digital converter for analog in. I still have two different ADCs to write code for, two more models of digital pots, and later this week I should be receiving my DACs (digital to analog converter), my GPS receiver chip, and my MCP3004/8 ADCs.

    This doesn't do much, but it's the base of what will eventually allow me to have a Pi in the corner that all it does is pull from github and continuously (and automatically!) run unit tests for the Pi software. However, with true analog output/inputs, there's a lot more a Pi can do.

    The schematic and the breadboard layout for the setup.

    Note that the digital pot operates over the SPI bus, which uses RPi::SPI, which is the software I wrote that allows an aref to be sent into a C function (I haven't changed it to use one of the other methods yet) as discussed in this node. The fun aref part is here, in the base WiringPi::API.

    update: the SPI RW functionality now not only allows an aref to be sent in, but will very soon return a proper Perl array, so that you'll always get the read bytes back on every transaction, in the proper order and count. See here if you're interested in what I call Perl Awesomeness./update


    use warnings; use strict; use RPi::WiringPi; my $pi = RPi::WiringPi->new; my $adc = $pi->adc; my $cs = $pi->pin(18); my $dpot = $pi->dpot($cs->num, 0); $dpot->set(0); print "\nValue, Output %\n\n"; for (0..255){ if (($_ % 10) != 0 && $_ != 255){ next; } $dpot->set($_); my $p = $adc->percent(0); print "$_/255: $p %\n"; select(undef, undef, undef, 0.3); } print "\n\nOutput % at 127/255\n\n"; $dpot->set(127); for (0..10){ print $adc->percent(0) . " %\n"; select(undef, undef, undef, 0.2); } $pi->cleanup;

    All it does is switch to different taps (resistor level) on the digital pot which increases/decreases output voltage. The ADC's input pin (A0) is connected directly to the output of the pot, as is the LED, just so I can see visually the changes as well as receive them digitally via the software.


    Value, Output % 0/255: 0.36 % 10/255: 4.24 % 20/255: 8.12 % 30/255: 12.00 % 40/255: 15.88 % 50/255: 19.76 % 60/255: 23.70 % 70/255: 27.58 % 80/255: 31.45 % 90/255: 35.33 % 100/255: 39.21 % 110/255: 43.09 % 120/255: 46.97 % 130/255: 50.85 % 140/255: 54.79 % 150/255: 58.61 % 160/255: 62.48 % 170/255: 66.42 % 180/255: 70.24 % 190/255: 74.12 % 200/255: 77.70 % 210/255: 81.21 % 220/255: 84.91 % 230/255: 88.67 % 240/255: 92.67 % 250/255: 96.97 % 255/255: 99.21 % Output % at 127/255 49.70 % 49.70 % 49.70 % 49.70 % 49.70 % 49.70 % 49.70 % 49.70 % 49.76 % 49.76 % 49.70 %
Bit string manipulation made easy with Bit::Manip
3 direct replies — Read more / Contribute
by stevieb
on Jan 25, 2017 at 12:40

    I've been writing a lot of software lately that deals with direct hardware access (specifically analog and digital hardware for the Raspberry Pi). This means that I've had to learn some C, as well as get proficient with bit manipulation and the bitwise operators.

    As part of my learning, I thought I'd write a module to do this bit manipulation for me, hence Bit::Manip was born. (There's also a Bit::Manip::PP for those who can't/don't want to use XS. It should be indexed shortly).

    Here's a scenario based example of how the software can be used.

    You have a 16-bit configuration register for a piece of hardware that you want to configure and send in. Here's the bit configuration:

    |<--------- 16-bit config register ---------->| | | | |---------------------------------------------| | | | | | | |<------Byte 1: Control------>|<-Byte0: Data->| | | | |-----------------------------|---------------| | 15 | 14 13 | 12 11 | 10 9 8 | 7 6 5 4 3 2 1 | __ _____ _____ ______ _____________ ^ ^ ^ ^ ^ | | | | | START | | UNUSED DATA CHANNEL | PIN SELECT

    ...and the bit configuration:

    15: Start conversation 0 - do nothing 1 - start conversation 14-13: Channel selection 00 - channel 0 01 - channel 1 11 - both channels 12-11: Pin selection 00 - no pin 01 - pin 1 11 - pin 2 10-8: Unused (Don't care bits) 7-0: Data

    Let's start out with a 16-bit word, and set the start bit. Normally, we'd pass in an actual value as the first param ($data), but we'll just set bit 15 on 0 to get our initial data.

    my $data = bit_on(0, 15);

    A couple of helper functions to verify that we indeed have a 16-bit integer, and that the correct bit was set:

    say bit_count($data); say bit_bin($data);

    Output to ensure we're good:

    16 1000000000000000

    Now, we've got the conversation start bit set in our register, and we want to set the channel. Let's use both channels. For this, we need to set multiple bits at once. The datasheet says that the channel is at bits 14-13. Take the LSB (13), pass it along with the data to bit_set(), the number of bits we're intending to update (2) and as the last parameter, put the binary bit string that coincides with the option you want (0b11) for both channels):

    # setting channel $data = bit_set($data, 13, 2, 0b11); # result: 1110000000000000

    We'll use pin 1, and per the datasheet, that's 0b01 starting from bit 11 (again, we're setting 2 bits):

    # setting pin $data = bit_set($data, 11, 2, 0b01); # result: 1110100000000000

    The next two bits are unused, so we'll ignore them, and set the data. Let's use 186 as the data value (10111010 in binary). We're setting 8 bits here:

    # setting data $data = bit_set($data, 0, 8, 186); # or: bit_set($data, 0, 8, 0b10111010); # result: 1110100010111010

    Now we realize that we made a mistake above. We don't want both channels after all, we want to use only channel 1 (value: 0b01). Since we know exactly which bit we need to disable (14), we can just turn it off:

    $data = bit_off($data, 14); # result: 1010100010111010

    (You could also use bit_set() to reset the entire channel register bits (14-13) like we did above).

    Let's verify that we've got the register configured correctly before we send it to the hardware. We use bit_get() for this. The 2nd and 3rd parameters are MSB and LSB respectively, and in this case, we're only checking the 1-bit start bit, so MSB and LSB are the same:

    my $value = bit_get($data, 15, 15); say bit_bin($value); # result: 1

    So yep, our start bit is set. Let's verify the rest:

    # data # (note no LSB param. We're reading from bit 7 through to 0). # since we readily know the data value in decimal (186), we don't # need to worry about the binary representation say bit_get($data, 7); # result 186 # channel say bit_bin(bit_get($data, 14, 13)); # result 1 # pin select say bit_bin(bit_get($data, 12, 11)); # result 1 # ensure the unused bits weren't set say bit_get($data, 10, 8);

    So now we've set up all of our register bits, and confirmed it's ready to be sent to the hardware for processing.

    Perl has some handy features for making binary conversions very easy:

    # use a binary number directly: say 0b101; # 5 # format a number into a binary string, and print it printf("%b\n", 254); # 11111110 # convert a number to a binary string, and assign it my $bin = sprintf("%b", 127); # 1111111

    Disclaimer: also posted at

    update: updated to reflect a bug fix talked about later in the replies./update

Reading from an HC-SR04 ultrasonic distance sensor on the Raspberry Pi
2 direct replies — Read more / Contribute
by stevieb
on Jan 13, 2017 at 17:38

    I've completed another Raspberry Pi related distribution, RPi::HCSR04. This one allows you to use Perl to read data from the HC-SR04 ultrasonic distance sensor.

    It's trivial to use, however, because it uses wiringPi internally, your scripts require root privileges.

    use warnings; use strict; use feature 'say'; use RPi::HCSR04; my $trig_pin = 23; my $echo_pin = 24; my $sensor = RPi::HCSR04->new($trig_pin, $echo_pin); # each call is a separate poll say $sensor->raw; say $sensor->cm . " cm"; say $sensor->inch . " \"";


    634 10.915135593358 cm 4.29729747772217 "

    There's still a bit more work I have to do (catch out-of-range measurements etc), but it works pretty well and is surprisingly accurate.

    Note that per the documentation, the HC-SR04 requires 5v in, and also returns 5v from the ECHO pin back to the Pi's GPIO (which only handles 3.3v), so a voltage regulator or voltage divider is required to limit the voltage to a healthy 3.29v. I opted for the divider while writing the software. Here's a diagram depicting how I achieved that.

    Next up, a SN74HC595 shift register, as I need it to continue to work on the other various projects I have going on. I've almost completed the dist for the BMP180 barometric/altimeter sensor, as well as the MCP300x series analog-to-digital converters.

Perl interface to analog inputs on the Raspberry Pi
2 direct replies — Read more / Contribute
by stevieb
on Jan 11, 2017 at 11:16

    Over the last few months, I've been writing Perl modules/distributions to interact with a Raspberry Pi, its GPIO, and external hardware/devices. My primary objective is to create a complete indoor grow room automation system.

    The past week and a half, I have been focusing on writing a distribution that provides Perl users a way to interact with Adafruit's ADS1xxx Analog to Digital Converters (ADC), so that I could communicate with analog devices via the Pi which does not have any analog inputs. Hence, RPi::ADC::ADS was born.

    The vast majority of functionality specified in the unit's datasheet has been incorporated into this XS-based module, and the documentation outlays all of the critical pieces from the hardware docs.


    • operates on the i2c bus, can connect four units simultaneously for a total of 16 analog inputs
    • ability to access all four channels on each ADC
    • continuous and single conversion modes
    • all eight Data Rate settings available
    • ability to change comparator polarity
    • ability to modify the comparator queue
    • all programmable gain amplifier modes available
    • ability to operate in single-ended mode or differential mode**
    • ability to return either the input data as raw, as a percentage, or as voltage
    • very easy to configure and run; most users won't have to set any parameters or call any configuration methods; the defaults are very sane
    • the documentation is extensive and detailed, but easy to understand and laid out reasonably well (I hope)
    • it has zero dependencies, Perl or otherwise (well, other than the need for a C compiler for the XS file)
    • works with any ADS1xxx model, and will properly bit-shift according to whether we're on a 16-bit wide resolution model, or a 12-bit one

    ** - single-ended mode is the measurement of voltage of a single input relative to ground. Differential mode retrieves the voltage difference between two separate inputs.

    Here's a basic example, and for the most part, exactly how I use the software. Say I have a moisture sensor connected to analog input A0 (0 as far as the software is concerned) and I want to get the moisture level from it:

    use warnings; use strict; use RPi::ADC::ADS; my $adc = RPi::ADC::ADS->new; my $v = $adc->volts; my $p = $adc->percent; my $r = $adc->raw;

    Volts is a floating point number, percent is a float chopped at .2f and raw is the raw 16-bit unsigned int.

    If you have more than one channel active at a time, specify which channel you want to fetch from:

    # A0, A1 and A3 input channels for (0, 1, 3){ print $adc->percent($_) ."\n"; }

    All configuration register options can be changed on the fly, as they each have their own setter/getter. Say you are using two inputs (eg: A0 and A3) as single-ended inputs and at one point in code, you need to retrieve the value of the difference in levels between them. The documentation has a map for parameter value to hardware functionality for all settings. All fetch methods allow you to send in the channel to retrieve on the fly, so we don't have to do anything special here. Per the map in the docs above, either of these will work:

    my $diff_a0_a3 = $adc->percent(5); # or $adc->channel(5); my $diff_a0_a3 = $adc->percent;

    The software is quite complete, and I have tested the vast majority of the configuration settings. I'm about 85% test coverage so there's a bit more work to do there, but I digress.

    My next project is to write a Perl distribution for a BMP 180 barometric and altimiter sensor which I just bought and soldered yesterday, and an MCP3008 analog-digital converter. The 3008 has 10 input channels whereby the ADS only has four, but the ADS has 12-bit or 16-bit of resolution accuracy, where the MCP3008 only has 10-bit, so I decided I'd write code for both.

    See also:

    WiringPi::API, my original and nearly feature complete wrapper for the WiringPi libraries.

    RPi::WiringPi, OO interface for the above wrapper with error detection, safe exit on unexpected terminations, and more.

    RPi::DHT11, module to access the DHT11 temperature and humidity sensor.

    App::RPi::EnvUI, my Dancer2 and jQuery one-page app for indoor grow room control (not yet fully completed).

    RPi::WiringPi::Constant, module that contains commonly used names in the Raspberry Pi/electrical/electronics realm.

relink: Rewriting Symlinks with Perl
No replies — Read more | Post response
by haukex
on Jan 04, 2017 at 13:01

    Fellow Monks,

    I've just made the first public release of a tool I've been working on called relink (Update: it's GPLv3), and I wanted to tell you about it in the form of a short narrative, below. But first, just to whet your appetite:

    # Rewrite the targets of symlinks with Perl $ relink rewrite 's/foo/bar/' PATH(s) # Convert between absolute and relative targets $ relink abs2rel PATH(s) $ relink rel2abs PATH(s) # List links [only certain symlinks] $ relink list [-t '/foo/'] PATH(s)

    -- Hauke D

Drano - A tool for monitoring IBM POWER Series VIO Server NICs
No replies — Read more | Post response
by bpoag
on Dec 30, 2016 at 15:25
    We had a nasty performance bottleneck hit us about a year ago, one that we felt was sneaky enough to require some custom monitoring.. Hence the need for this script. Drano keeps tabs on a particular parameter of a NIC inside of a VIO server, and reports whether or not bandwidth choke may be occuring.
    #!/usr/bin/perl ## ## Drano v0.1 written 032415 by Bowie Poag ## ## Drano makes sure the pipes are clear by reporting when network buff +ers have been depleted. ## This script is designed to be run as root on a VIO server. ## ## It also requires a little tuning on your part -- Replace any instan +ce of "ent12" with the adapter of your choice. ## $DEBUG=0; $threshold=$ARGV[0] || 90; $problemTiny="NO"; $problemSmall="NO"; $problemMedium="NO"; $problemLarge="NO"; $problemHuge="NO"; print "\nDrano: Spinning up..\n"; print "Drano: Collecting netstat dump..\n"; print "Drano:\n"; @netstatDump=`netstat -v ent12 2>/dev/null`; foreach $item (@netstatDump) { chomp($item); $item=~s/^\s+//g; if ($item=~/Max Buffers/) { print "Drano: $item\n"; $item=~s/\s+/ /g; @line=split(" ",$item); $maxTiny=$line[2]; $maxSmall=$line[3]; $maxMedium=$line[4]; $maxLarge=$line[5]; $maxHuge=$line[6]; } if ($item=~/Allocated/ && $item!~/Max/) { print "Drano: $item\n"; $item=~s/\s+/ /g; @line=split(" ",$item); $alcTiny=$line[1]; $alcSmall=$line[2]; $alcMedium=$line[3]; $alcLarge=$line[4]; $alcHuge=$line[5]; } } print "Drano:\n"; $DEBUG && print "MxT:[$maxTiny] MxS:[$maxSmall] MxM:[$maxMedium] MxL:[ +$maxLarge] MxH:[$maxHuge]\n"; $DEBUG && print "AlT:[$alcTiny] AlS:[$alcSmall] AlM:[$alcMedium] AlL:[ +$alcLarge] AlH:[$alcHuge]\n"; print "Drano: Analyizing buffer stats..\n"; print "Drano:\n"; $conTiny=sprintf("%-2.2f",($alcTiny/$maxTiny)*100); $conSmall=sprintf("%-2.2f",($alcSmall/$maxSmall)*100); $conMedium=sprintf("%-2.2f",($alcMedium/$maxMedium)*100); $conLarge=sprintf("%-2.2f",($alcLarge/$maxLarge)*100); $conHuge=sprintf("%-2.2f",($alcHuge/$maxHuge)*100); if ($conTiny > $threshold) { $problemTiny="YES"; } if ($conSmall > $threshold) { $problemSmall="YES"; } if ($conMedium > $threshold) { $problemMedium="YES"; } if ($conLarge > $threshold) { $problemLarge="YES"; } if ($conHuge > $threshold) { $problemHuge="YES"; } printf("%-16s %16s %16s %11s\n","Drano: Buffer Size","Consumption (%)" +,"Threshold (%)","Problem?"); printf("%-16s %16s %16s %11s\n","Drano: -----------","---------------" +,"-------------","--------"); printf("%-16s %16f %16f %11s\n","Drano: Tiny","$conTiny","$threshold", +"$problemTiny"); printf("%-16s %16f %16f %11s\n","Drano: Small","$conSmall","$threshold +","$problemSmall"); printf("%-16s %16f %16f %11s\n","Drano: Medium","$conMedium","$thresho +ld","$problemMedium"); printf("%-16s %16f %16f %11s\n","Drano: Large","$conLarge","$threshold +","$problemLarge"); printf("%-16s %16f %16f %11s\n","Drano: Huge","$conHuge","$threshold", +"$problemHuge"); print "Drano:\n"; if ($problemTiny ne "YES" && $problemSmall ne "YES" && $problemMedium +ne "YES" && $problemLarge ne "YES" && $problemHuge ne "YES") { print "Drano: Everything looks fine. All your buffer consumpti +on percentages are below the given threshold of $threshold"."%".".\n" +; } else { print "Drano: Looks like you've run out of network buffers on +one or more fronts. Time to tune up.\n"; system("logger Drano Warning - Partial network buffer overflow + detected. Network interface requires additional tuning."); }
Slice and dice a ref to hash
2 direct replies — Read more / Contribute
by teun-arno
on Dec 07, 2016 at 16:53
    No question here! Just want to show some code.
    $systems = { 'xai61001' => { # ref to hash ip => '' , name => 'xai61001', model => '8205_E6C', }, 'xai61002' => { # ref to hash name => 'xai61002', model => '9119_MME', ip => '' , }, }; $merge = { 'xai61001' => { # ref to hash description => 'Dit is een test voor xai61001', ibm_description => 'IBM system power 8 xai61001', }, 'xai61002' => { # ref to hash description => 'Dit is een test voor xai61002', ibm_description => 'IBM system power 8 xai61002', }, }; #Most examples I've found on the internet are speaking about "normal" +hashes!! #Wanted to be able to process a scalar pointing to a hash. # this is perl5 , Not perl6. Yes still struggling with it's syntax!! ##################################################################### # sliceAndDice a ref to a hash, in the end you can determine the order + in which a hash is displayed!! # I created 2 arrays which have the same order. Could be expensive whe +n processing a large hash_ref. # $sys = 'xai61002'; # entrie of the $systems HASH! print "$systems->{$sys}\n"; # HASH(0xnumber} print "$systems->{$sys}{ip}\n"; # ip address @keys = ( qw/ip name model/ ); # hardcoded the order of the keys!! print keys %{$systems->{$sys}},"\n"; # the normal way to get at the k +eys @values = @{$systems->{$sys}}{@keys} ; # getting the at the values, i +n the order of @keys # the above is processing a hash ( looks like an A +RRAY, but is NOT ) #print @keys, @values,"\n"; print "\n\nOriginal hash_ref\n"; for $idx ( 0 .. $#keys ) { print "$keys[$idx] => $values[$idx]\n"; } # adding some keys and values, please notice : This is adding to a has +h!! (which is a scalar reference) # the keys in $systems and $merge must be THE SAME $systems->{$key} $m +erge->{$key} !!, but the reference keys MUST be different # otherwise the merge will overwrite an existing key!! @merge = ( qw/description ibm_description/ ); @{$systems->{$sys}}{ @merge } = ( $merge->{$sys}{$merge[0]} , $merge-> +{$sys}{$merge[1]}); @keys = ( qw/description ip name model ibm_description/ ); @values = @{$systems->{$sys}}{@keys} ; #print @keys, @values,"\n"; print "\n\nAdded some keys and values: showing all items\n"; for $idx ( 0 .. $#keys ) { print "$keys[$idx] => $values[$idx]\n"; }

    hope it's usefull to somebody!!

picwoodpecker -- a Tk program to choose and modify your photos
3 direct replies — Read more / Contribute
by Discipulus
on Nov 30, 2016 at 03:37
    Hello monks,

    after months of sparetime works I'm proud to present you picwoodpecker my last Tk effort (now also on github).

    It is application to review and copy pictures with many features.

    Suggestions are welcome.Tests with exif tags from different cameras are even more welcome.

    The post is splitted in 3 parts: here you have the rendered documentation, below there is the code and finally the POD that must be pasted after the __DATA__ token of the program.

                   This software is dedicated to my parents, who need to see printed pictures -- 6 July 1966 




    perl [-s -d -debug -pr -wr -p -x -y -nothumbs -e -quality -pngcompression -df]


         -s|src|source        path
                        the path where search for jpg files to be loaded
                        Can be modified in the Tk interface

    -d|dest|destination path path used to save files Can be modified in the Tk interface

    -debug print more information on the screen Can be modified in the Tk interface

    -pr|phratio floating point the ratio used to display the current photo Can be modified in the Tk interface

    -wr|winratio floating point the ratio to size the window where the photo is displayed Can be modified in the Tk interface

    -p|preload integer how many photos load in memory after and before the current one. Can increase drawing speed time

    -x|gridx integer how many columns in the thumbnail grid

    -y|gridy integer how many rows in the thumbnail grid

    -nothumbs does not load thumbnails at all

    -e|extension jpg|gif|png|gd|gd2 the exstension of saved files Can be modified in the Tk interface

    -quality|jpegquality 0-100 the quality of the file used by GD when saving the current photo in jpeg format An empty value let GD to choose a good default Can be modified in the Tk interface

    -pngcompression 0-9 the compression factor used by GD when saving the current photo in png format -dateformat|df string the format used for dates. It defaults to %Y_%m_%d_%H_%M_%S in such way resulting pics can be ordered correctly. See C<strftime> in L<POSIX> to more hints about formatting.


    The program is aimed to let you to easely choose among photos and save one (or more) copy in the preferred format (jpg as default; gif png gd and gd2 are also available). The name of the copy is crafted using the timestamp when the photo was taken.

    Basically the program will load all jpg files found globbing the path given with command line parameter -source or entered in the graphical interface, and foreach file will examine some exif tags to get sizes, timestamps and the thumbnail (if not present showing a black empty one).

    Orientation of the image is handled automatically for thumbnails and main photo.

    Advanced options are available to manipulate how many photos are copied, in which format and let you to postprocess via exiftool each created image.

    The program uses GD for image manipulation and Image::ExifTool to load infos from photos and in the postprocess of them.


    A main control window and a display one are created. Optionally a third window is created to access the advanced copy options. The display window tends to take the focus being the program focused on displaying photos.

    control window

    The control window contains:

    • all controls to manipulate the photo list (&#39;browse&#39; &#39;new list&#39; &#39;view list&#39; &#39;add to list&#39; and &#39;clear list&#39;) Note the that the &#39;browse&#39; does not fill the list; you need to use &#39;new list&#39; or &#39;add to list&#39; after using it.
    • an entry to choose the destination folder (that will be checked for existence)
    • the photo ratio and the window ratio controls and the debug switch
    • an informative text about the current displayed photo or about the grid of thumbnails
    • the editable name of the current photo (and an eventual suffix) used to save it
    • an information text about the status of the main program (with only relevant information about copying and loading operations as eventual errors)
    • the save button and the advanced options one.
    • controls to navigate the photo list

    display window

    The display window will starts showing a grid of thumbnails. The first one is selected. You can navigate the grid using right arrow and left arrow of the keyboard to move the current selection on or back.

    up arrow and down arrow let you load previous or next grids of thumbanails.

    Enter key will open the currently selected thumbanil in a bigger resolution ( determinted by photo ratio parameter) photo filling the whole window.

    When just one photo is displayed right arrow and left arrow of the keyboard can be used to show next and previous photo while up arrow and down arrow bring you back to the thumbnail view.

    In both control and display window space bar can be used to save the current photo and p key can be used to toggle autoplay. If autoplay is active the time between photos can be set in the control window. Please note that specifying a short amount of time (shorter than the time needed to load photos data) can produce weird showing behaviours.

    advanced copy options

    This menu lets you to have a granular control about how original photo will be copied.

    The allow overwrite if checked silently overwrite a photo wich has the same name of what is composed for the current one.

    bypass original file elaboration (simple copy) make a copy of the original file using the new name but without processing it with GD

    output file type lets you to choose among different file fomrmats (jpeg, gif, png, gd and gd2) and to set the quality (0-100) for jpeg ones. For png files the compression factor (0-9) can be specified only via the command line parameter -pngcompression

    enable multiple copies is trickier. If enabled lets you to choose to copy the file many times, each one with a different resolution. In the multi copies pattern you can specify different resolutions in the format widthxheigth as in 800x600 or 1024x768 and for each format a copy will be created.

    enable post processing can be used to postprocess every image with exiftool program that ships with Image::ExifTool module. You can use alternative program to postprocess your image inserting the full path in the program field. Arguments to such program can be also specified where $ can be used to refer to the full path of the original image. In the exiftool command line you can also specify @ to refer to the current file. So you can postprocess every copy using the following arguments:

    -overwrite_original -all= -tagsFromFile $ -ImageSize -ImageWidth -ImageHeight -ThumbnailImage -Orientation -DateTimeOriginal

    This exiftool command will be issued for every copy made, overwriting each exif tags in the copy, removing them all but taking some tag frome the original file and applying them to the copy. See the exiftool page for a lot of options this wonderful program lets you to use.


    The program was tested against few different camera formats; i dont know if exif tags extracted are widely valid.

    The autoplay feature does not plays well with little time intervals: infact when the interval is smaller than the overall time taken to load the the photo and to redesign the display i'll notice photos and information skipped and the timer going on. I tried fixing this using waitVisibility Tk method, with no luck.


    This software and icons are copyright of Discipulus as found on You may redistribute or modify it under the same term of Perl itself.


Does hash contain minimum keys?
2 direct replies — Read more / Contribute
by GotToBTru
on Nov 11, 2016 at 12:17

    Testing a hash to see if it has values for all required keys. Extraneous keys are okay.

    use strict; use warnings; use Test::More tests => 3; my (%required,%over,%under,%partial); $required{$_} = 1 for qw/header detail trailer/; $over{$_} = $_ for qw/title header subject detail trailer postscript/; $under{$_} = $_ for qw/header trailer/; $partial{$_} = $_ for qw/header trailer/; $partial{detail} = undef; ok(test_it(%over),'checking %over for required keys'); ok(test_it(%under),'checking %under for required keys'); ok(test_it(%partial),'checking %partial for required keys'); sub test_it { my %h = @_; # return (grep {$required{$_} && $h{$_}} keys %h) == (keys %required) +; return (grep {$required{$_} && defined $h{$_}} keys %h) == (keys %r +equired); }

    Update: use of defined suggested by choroba. I would have run into this because 0 is a value I would encounter.

    But God demonstrates His own love toward us, in that while we were yet sinners, Christ died for us. Romans 5:8 (NASB)

extracting strings from non-text files
2 direct replies — Read more / Contribute
by RonW
on Oct 20, 2016 at 21:09

    A coworker (on MS Windows) was cursing he couldn't see what symbol names might be hidden in a non-text configuration file for a proprietary, 3rd party tool he has to use. Since I didn't want to risk being constantly asked to "dump symbols" using my Lunix system, I took a few minutes to write the following program in Perl. Made him happy (for now, at least).

    Note: The tool being used only supports ASCII characters, so I didn't bother with encodings. Probably didn't need to specify ":bytes" in the open statement, but no harm in doing so.

    Maybe others will find this useful.

    #!perl use 5.010_000; use warnings; use strict; if ((@ARGV < 1)) { $0 =~ m#([^\\/]+$)#; my $name = $1 // $0; print STDERR "$name file ...\n" . <<'_DESCRIPTION_'; Extract ASCII strings from files listed. Multiple files allowed. _DESCRIPTION_ exit 1; } for my $file (@ARGV) { open my $fh, '<:bytes', $file or die "Error: Can't open '$file': $ +!\n"; my $buf; while (read $fh, $buf, 1024) { my @strings = split /\P{PosixGraph}/, $buf; for (@strings) { next if /^\s*$/; print "$_\n"; } } }

Add your CUFP
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":

  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?

    What's my password?
    Create A New User
    [marioroy]: Is List::Util::XS a core Perl module?
    [stevieb]: corelist List::Util::XS says "List::Util::XS was first released with perl v5.10.1"
    [stevieb]: so yes, since v5.10.1 :)
    [MidLifeXis]: According to [metaperl:// corelist], Perl 5.10.1
    Lady_Aleena blurbles. One of her favorite modiles is now in "maintenance mode" in favor of a new fangled module.
    [MidLifeXis]: D'oh. Slow out of the gate.
    [marioroy]: Thanks stevieb, MidLifeXis.

    How do I use this? | Other CB clients
    Other Users?
    Others taking refuge in the Monastery: (12)
    As of 2017-05-24 17:52 GMT
    Find Nodes?
      Voting Booth?