domingo, outubro 20, 2013

Integrating RaspberryPi + Xbee USB + Relays Shield


Well, this is my first post in english, just to try disseminate the knowledge about integration between Raspberry Pi and Xbee radio modules, using a USB/Serial card to interface the both.
The basic steps are showed bellow, the configuration steps aren't considered because it's not our focus.

Idea:
The main idea is schedule the time of remote relays that will be activated or not. In my case, will be used to controller the lights of my fish tank.
Using a linux crontab daemon to schedule the tasks, we can set the time to switch the states of lights (relays), like power on at 6 PM and shutdown at 10 PM automatically.

To me, the easy way to integrate the local Xbee module and Raspberry is using a USB/Serial Card to interface. We can use a Raspberry embedded serial port too, but in my case is being used.
So, the picture bellow show the USB card + Xbee module ready to connect in any computer, like my Raspeberry Pi.


Basic Steps:
In my case, the Raspbian distro installed on my Pi recognized the FT 232 automatically, creating a /dev/ttyUSB1 serial interface.

You can find the way to configure your Xbee modules on internet, I won't show here.
This local Xbee module was configured with following options:

MODE : ZIGBEE COORDINATOR API
(Node identifier) NICoordinator_bee
(Node Discovery Options) NO2

On the remote side, we are using another Xbee radio module, connected directly on a 4 Relays Shield supplied with 5V. Note that this board have a option to isolate the 5V and 3.3V between relay and data bus used by Xbee.
The schema of the connections between Xbee and Relay shield is simple like:
  • Xbee Pin (D0) - Relay Pin (1);
  • Xbee Pin (D1) - Relay Pin (2);
  • Xbee Pin (D2) - Relay Pin (3);
  • Xbee Pin (D3) - Relay Pin (4);

So, when the D0 pin change to UP, the relay number 1 will be closed, powering the load (aquarium lights).
On this way, we have a 4 different outputs controlled independently according data received by Xbee.
The relay side will be connected at 4 power outlets powered on 127 Vac.

This remote Xbee module was been configured with following options:

MODE ZIGBEE ROUTER AT(Node identifier) NIEnd_Device_bee
D0: 
D1: 4
D2: 4
D3: 4



The value 4 indicate that the pins D0..D3 are initialized with 0V. To enable the pin, just write a value 5 on this pin. 
Remember: Pin OFF = value 0x04 and Pin ON = value 0x05. 

Bellow you can see the wiring diagram:

where R1...R4 are the relays data pin (load).

Communication:
The communication between Xbee modules consist in a simple message, sending a packet data with the status of the 4 relays. If state = 1, relay will be activated, If state = 0, relay will be deactivated.

I choosed the perl language to write a simple script to send packet data from Raspberry using a local Xbee module to a remote Xbee that control the relays.

The script are divided in 2 main stages:
1 - Open serial port connection (Xbee USB Card) and send a discovery command to find the Xbee modules configured on the Bee network.
In my case, just 2 device are discovered as expect, 1 coordinator (local) and 1 remote.
After the remote Xbee was found, the next stage:
2 -  Check the script arguments and replicate the state in the relays, sending data packets to activate or not each Xbee pin (connected to pin of the relays). In our case, we need to send 4 data packets, containing the state of each relay.
To running the script manually, just type something like that:

 perl enable_relays.pl R1_status R2_status R3_status R4_status
 Ex.:  enable all pins  : perl enable_relays.pl 1 1 1 1  
       disable all pins : perl enable_relays.pl 0 0 0 0
       relays 1 and 3   : perl enable_relays.pl 1 0 1 0

To write this script, I used the Device::SerialPort and Device::Xbee::API perl modules existing on CPAN to create a serial port connection between Raspberry Pi and Xbee USB Card and, after that, create a connection between Local Xbee and Remote Xbee to send data.
At this point, I would like to thank's John, the developer of perl Xbee module that helped me on some issues involved to send a packet data using him api.

Here the perl source code:

#!/usr/bin/perl -w
# usage: perl enable_relays.pl status_pin1,…,status_pin4
# example: perl enable_relays.pl 1 0 1 0
#          perl enable_relays.pl 1 1 1 1
#   perl enable_relays.pl 0 0 0 0

use Device::SerialPort;
use Device::XBee::API;

# Autoflush
$|=1;

# Define Pin Status char
my $pin_on = pack( 'C', 5 ); 
my $pin_off = pack( 'C', 4 ); 

# Communication
my $rx =""; 
my $serialport ="/dev/ttyUSB1";

# Remote Xbee Node Parameters
my $high = 0x13A200;
my $low = 0x40744398;
my $ni = "Remote_NODE_NAME";

# Pins Status of Remote Xbee Node
# 1 = enable(ON) / 0 = disable(OFF)
my @dpins = (0,0,0,0);
$dpins[0]=$ARGV[0];
$dpins[1]=$ARGV[1];
$dpins[2]=$ARGV[2];
$dpins[3]=$ARGV[3];

# Configure the local serial port
my $serial_port_device = Device::SerialPort->new($serialport);
$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 );
$serial_port_device->read_const_time( 1000 );

# Create the API object
my $api = Device::XBee::API->new( { fh => $serial_port_device } ) || die $!;

# Send the ND API command
my $at_frame_id = $api->at( 'ND' );
die "Transmit failed" unless $at_frame_id;

# Wait receive the reply from nodes
while($rx = $api->rx_frame_id( $at_frame_id )){
# Received error ?
if ( $rx->{status} != 0 ) {
die "API error" if $rx->{is_error};
die "Invalid command" if $rx->{is_invalid_command};
die "Invalid parameter" if $rx->{is_invalid_parameter};
die "Unknown error";
}
# If remote node name founded, exit
if($rx->{ni} =~ /$ni/g){ 
# print $rx->{ni};
last;
}
}

# Foreach pin, commit pin status
# Check Pin D0 if ON/OFF
    if($dpins[0] == 1){ #ON
$at_frame_id = $api->remote_at( { sh => $high , sl =>  $low , apply_changes => 1}, D0,$pin_on);
}
else{ #OFF
$at_frame_id = $api->remote_at( { sh => $high , sl =>  $low , apply_changes => 1}, D0,$pin_off);
}

# Check Pin D1 if ON/OFF
        if($dpins[1] == 1){
$at_frame_id = $api->remote_at( { sh => $high , sl =>  $low , apply_changes => 1}, D1,$pin_on);
}
else{
$at_frame_id = $api->remote_at( { sh => $high , sl =>  $low , apply_changes => 1}, D1,$pin_off);
}

# Check Pin D2 if ON/OFF
    if($dpins[2] == 1){ #ON
$at_frame_id = $api->remote_at( { sh => $high , sl =>  $low , apply_changes => 1}, D2,$pin_on);
}
else{ #OFF
$at_frame_id = $api->remote_at( { sh => $high , sl =>  $low , apply_changes => 1}, D2,$pin_off);
}

# Check Pin D3 if ON/OFF
        if($dpins[3] == 1){
$at_frame_id = $api->remote_at( { sh => $high , sl =>  $low , apply_changes => 1}, D3,$pin_on);
}
else{
$at_frame_id = $api->remote_at( { sh => $high , sl =>  $low , apply_changes => 1}, D3,$pin_off);
}

exit 0;

To do the crontab daemon execute the script, you can add the following entries on your crontab:

## Aquarium Main Light ON (Relay 2) @ 18:00 & OFF @ 22:00
00 18 * * * /home/pi/Aquarium/enable_relays.pl 1 1 0 0 >> /dev/null 2>&1
00 22 * * * /home/pi/Aquarium/enable_relays.pl 1 0 0 0 >> /dev/null 2>&1

Now you have a simple script to send data and control the 4 Xbee pins (relays) remotely. 

My fish tank :-)