Device-XBee-API
view release on metacpan or search on metacpan
lib/Device/XBee/API.pm view on Meta::CPAN
package Device::XBee::API;
use strict;
require Exporter;
our ( @ISA, @EXPORT_OK, %EXPORT_TAGS );
our $VERSION = 0.8;
use IO::Select;
use constant 1.01;
use constant XBEE_API_TYPE__MODEM_STATUS => 0x8A;
use constant XBEE_API_TYPE__AT_COMMAND => 0x08;
use constant XBEE_API_TYPE__AT_COMMAND_QUEUE_PARAMETER_VALUE => 0x09;
use constant XBEE_API_TYPE__AT_COMMAND_RESPONSE => 0x88;
use constant XBEE_API_TYPE__REMOTE_COMMAND_REQUEST => 0x17;
use constant XBEE_API_TYPE__REMOTE_COMMAND_RESPONSE => 0x97;
use constant XBEE_API_TYPE__ZIGBEE_TRANSMIT_REQUEST => 0x10;
use constant XBEE_API_TYPE__EXPLICIT_ADDRESSING_ZIGBEE_COMMAND_FRAME => 0x11;
use constant XBEE_API_TYPE__ZIGBEE_TRANSMIT_STATUS => 0x8B;
use constant XBEE_API_TYPE__ZIGBEE_RECEIVE_PACKET => 0x90;
use constant XBEE_API_TYPE__ZIGBEE_EXPLICIT_RX_INDICATOR => 0x91;
use constant XBEE_API_TYPE__ZIGBEE_IO_DATA_SAMPLE_RX_INDICATOR => 0x92;
use constant XBEE_API_TYPE__XBEE_SENSOR_READ_INDICATOR_ => 0x94;
use constant XBEE_API_TYPE__NODE_IDENTIFICATION_INDICATOR => 0x95;
use constant XBEE_API_TYPE_TO_STRING => {
0x8A => 'MODEM_STATUS',
0x08 => 'AT_COMMAND',
0x09 => 'AT_COMMAND_QUEUE_PARAMETER_VALUE',
0x88 => 'AT_COMMAND_RESPONSE',
0x17 => 'REMOTE_COMMAND_REQUEST',
0x97 => 'REMOTE_COMMAND_RESPONSE',
0x10 => 'ZIGBEE_TRANSMIT_REQUEST',
0x11 => 'EXPLICIT_ADDRESSING_ZIGBEE_COMMAND_FRAME',
0x8B => 'ZIGBEE_TRANSMIT_STATUS',
0x90 => 'ZIGBEE_RECEIVE_PACKET',
0x91 => 'ZIGBEE_EXPLICIT_RX_INDICATOR',
0x92 => 'ZIGBEE_IO_DATA_SAMPLE_RX_INDICATOR',
0x94 => 'XBEE_SENSOR_READ_INDICATOR_',
0x95 => 'NODE_IDENTIFICATION_INDICATOR',
};
use constant XBEE_API_TRANSMIT_STATUS_TO_STRING => {
0x00 => 'Success',
0x02 => 'CCA Failure',
0x15 => 'Invalid destination endpoint',
0x21 => 'Network ACK Failure',
0x22 => 'Not Joined to Network',
0x23 => 'Self-addressed',
0x24 => 'Address Not Found',
0x25 => 'Route Not Found',
0x74 => 'Data payload too large',
};
use constant XBEE_API_BAUD_RATE_TABLE => [1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200];
use constant XBEE_API_BROADCAST_ADDR_H => 0x00;
use constant XBEE_API_BROADCAST_ADDR_L => 0xFFFF;
use constant XBEE_API_BROADCAST_NA_UNKNOWN_ADDR => 0xFFFE;
{
my @xbee_flags = map { /::([^:]+)$/; $1 }
grep( /^Device::XBee::API::XBEE_API_/, keys( %constant::declared ) );
@ISA = ( 'Exporter' );
@EXPORT_OK = ( @xbee_flags );
%EXPORT_TAGS = ( 'xbee_flags' => [@xbee_flags], );
}
=head1 NAME
Device::XBee::API - Object-oriented Perl interface to Digi XBee module API
mode.
=head1 EXAMPLE
A basic example:
use Device::SerialPort;
use Device::XBee::API;
use Data::Dumper;
$Data::Dumper::Useqq = 1;
my $serial_port_device = Device::SerialPort->new( '/dev/ttyU0' ) || die $!;
$serial_port_device->baudrate( 9600 );
$serial_port_device->databits( 8 );
$serial_port_device->stopbits( 1 );
$serial_port_device->parity( 'none' );
$serial_port_device->read_char_time( 0 ); # don't wait for each character
$serial_port_device->read_const_time( 1000 ); # 1 second per unfulfilled "read" call
my $api = Device::XBee::API->new( { fh => $serial_port_device } ) || die $!;
if ( !$api->tx( { sh => 0, sl => 0 }, 'hello world!' ) ) {
die "Transmit failed!";
}
my $rx = $api->rx();
die Dumper( $rx );
=head1 SYNOPSIS
Device::XBee::API is a module designed to encapsulate the Digi XBee API in
object-oriented Perl. This module expects to communicate with an XBee module
using the API firmware via a serial (or serial over USB) device.
This module is currently a work in progress and thus the API may change in the
lib/Device/XBee/API.pm view on Meta::CPAN
return $r;
}
sub send_packet {
my ( $self, $api_id, $data ) = @_;
my $xbee_data = pack( 'nC', length( $data ) + 1, $api_id );
my $checksum = $api_id;
for ( my $i = 0; $i < length( $data ); $i++ ) {
$checksum += unpack( 'C', substr( $data, $i, 1 ) );
}
$checksum = pack( 'C', 0xFF - ( $checksum & 0xFF ) );
$xbee_data = $xbee_data . $data . $checksum;
if ( $self->{api_mode_escape} ) {
# Note we insert the \x7D here, it's not part of the table!
$xbee_data =~ s/$self->{api_mode_escape_re}/\x7D$self->{api_mode_escape_table}->{$1}/g;
}
if ( !$self->{fh_sel} ) {
$self->{fh}->write( "\x7E" . $xbee_data );
} else {
syswrite( $self->{fh}, "\x7E" . $xbee_data );
}
}
=head2 at
Send an AT command to the module. Accepts two parameters, the first is the AT
command name (as two-character string), and the second is the expected data
for that command (if any). Data will be converted to a string before it is
sent, so integers must first be packed before sending. See the XBee datasheet
for a list of supported AT commands and expected data for each.
Returns the frame ID sent for this packet. This method does not wait for a
reply from the XBee, as the expected reply is dependent on the AT command sent.
To retrieve the reply (if any), call one of the L<rx> methods.
If no reply is expected, the caller should immediately free the returned frame
ID via L<free_frame_id> to prevent frame ID leaks.
=head3 Example
my $at_frame_id = $api->at( 'AO', pack( 'C', 1 ) );
=back
=cut
sub at {
my ( $self, $command, $data ) = @_;
$data = '' unless $data;
my $frame_id = $self->alloc_frame_id();
$self->send_packet( XBEE_API_TYPE__AT_COMMAND, pack( 'C', $frame_id ) . $command . $data );
return $frame_id;
}
=head2 remote_at
Send an AT command to a remote module. Accepts three parameters: a hashref with
endpoint addresses, command options, frame_id; the AT command name (as
two-character string); and the third as the expected data for that command (if
any) as a string. See the XBee datasheet for a list of supported AT commands
and expected data for each.
Endpoint addresses should be specified as a hashref containing the following
keys:
=over 4
=item sh
The high 32-bits of the destination address.
=item sl
The low 32-bits of the destination address.
=item na
The destination network address.
=item apply_changes
If set, changes are applied immediately. If not set, an AT AC command must be
sent separately before the changes will take effect.
=back
Note: The following command options are not supported on all XBee devices.
=over 4
=item disable_ack
If set, the remote node will not reply with an ack.
=item extended_xmit_timeout
If set, the exteded transmission timeout will be used.
=back
Returns the frame ID sent for this packet. To retrieve the reply (if any), call
one of the L<rx> methods. If no reply is expected, the caller should immediately
free the returned frame ID via L<free_frame_id> to prevent frame ID leaks.
=head3 Example
The following example disables the blinking association light on the XBee with
a constant on.
if (
!$api->remote_at(
{ sh => 1234567, sl => 1234567890, apply_changes => 1 },
'D5', "\x1"
)
) {
die "Transmit failed!";
}
my $rx = $api->rx();
warn Dumper( $rx );
=cut
sub remote_at {
my ( $self, $tx, $command, $data ) = @_;
my @my_rx_queue;
if ( !$command ) { die "Invalid parameters"; }
if ( !$tx && !$data ) { die "Invalid parameters"; }
if ( !defined $tx && defined $data ) {
$tx = {};
} elsif ( ref $tx ne 'HASH' ) {
$data = $tx;
$tx = {};
}
if ( ( $tx->{sh} && !$tx->{sl} )
|| ( !$tx->{sh} && $tx->{sl} ) )
{
die "Invalid parameters";
}
if ( !defined $tx->{na} ) {
$tx->{na} = XBEE_API_BROADCAST_NA_UNKNOWN_ADDR;
}
if ( !defined $tx->{sh} ) {
$tx->{sh} = XBEE_API_BROADCAST_ADDR_H;
$tx->{sl} = XBEE_API_BROADCAST_ADDR_L;
}
my ( $ack, $chg, $timeout );
if ( defined $tx->{disable_ack} ) {
$ack = 0x01;
} else {
$ack = 0x00;
}
if ( defined $tx->{apply_changes} ) {
$chg = 0x02;
} else {
$chg = 0x00;
}
if ( defined $tx->{extended_xmit_timeout} ) {
$timeout = 0x40;
} else {
$timeout = 0x00;
}
my $options = $ack + $chg + $timeout;
$data = '' unless defined $data;
my $frame_id = $self->alloc_frame_id();
my $tx_req = pack( 'CNNnC', $frame_id, $tx->{sh}, $tx->{sl}, $tx->{na}, $options );
$self->send_packet( XBEE_API_TYPE__REMOTE_COMMAND_REQUEST, $tx_req . $command . $data );
return $frame_id;
}
=head2 tx
Sends a transmit request to the XBee. Accepts three parameters, the first is the
endpoint address, the second is a scalar containing the data to be sent, and the
third is an optional flag (known as the async flag) specifying whether or not
the method should wait for an acknowledgement from the XBee.
Endpoint addresses should be specified as a hashref containing the following
keys:
=over 4
=item sh
The high 32-bits of the destination address.
=item sl
The low 32-bits of the destination address.
=item na
The destination network address. If this is not specified, it will default to
XBEE_API_BROADCAST_NA_UNKNOWN_ADDR.
=back
If both sh and sl are missing or the parameter is undefined,, they will default
to XBEE_API_BROADCAST_ADDR_H and XBEE_API_BROADCAST_ADDR_L.
The meaning of these addresses can be found in the XBee datasheet. Note: In
the future, a Device::XBee::API::Node object will be an acceptable parameter.
If the async flag is not set, the method will wait for an acknowledgement packet
from the XBee. Return values depend on calling context. In scalar context, true
or false will be returned representing transmission acknowledgement by the
remote XBee device. In array context, the first return value is the delivery
status (as set in the transmit status packet and documented in the datasheet),
and the second is the actual transmit status packet (as a hashref) itself.
If the async flag is set, the method will not wait for an acknowledgement packet
and the tx frame ID will be returned. The caller will need to then receive the
transmit status packet (via one of the L<rx> methods) and free the frame ID (via
L<free_frame_id>) manually.
No retransmissions will be attempted by this module, but the XBee
device itself will likely attempt retransmissions as per its configuration (and
subject to whether or not the packet was a "broadcast").
=cut
# API is goofy here. If called in scalar context, returns true or false if the
# packet was transmitted. If called in array context, returns the delivery
# status and the transmit status packet as an array. Note: the actual delivery
# status uses 0 (or false) to indicate success.
sub tx {
my ( $self, $tx, $data, $async ) = @_;
my @my_rx_queue;
if ( !$tx && !$data ) { die "Invalid parameters"; }
if ( !defined $tx && defined $data ) {
$tx = {};
} elsif ( ref $tx ne 'HASH' ) {
$data = $tx;
$tx = {};
( run in 2.721 seconds using v1.01-cache-2.11-cpan-524268b4103 )