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 )