NAME
RPi::WiringPi::INTERRUPTS - interrupt (edge event) usage examples for RPi::WiringPi
DESCRIPTION
A practical, runnable guide to handling GPIO interrupts (edge events) with the object-oriented RPi::WiringPi. The interrupt API described here is built on WiringPi::API 3.18 and verified on Pi 5 hardware; the snippets run as written.
This document is edge-only and uses no use threads - general concurrency/worker examples ($pi->worker) live in RPi::WiringPi::WORKERS / docs/examples/threads-examples.md. Callbacks fire in your own interpreter when you service dispatch, so they work on any Perl, threaded or not.
This is the perldoc form of docs/examples/interrupt-examples.md. See RPi::WiringPi for the per-method reference, and the underlying WiringPi::API for the low-level functional API.
THE MODEL
Interrupts are armed on a pin and driven from the Pi object:
Arm on a pin object:
$pin->set_interrupt(...)(or, when RPi::Pin ships it,$pin->background_interrupt(...)).Drive and control dispatch on the Pi object
$pi:wait_interrupts,run_interrupt_loop,dispatch_interrupts,auto_dispatch_interrupts,stop_interrupts,last_interrupt,interrupt_buffer,background_interrupts.
The split is deliberate: arming concerns a single pin, but the dispatch queue and signal wiring are process-wide (one shared event pipe), so those live on $pi, not on individual pins.
The #1 gotcha: as of WiringPi::API 3.18 a callback does not auto-fire. It runs in your interpreter only when you service dispatch. So after arming you must drive dispatch (a loop, or auto_dispatch_interrupts, or a background process). The callback must be a code reference - string sub names are no longer accepted.
QUICK START
use RPi::WiringPi;
use RPi::Const qw(:all);
my $pi = RPi::WiringPi->new;
my $pin = $pi->pin(18);
$pin->mode(INPUT);
# Arm: callback gets ($edge, $timestamp_us)
$pin->set_interrupt(
EDGE_RISING,
sub {
my ($edge, $ts_us) = @_;
print "rising edge at $ts_us us\n";
}
);
# Drive dispatch (callbacks fire here, in this process)
$pi->wait_interrupts(1000) while 1;
DRIVING DISPATCH
Pick whichever fits your program. All of these live on $pi:
# 1) Block until an edge (or timeout ms), then dispatch:
$pi->wait_interrupts(1000) while 1;
# 2) The built-in loop helper (so you don't write the 'while 1' yourself):
$pi->run_interrupt_loop(1000); # Forever
$pi->run_interrupt_loop(1000, 50); # ... or until 50 events
# Break out from a callback or signal handler with:
$pi->stop_interrupt_loop;
# 3) Non-blocking, from inside your own event loop:
$pi->dispatch_interrupts; # Run any pending callbacks, return count
HANDS-OFF: FIRE CALLBACKS WITH NO LOOP
auto_dispatch_interrupts wires the event pipe to a signal so callbacks fire automatically, in your own process, at safe points - no dispatch loop, and the callback can touch your program's variables with no locking. It is a process-wide switch (it affects every armed pin), which is why it lives on $pi.
$pi->auto_dispatch_interrupts(1); # On (default SIGIO)
$pin->set_interrupt(EDGE_RISING, sub { $count++ });
# ... your program runs; the callback fires on its own ...
$pi->auto_dispatch_interrupts(0); # Off
# Choose a different delivery signal to avoid clashing with other SIGIO users:
$pi->auto_dispatch_interrupts(1, 'USR1');
You can also opt in while arming, instead of a separate call:
$pin->set_interrupt(EDGE_RISING, \&handler, { auto_dispatch => 1 });
# or: { auto_dispatch => 'USR1' }
Caveat: a long, non-yielding C/XS call defers the callback until it returns. If you need it to fire even then, use a background process (below).
BACKGROUND HANDLING (ONE PROCESS, FIRES EVEN WHILE MAIN IS BUSY)
Dependency note: the per-pin $pin->background_interrupt form shown in this section requires RPi::Pin 2.3609 or greater (the version this distribution already requires). To handle several pins, the multi-pin $pi->background_interrupts form ("MANY PINS IN ONE BACKGROUND CHILD") runs them all from a single child instead of one child per pin.
$pin->background_interrupt forks a child that runs the handler on each edge while your main program does anything it likes. The handler runs in the child, so it can't touch your main variables - use it for independent work (drive a pin, log, notify).
my $h = $pin->background_interrupt(
EDGE_RISING,
sub {
my ($edge, $ts_us) = @_;
# Independent work, in the background
}
);
# ... main carries on ...
$h->stop; # stop + reap (idempotent); $h->pid / $h->running too
To get values back from the handler, enable the results channel and return a value:
my $h = $pin->background_interrupt(
EDGE_RISING,
sub {
my ($edge, $ts_us) = @_;
return "$edge\@$ts_us";
},
{ results => 1 }
);
while (defined(my $msg = $h->read)) {
# Non-blocking drain in the parent
print "handler said: $msg\n";
}
# $h->fh is the read filehandle, for select / IO::Select
This results channel is identical to $pi->worker's { results => 1 } (see RPi::WiringPi::WORKERS).
MANY PINS IN ONE BACKGROUND CHILD
$pi->background_interrupts is the multi-pin version: one child services several pins (instead of one child per pin), with runtime arm/disarm. Because it spans several pins it lives on $pi.
my $h = $pi->background_interrupts(
[18, EDGE_RISING, \&on_button],
[23, EDGE_BOTH, \&on_sensor, 5000], # Optional debounce (us)
);
$h->disarm(23); # Stop servicing pin 23 (child keeps running for pin 18)
$h->arm(23); # Resume it
$h->stop; # Tear down + reap the single child
The callbacks are fixed when the child forks, so arm/disarm only toggle pins that were in the initial list (arming an unregistered pin croaks).
INSPECTING THE MOST RECENT EVENT
The callback only receives ($edge, $timestamp_us). If one shared handler is armed on several pins, $pi->last_interrupt tells you which fired:
my $cb = sub {
my $i = $pi->last_interrupt; # { pin, pin_bcm, edge, status, ts_us }
printf "pin %d (BCM %d) edge %d\n", $i->{pin}, $i->{pin_bcm}, $i->{edge};
};
$pi->pin($_)->set_interrupt(EDGE_BOTH, $cb) for (18, 23);
It returns the most recently dispatched event (or undef), and is published before the callback runs, so the callback can read it.
QUEUE SIZING AND DROPPED EDGES
Edges are FIFO-queued in a kernel pipe. If a fast source outruns your dispatching the queue fills and the newest edges are dropped (never merged, never blocked) and counted - so loss is never silent:
my $lost = $pi->interrupt_dropped; # 0 unless the pipe overflowed
$pi->interrupt_buffer(1 << 20); # Enlarge the queue (~1 MiB)
my $size = $pi->interrupt_buffer; # Read the current capacity
interrupt_buffer may be set before arming and persists across teardown. Other mitigations: dispatch faster, use a background process, or debounce.
FORKING AND CLEANUP
$pi->cleanup (called automatically at object destruction) resets pins and releases armed interrupts (it calls stop_interrupts for you). It is fork-aware: in a forked child the call is a no-op, so a child can't reset the parent's pins or tear down its interrupts on exit. You can also disarm explicitly while running:
$pin->set_interrupt(EDGE_RISING, \&handler);
# ... later ...
$pi->stop_interrupts; # Release every armed interrupt
$pi->cleanup; # Full teardown (also releases interrupts)
METHOD REFERENCE
Arming methods live on a pin object (my $pin = $pi->pin($n)):
$pin->set_interrupt($edge, $cb [, $debounce_us] [, \%opts])-
Arm an interrupt;
\%optsmay include{ auto_dispatch => 1 | $signal }. $pin->background_interrupt($edge, $cb [, $debounce_us] [, \%opts])-
Handle it in a forked child;
\%optsmay include{ results => 1 }; returns a handle. (Pending an RPi::Pin release - see the dependency note above.)
Dispatch and control methods live on the Pi object ($pi):
$pi->wait_interrupts($timeout_ms)-
Block until an edge/timeout, then dispatch.
$pi->run_interrupt_loop($timeout_ms [, $max])/$pi->stop_interrupt_loop-
Built-in blocking dispatch loop.
$pi->dispatch_interrupts-
Non-blocking: run pending callbacks.
$pi->auto_dispatch_interrupts($bool [, $signal])-
Fire callbacks automatically (no loop).
$pi->background_interrupts([$pin, $edge, $cb [, $deb]], ...)-
One shared child for many pins (+
arm/disarm). $pi->last_interrupt-
Hashref of the most recent dispatched event.
$pi->interrupt_buffer([$bytes])-
Get/set the event-queue capacity.
$pi->interrupt_dropped-
Running count of edges dropped on queue overflow.
$pi->stop_interrupts-
Release every armed interrupt.
Edge constants (EDGE_FALLING=1, EDGE_RISING=2, EDGE_BOTH=3) and INPUT come from RPi::Const qw(:all).
SEE ALSO
RPi::WiringPi, RPi::WiringPi::WORKERS (running background work with $pi->worker), the RPi::WiringPi::FAQ "Interrupt usage" section, docs/examples/interrupt-examples.md, and the underlying WiringPi::API.
AUTHOR
Steve Bertrand, <steveb@cpan.org>