NAME
RPi::WiringPi::FAQ - FAQ and Tutorial for RPi::WiringPi
DESCRIPTION
This document will hopefully provide enough information in a sane way to get you well on your way with manipulating your Raspberry Pi with the RPi::WiringPi and related distributions.
In this document we use constants that are provided with the :all
tag in RPi::WiringPi::Constant module.
GLOSSARY OF TERMS
HIGH - connected to 3.3v (positive)
LOW - connected to 0v (ground)
floating - state where a pin is not stable at HIGH or LOW
PWM - Pulse Width Modulation (potentiometer-like)
INPUT - pin is listening only
OUTPUT - pin is active in turning things on/off
PWM_OUT - pin is OUTPUT, but PWM capable
GPIO_CLOCK - pin is used for timing
PUD - internal pull up/down resistor
PUD_UP - PUD resistor pulled to HIGH
PUD_DOWN - PUD resistor pulled to LOW
EDGE_FALLING - a state when a pin goes from HIGH to LOW
EDGE_RISING - a state when a pin goes from LOW to HIGH
EDGE_BOTH - both of the above states
DPOT - digital potentiometer
ADC - analog to digital converter (in)
DAC - digital to analog converter (out)
IC - integrated circuit
SETUP
System configuration
If you want to avoid using your system installed Perl installation to use this software (or you want to use interrupts), you should do some pre-configuration of your system. Note that the system installed Perl does not use threads, which is required for interrupts to function.
As your normal, every day user, install perlbrew:
\wget -O - https://install.perlbrew.pl | bash
echo "source /home/pi/perl5/perlbrew/etc/bashrc" >> ~/.bashrc
First thing to do is to install the appropriate `cpanm` version:
perlbrew install-cpanm
Then, install an instance of Perl, and switch to it:
perlbrew install perl-5.26.0
perlbrew switch perl-5.26.0
To use sudo
to run your scripts within the appropriate Perl installation, you need to modify the /etc/sudoers
file. Prepend the string value for the secure_path
directive to include the path to the new perlbrew
managed perl, followed by a colon. For example: /home/pi/perl5/perlbrew/perls/perl-5.26.0/bin:
. Leave the existing part of the string in place.
Now, you can do everything with the normal user account, using their personal installation of Perl, and you just need sudo
to run your scripts, leaving your default system installation intact.
NOTE: Only when using PWM features do you need to use sudo
. All other aspects of this software work properly with a non-privileged user account.
PI
Create a Raspberry Pi object
The first thing you need to do is call the setup()
method. We do that automatically. See new(). We use the GPIO pin numbering scheme.
my $pi = RPi::WiringPi->new;
Board revision
The board revision is the same as the GPIO pin layout on the board:
my $revision = $pi->gpio_layout;
PIN
Creating and using a GPIO pin object
The RPi::Pin class provides you with objects that directly map to the Raspberry Pi's onboard GPIO pins. You generate a pin object through the main $pi
object we created above. See that documentation for full usage information.
my $pin = $pi->pin(27);
# set the mode to output, presumably to power an external device
$pin->mode(OUTPUT);
# by default, pins are set to LOW (ie. 0 voltage). Turn it on...
$pin->write(HIGH);
# get the current status of a pin (HIGH or LOW)
my $state = $pin->read;
# get a pin's pin number
my $num = $pin->num;
Internal pull up/down resistor
All GPIO pins on the Raspberry Pi have built-in pull up/down resistors to prevent pins being in a "floating" state when not connected to either ground or power. This is very important in many situations, particularly when using interrupts.
# HIGH when not in use
$pin->pull(PUD_UP);
# LOW when not in use
$pin->pull(PUD_DOWN);
Pulse Width Modulation (PWM)
Pulse Width Modulation kind of acts like a potentiometer (or a variable switch... like a light dimmer). They are used to send pulses of electricity to a device across time. It is required for things like stepper motors, or dimming an LED. Note that only physical pin 12
on the Raspberry Pi has hardware-based PWM (GPIO #18).
# set the pin to PWM_OUT mode. Must be physical pin 12
$pin->mode(PWM_OUT);
# values are 0-1023 which represent 0% to 100% power
$pin->pwm(512); # pin output is ~50%
# make pin go from off to bright gradually...
# requires Time::HiRes qw(usleep);
my $pin = $pi->pin(18);
$pin->mode(PWM_OUT);
for (0..1023){
$pin->pwm($_);
usleep 50000;
}
Interrupt usage
Built in is the ability to have Perl code you define executed when a pin's edge changes (a pin goes from LOW to HIGH or vice-versa). This code acts as an interrupt handler. The Interrupt Service Request that listens for the change runs in a separate C thread than your application.
Interrupts are useful in many cases, but think of a button; you want an action to happen when someone presses a physical button on your prototype, but you obviously want to be doing other things while waiting for that button press.
# set an interrupt handler for when the pin goes from
# LOW to HIGH. The second parameter is the string name
# of the Perl subroutine at the bottom of this example
$pin->interrupt_set(EDGE_RISING, 'handler');
# HIGH to LOW
$pin->interrupt_set(EDGE_FALLING, 'handler');
# HIGH and LOW (handler will be called on both changes)
$pin->interrupt_set(EDGE_BOTH, 'handler');
sub handler {
print "in handler\n";
# do other stuff, perhaps turning on/off other pins
}
NOTE: If you receive errors about the handler callback not being found, simply specify the callback with the full package (eg. 'main::handler'
).
I2C BUS
Allows you to read and write to devices on the I2C bus using the external RPi::I2C distribution. Please refer to that documentation for full usage instructions.
Instantiation and communication
my $device_addr = 0x04;
my $device = $pi->i2c($device_addr);
# read a single byte at the default register address
print $device->read;
# read a single byte at a specified register
print $device->read_byte(0x15);
# read a block of five bytes (register param optional, not shown)
my @bytes = $device->read_block(5);
# write a byte
$device->write(255);
# write a byte to a register location
$device->write_byte(255, 0x0A);
# write a block of bytes (register param left out again)
$device->write_block([1, 2, 3, 4]);
SERIAL BUS
Allows you to perform basic read and write operations on a standard serial interface using the RPi::Serial library. See that documentation for full usage information.
my $dev = "/dev/ttyAMA0";
my $baud = 115200;
my $ser = $pi->serial($dev, $baud);
$ser->putc(5);
my $char = $ser->getc;
$ser->puts("hello, world!");
my $num_bytes = 12;
my $str = $ser->gets($num_bytes);
$ser->flush;
my $bytes_available = $ser->avail;
SERIAL PERIPHERAL INTERFACE (SPI) BUS
Set up and communication
Allows you to write to and read from devices attached to the SPI bus, using the external RPi::SPI distribution. Please refer to that documentation for full usage instructions.
# generate a new SPI object
my $channel = 0; # /dev/spidev0.0
my $spi = $pi->spi($channel);
my $buf = [0x01, 0x02];
my $len = 2;
# write the two bytes in the buffer to channel /dev/spidev0.0
$spi->rw($buf, $len);
# do a read-only call. Send in the number of bytes you want back as dummy
# bytes (ie. 0)
my $dummy = [0x00, 0x00, 0x00];
my @read_buf = $spi->rw($dummy, 3);
ANALOG TO DIGITAL CONVERTERS (ADC)
Initialization and reading input
We provide access to both the ADS1x15 and MCP3008 ADCs.
The default is to return an ADS1115 object from RPi::ADC::ADS. Please review that documentation for full usage instructions.
# fetch a new ADC object
my $adc = $pi->adc;
# fetch the voltage level on pin A0 on the ADC
my $v = $adc->volts(0);
# fetch the percentage of input on pin A0
my $p = $adc->percent(0);
You can also request an MCP300x ADC from RPi::ADC::MCP3008. Again, for full details, see that documentation.
my $adc = $pi->adc(model => 'MCP3008', channel => 0);
my $raw = $adc->raw(0);
my $percent = $adc->percent(0);
DIGITAL TO ANALOG CONVERTERS (DAC)
Configuration, initialization and setting analog output levels
This functionality is brought in from RPi::DAC::MCP4922. Please refer to that documentation for full configuration and usage instructions.
# prepare and fetch a new DAC object
my $dac_cs_pin = $pi->pin(29);
my $spi_chan = 0;
my $dac = $pi->dac(
model => 'MCP4922',
channel => $spi_chan,
cs => $dac_cs_pin
);
my ($dacA, $dacB) = (0, 1);
$dac->set($dacA, 4095); # 100% output
$dac->set($dacB, 0); # 0% output
DIGITAL POTENTIOMETERS
Initialization and usage
This functionality is brought in from RPi::DigiPot::MCP4XXXX. Please refer to that documentation for full usage instructions.
my $cs = 18; # GPIO pin connected to dpot CS pin
my $channel = 0; # SPI channel /dev/spidev0.0
my $dpot = $pi->dpot($cs, $channel);
# set to 50% output
$dpot->set(127);
# shutdown (sleep) the potentiometer
$dpot->shutdown;
SHIFT REGISTERS
Overview
This software has the capability to utilize 74HC595 shift registers.
Each register contains 8 digital outputs, and four can be daisy-chained together for a total of 32 extra output pins.
Each register (or chain of registers) require only three GPIO pins.
Usage
We'll get right into the code:
# the new register pins will start at GPIO 100.
# this can be any number outside of existing GPIO
my $base = 100;
# the number of pins on the register(s) you plan on
# using. Maximum eight per register
my $num_pins = 8;
# the GPIO pin number that the register's DS pin (14)
# is connected to
my $data = 5;
# the GPIO pin number that the register's SHCP pin (11)
# is connected to. This is the register's clock
my $clk = 6;
# the GPIO pin number that the register's STCP pin (12)
# is connected to. This is the register's latch pin
my $latch = 13;
# initialize the register
$pi->shift_register($base, $num_pins, $data, $clk, $latch);
# now you have full access to the register's eight outputs
# through standard methods
for (100..107){
my $pin = $pi->pin($_);
$pin->write(HIGH);
}
GPS
Allows you to track your current position and various other information. gpsd
library must be installed and running. See the GPSD::Parse documentation for full usage instructions.
Usage
my gps = $pi->gps;
print $gps->tpv('lat') ."\n"; # latitude
print $gps->tpv('lon') ."\n"; # longitude
print $gps->tpv('speed') ."\n"; # current speed
print $gps->direction ."\n"; # current heading
LCD TOUCH SCREENS
Typical 16-pin, 2-4 row and 16-20 column LCD screens work here. You can use 4-bit or 8-bit mode (4-bit requires 6 GPIO pins, 8-bit requires 10). If you need a higher rate of data transmission to the LCD, use 8-bit mode. Typically, 4-bit has always worked perfectly for me.
Initialize
Before an LCD can be used, it must be initialized. This may look like a lot, but you only need to do it once. Essentially, you're configuring all pins up front.
NOTE: When in 4-bit mode, although you're setting d0
through d3
pins up and leaving d4
through d7
as 0
, the wiring must connect to LCD pins d4
through d7
. Look at LCD pin 4-7 as LCD pin 0-3 when in 4-bit mode.
my %lcd_args = (
rows => 2, # number of display rows, 2 or 4
cols => 16, # number of display columns 16 or 20
bits => 4, # data width in bits, 4 or 8
rs => 1, # GPIO pin for the LCD RS pin
strb => 2, # GPIO pin for the LCD strobe (E) pin
d0 => 3, #
... # d0-d3 GPIO pinout numbers
d3 => 6, #
d4 => 7, # set d4-d7 to all 0 (zero) if in 4-bit mode
... # otherwise, set them to their respective
d7 => 11 # GPIO pins
);
my $lcd = $pi->lcd(%lcd_args);
Display operations
Now that we've initialized the LCD, we're ready to use it.
# turn the display on/off. It's on by default
$lcd->display(ON); # or OFF
# put the cursor at col 0, row 0
$lcd->home;
# clear the display and move cursor to home
$lcd->clear;
Cursor operations
# move the cursor to a position
$lcd->position(0, 0); # col 0 (first slot), row 0 (top row)
$lcd->position(0, 1); # col 0 (first slot), row 1 (bottom row on 2 row LCD)
$lcd->positon(5, 1); # col 5 (6th slot), row 1
# turn on/off cursor (on by default)
$lcd->cursor(OFF); # or ON
# make the cursor blink (off by default)
$lcd->cursor_blink(ON); # or OFF
Output operations
By default, output starts at col 0 and row 0 of the display. Use position()
to move it around before outputting.
# print out a string
$lcd->print("My name is stevieb");
Putting it all together
Here's a trivial script that outputs information to specific LCD positions (we'll start right after an LCD init()
).
my $perl_ver = '5.24.0';
my $name = 'stevieb';
$lcd->home;
$lcd->print("${name}'s RPi, on");
$lcd->position(0, 1);
$lcd->print("Perl $perl_ver...");
BAROMETRIC PRESSURE SENSORS
There's support for the BMP085 and BMP180 barometric pressure and altimiter sensors, which also include a temperature sensor. This functionality is provided by the RPi::BMP180 distribution.
Usage
A full use-case example of using the barometric/temperature sensor:
my $pin_base = 200; # any number higher than the highest GPIO
my $bmp = $pi->bmp($pin_base);
my $f = $bmp->temp;
my $c = $bmp->temp('c');
my $p = $bmp->pressure; # kPa
HYGROMETER SENSORS
We provide access to the DHT11
temperature/humidity sensors through the RPi::DHT11 distribution.
Usage
my $sensor_pin = 21;
my $env = $pi->hygrometer($sensor_pin);
my $humidity = $env->humidity;
my $temp = $env->temp; # celcius
my $farenheit = $env->temp('f');
UTIL LIBRARY
Overview
The included RPi::WiringPi::Util module contains a few helper-type methods for internal and external use. Most of these you won't need, but others are very helpful when writing your own scripts that go beyond trivial.
You can transform pin numbers from one scheme to another, get full pin number maps/translation hashes, manually export and unexport GPIO pins etc.
It's worth having a look at...
RUNNING TESTS
Setup and configuration
First off, please review the t/README
file for the GPIO pins we use for the test physical configuration, and set up the Pi according to the unit test diagram.
Base information
Before running the tests, you need to set a special environment variable so that we know we're on a Pi board. This ensures CPAN testers won't run the tests across all of its platforms:
export RPI_BOARD=1
Personally, I set all the environment variables in my /etc/environment
file.
There are a couple of test files that require root privileges, but we handle this internally by re-running the file with sudo
enabled. This allows all tests but these couple to be run as a standard user.
Arduino/I2C
For testing the RPi::I2C module, we have a dedicated Arduino sketch in the docs/sketch
directory that we test against. Install the sketch, hook up the I2C between the Pi and the Arduino, and connect a ground pin on the Arduino to the ground bus on the Pi.
You then set the following environment variable:
export RPI_ARDUINO=1
These tests skip by default.
Serial Port Testing
To test the serial port RPi::Serial library, you must have a loopback between the Tx and Rx pins, and:
export RPI_SERIAL=1
These tests will skip by default otherwise.
Automated with Test::BrewBuild
Test::BrewBuild has some special features specifically to facilitate the automatic testing of this distribution while on a Raspberry Pi, with provisions that allow you to display the last test run results on an LCD if desired.
First, on the Pi you're running, you need to set the PI_BOARD
environment variable to ensure all tests run. I set this to 1
in /etc/environment
.
First, install Test::BrewBuild
. Then, start the bbtester
software in the background:
bbtester start -a
The above bbtester
command string will only trigger a test build if the local repository commit checksum differs from the remote. To bypass this check and execute a test run every cycle regardless if the checksums differ or not, send in the --csum|-c
flag to bbtester
:
bbtester start -a -c
Next, run a bbdispatch
run against the local tester in --auto
mode. The -a
flag with no parameters runs continuously, sleeping for a default of 60 seconds between runs. Simply send in an integer value that represents a certain number of runs if desired.
bbdispatch -t localhost -r https://github.com/stevieb9/rpi-wiringpi -a
To use the LCD functionality which displays the last date/time that a test run succeeded, along with the last commit tested and it's result (PASS/FAIL), set up an environment variable that contains a comma-separated list of GPIO pin numbers that the LCD is connected to:
export BB_RPI_LCD=5,6,4,17,27,22
...which represents LCD pins:
RS, E, D4, D5, D6, D7
To make it work. I set this one in /etc/environment
as well. You then need to restart the dispatcher with the --rpi
or the equivalent -R
flag:
bbdispatch -t localhost -r https://stevieb9/rpi-wiringpi -a --rpi
The --rpi
flag is rather hidden (but it *is* documented subtly), as this is a feature that most likely I'll be the only consumer of.
DEVELOPMENT
Section that describes some particulars when developing or adding new external devices to RPi::WiringPi. This section is new, and very incomplete. I'll add things as I think of them.
Pins
Any time you accept a pin number to pass along to an external module for use, you *must* call $self-
register_pin($pin_num)> in order to have the cleanup functionality tidy things up properly. Neglecting to do this will prevent the cleanup regimen from knowing about these pins, and therefore will be left in an inconsistent state, possibly causing damage on a different run.
AUTHOR
Steve Bertrand, <steveb@cpan.org>
COPYRIGHT AND LICENSE
Copyright (C) 2017 by Steve Bertrand
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.18.2 or, at your option, any later version of Perl 5 you may have available.