Fri Jun 12 06:28:28 EDT 2009: XBee Shell •

I finally got around to doing some things with my XBee chips. Nothing special at all. I didn't really like the way minicom interfaced the things. First of all, it keeps telling the "modem" to init and reset after the session. The XBee does take AT commands, but ... not like that.

Despite my telling minicom to echo local characters, it still wasn't echoing the AT commands in command mode ... that made me kinda mad. So I wrote my own console program. And when I say I wrote my own, I mean I used POE (so I only wrote 10% of it) and I started from the cookbook (so I only wrote 5% of it).

#!/usr/bin/perl
# My XBee shell.  Copyright 2009 Paul Miller - LGPL
#
# This is very much ripped off from the cookbook though, that license (if any)
# may apply.  http://poe.perl.org/?POE_Cookbook/Serial_Ports
# 
# POE makes this so easy.  Yes, you could do it with blocking read/writes and
# select() and rts/cts checks and things.  Would you really want to?
#

use warnings;
use strict;
use POE qw(Wheel::ReadWrite Filter::Stream);
use Symbol qw(gensym);
use Device::SerialPort;
use Term::ReadKey;

my $dev = uc( shift || "usb0" );
   $dev = "/dev/tty$dev";

die "no such dev: $dev" unless -c $dev;

my $lck = $dev;
   $lck =~ s/\/dev\//\/var\/lock\/LCK../;

# POE is very fun.  You don't write the program flow at all in POE, you simply
# name the things you want to happen and ... they do!

POE::Session->create( package_states => [ main => [qw(_start got_port got_console got_error byebye)] ]);
POE::Kernel->run();

exit 0; # notice, run() doesn't return until the program is done.

sub _start {
    my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];

    # The XBee won't work quite right in line mode, not if you want to be
    # able to interact with the +++ command mode.  Also, I have mine set to
    # 115200, you'd have to change the baud rate to use this if yours is
    # set to default.

    # ATBD 7

    ReadMode 'ultra-raw';
        # ultra-raw gets really serious about grabbing the actual bytes
        # from the serial port.

    my $handle = gensym();
    my $port = tie( *$handle, "Device::SerialPort", $dev, 0, $lck );

    unless( $port ) {
        my $err = $!;
        ReadMode 'restore';
        $err = "lockfile exists: $lck" if $err =~ m/File exists/;
        die "can't open port: $err";
    }

    $port->datatype('raw');
    $port->baudrate(115200); # the baud rate, default is 9600
    $port->databits(8);
    $port->stopbits(1);
    $port->parity("none");
    $port->handshake("rts");
    $port->write_settings;

    # We have two "wheels," streams that handle input and output.   This
    # type of wheel is specifically for reading and writing at the same
    # time, in an event based manner.

    $heap->{port}       = $port;
    $heap->{port_wheel} = POE::Wheel::ReadWrite->new(
        Handle     => $handle,
        Filter     => $heap->{output_stream} = POE::Filter::Stream->new,
        InputEvent => "got_port",
        ErrorEvent => "got_error",
    );

    $heap->{port_wheel}->put("[enter: $ENV{USER}\@$ENV{HOSTNAME}:$dev]\r");

    $heap->{cons_wheel} = POE::Wheel::ReadWrite->new(
        InputHandle  => \*STDIN,
        OutputHandle => \*STDOUT,
        Filter       => $heap->{input_stream} = POE::Filter::Stream->new,
        InputEvent   => "got_console",
        ErrorEvent   => "got_error",
    );

    $heap->{cons_wheel}->put("Press ^D to stop.\r\n");
}

sub got_port {
    my ( $heap, $data ) = @_[ HEAP, ARG0 ];

    # We've received something from the XBee, so we reformulate it for the
    # terminal a little and pass it to the console.

    $data =~ s/\x0d/\x0d\x0a/g;
    $data =~ s/([^\x0d\x0a[:print:]])/"0x" . unpack("H*", $1)/eg;

    $heap->{cons_wheel}->put($data);
}

sub got_console {
    my ( $heap, $data ) = @_[ HEAP, ARG0 ];

    # we've received something from the console

    if( $data =~ s/(\x03|\x04|\x1c)//g ) { # 3 ^C, 4 ^D, 28 ^|
        # if we receive a ^C, ^D or ^|, break out of the program

        ReadMode 'restore';
        $heap->{port_wheel}->put("[exit: $ENV{USER}\@$ENV{HOSTNAME}:$dev]\r");
        $heap->{cons_wheel}->put("Bye!\r\n");
        $poe_kernel->delay("byebye", 1);
    }

    # echo the input locally

    $heap->{port_wheel}->put($data) if $data;

    # reformulate new input for the # XBee a little and pass it to the serial device.

    $data =~ s/\x0d/\x0d\x0a/g;
    $data =~ s/([^\x0d\x0a[:print:]])/"0x" . unpack("H*", $1)/eg;

    $heap->{cons_wheel}->put($data) if $data;
}

sub byebye {
    my $heap = $_[HEAP];

    # once the wheels are gone, POE will notice there's nothing left to do
    # and the program will exit

    delete $heap->{cons_wheel};
    delete $heap->{port_wheel};
}

sub got_error {
    my $heap = $_[HEAP];

    $heap->{cons_wheel}->put("$_[ARG0] error $_[ARG1]: $_[ARG2]");
    $heap->{cons_wheel}->put("bye!");

    delete $heap->{cons_wheel};
    delete $heap->{port_wheel};
}

-Paul

Wed Jun 3 06:48:36 EDT 2009: XBee •

XBee + USB boards

XBee + USB boards

I'm trying to break into the world of embedded programming. I'm starting small. I bought some zigbee modules and USB boards. I figured out a few things on the radios, which feel oddly similar to talking to an old fashion modem — modem in the traditional sense, not a terminal adapter.


bash$ mrsh uptime
Welcome to minicom 2.3-rc1
Compiled on Dec 10 2007, 10:31:35.
Port /dev/ttyUSB0
OK
ATDB
0
ATDB
17
ATVD
10CD
OK
ATCN
test

I haven't figured out the API mode yet, but the transparent passthrough mode has been fun. When I'm not in command mode, typing in one minicom shows up in the other and vice versa. When I issue ATDB in command mode, it reports the signal strength. When I first plugged it in, ATDB returned: 0... but after I had the other module send some data, it reported: 17. 17? The manual says, "Absolute values are reported. For example: 0x58 = -88 dBm (decimal)." So the signal strength when the modules are 0.5 inches apart is really: -23 dBm? I think?

XBee with Power

XBee with Power

I have successfully transferred files using Zmodem! Man that takes me back.

In the picture on the right, note that the lights are on the USB boards. Zigbee's main purpose is to be very power efficient, so the modules certainly wouldn't have lights on them!

-Paul

older stuff (archive-09-03-24.html)
older stuff (archive-08-12-04.html)
older stuff (archive-08-06-30.html)
older stuff (archive-08-01-29.html)
older stuff (archive-07-01-01.html)