XBee Extensions to the Python Socket API

From Digi Developer

Jump to: navigation, search

Digi has extended the standard Python sockets interface to abstract XBee/ZigBee Mesh networking technology. This has been accomplished by defining a new address family, AF_XBEE, and by defining new protocol constants XBS_PROT_APS and XBS_PROT_TRANSPORT for use with standards-compliant ZigBee XBee modules and proprietary ZigBee-like DigiMesh modules, respectively.

ZigBee transmits data in distinct frames, referred to by the ZigBee protocol specification as “APS Data” or “Application Data Units” (APDU). Frame transmission is connectionless: frames may be lost or be received in a different order from which they were sent by the transmitter. These behaviors match the SOCK_DGRAM socket type, socket datagram service.

Creating a socket and binding a socket to an endpoint using the socket() and bind() methods of the Python sockets module allows for easy communication on a Mesh network. The XBee Sockets extension for Python has been authored semantically to operate under the principle of least surprise: methods behave as much as possible as they do for any other network type. The socket() call creates a socket, sendto() and recvfrom() are used to send and receive datagrams, select() is used to wait on socket descriptor activity, and so on.

To get started writing Python code that takes advantage of the XBee extensions to the Python sockets API, see the ZigBee Sockets Examples later in this guide.

For detailed information on what functions have been extended to be used with XBee, see the following function reference:

Contents

close()

Purpose

close the socket.

Syntax
close(self) . None 
Description

Close the XBee socket. The socket cannot be used after this call.

If the socket was bound to an application endpoint, that application endpoint becomes available for other sockets to use.

getsockopt()

Purpose

get socket options

Syntax
getsockopt(self, level, optname) . integer or string 
Description

Get an XBee socket option.

level

level specifies which level the socket option is to be read from. The following levels are specified:

SOL_SOCKET Regular socket options, such as SO_LINGER
XBS_SOL_ENDPOINT Get options for the endpoint bound to the socket object.
XBS_SOL_EP Alias for XBS_SOL_ENDPOINT
optname

optname is expected to be an integer constant from the socket module. The following optname values are specific to the given level:

XBS_SOL_ENDPOINT / XBS_SOL_EP

XBS_SO_EP_FRAMES_TX Get the number of frames transmitted from this endpoint.
XBS_SO_EP_FRAMES_RX Get the number of frames received at this endpoint.
XBS_SO_EP_FRAMES_TX_ERR Get the number of frames that could not be transmitted due to an error.
XBS_SO_EP_FRAMES_RX_ERR Get the number of received frames dropped due to an exhaustion of internal buffers.
XBS_SO_EP_BYTES_TX Get the number of bytes transmitted from this endpoint.
XBS_SO_EP_BYTES_RX Get the number of bytes received at this endpoint.
XBS_SO_EP_BYTES_TX_ERR Get the number of bytes that could not be transmitted due to an error.
XBS_SO_EP_BYTES_RX_ERR_UTRUNC Get the number of bytes dropped because the user buffer passed to recvfrom() was not large enough to contain the entire packet.
XBS_SO_EP_BYTES_RX_ERR_NOBUF Get the number of received bytes dropped due to an exhaustion of internal buffers.
XBS_SO_EP_BCAST_RADIUS Get the value of the XBS_SO_EP_BCAST_RADIUS option. This value specifies the number of hops for a broadcast transmission. It is ignored if the frame is not a broadcast transmission. A value of 0 specifies the maximum number of hops (NH) is used.
XBS_SO_EP_SYNC_TX Get the value of the XBS_SO_EP_SYNC_TX flag. A value of 0 indicates messages sent by sendto() are queued and delivered asynchronously. If an error occurs, it is not reported. A value of 1 indicates sendto() waits until the message is acknowledged by the receiver. If an error occurs, an exception is thrown. Use of this value may reduce message throughput.
XBS_SO_EP_TX_STATUS Get the value of the XBS_SO_EP_TX_STATUS flag. A value of 0 indicates that transmit status frames will not be made available by a call to recvfrom() on the socket. A value of 1 indicates that a call to recvfrom() may include XBee Transmit Status information (generated by a previous sendto() call) allowing a program to determine asynchronously whether or not a prior transmission was successfully received by the remote recipient. See "Notes on Transmit Status", below.

recvfrom()

Purpose

receive a message from a socket.

Syntax
recvfrom(self, buflen [,flags]) . (data, addr) 
Description

Receive up to buflen bytes of a datagram from an endpoint bound on the socket. If the datagram contained more bytes than buflen, the extra bytes are silently discarded (like with UDP/IP). In practice, set buflen to 255 for most XBee technologies.

This function returns the data from the socket along with the sender’s address for the data in a tuple. The format for addr is the tuple (address_string, endpoint, profile_id, cluster_id, options_bitmask, transmission_id).

The only flag supported is MSG_DONTWAIT to force a single socket transaction to be non-blocking.

sendto()

Purpose

send a message from a socket.

Syntax
sendto(data[, flags], addr) . count 
Description

Send an APS datagram specifying the destination address.

The format for addr is the tuple (address_string, endpoint, profile_id, cluster_id, options_bitmask, transmission_id).

The only flag supported is MSG_DONTWAIT to force a single socket transaction to be non-blocking.

setsockopt()

Purpose

set socket options.

Syntax
setsockopt(self, level, optname, value) . integer or string 
Description

Set a XBee socket option.

level specifies the level to which the socket option is to be applied. The following levels are specified:

SOL_SOCKET Regular socket options.
XBS_SOL_ENDPOINT Mesh extension options.

optname is expected to be an integer constant from the socket module. The following optname values are specific to the given level:

XBS_SOL_ENDPOINT / XBS_SOL_EP

XBS_SO_EP_BCAST_RADIUS Set the value of the XBS_SO_EP_BCAST_RADIUS option. This value specifies the number of hops for a broadcast transmission. It is ignored if the frame is not a broadcast transmission. A value of 0 specifies the maximum number of hops (NH) is used.
XBS_SO_EP_SYNC_TX Set the value of the XBS_SO_EP_SYNC_TX flag. A value of 0 indicates messages sent by sendto() are queued and delivered asynchronously. If an error occurs, it is not reported. A value of 1 indicates sendto() waits until the message is acknowledged by the receiver. If an error occurs, an exception is thrown. Use of this value may reduce message throughput.
XBS_SO_EP_TX_STATUS Set the value of the XBS_SO_EP_TX_STATUS flag. A value of 0 indicates that transmit status frames will not be made available by a call to recvfrom() on the socket. A value of 1 indicates that a call to recvfrom() may include XBee Transmit Status information (generated by a previous sendto() call) allowing a program to determine asynchronously whether or not a prior transmission was successfully received by the remote recipient. See "Notes on Transmit Status", below.

value can be either an integer or a string depending on the argument requirements of the socket option named by optname.

socket()

Purpose

create a XBee endpoint for communication.

Syntax
socket(AF_XBEE [, type [, proto]]]) . socket object 
Description

Open a XBee socket of the given type. At present the type must be SOCK_DGRAM.

proto can be either XBS_PROT_TRANSPORT for the proprietary mesh transport or XBS_PROT_APS for the ZigBee standards compliant APS transport. The transport type depends on which Mesh radio and Mesh radio firmware is loaded in the gateway device. If the transport protocol specified is unusable with the installed radio, EINVAL will be returned.

Notes on XBee Address Tuples

The general format for an XBee address is: (address_string, endpoint, profile_id, cluster_id, options_bitmask, transmission_id)

address_string must be of the form "[nn:nn:nn:nn:nn:nn:nn:nn]!" for 64-bit addresses or "[nnnn]!" for 16-bit addresses.

endpoint is an 8-bit unsigned integer

profile_id is a 16-bit unsigned integer

cluster_id is a 16-bit unsigned integer

options_bitmask is an 8-bit unsigned integer

transmission_id is an 8-bit unsigned integer

Values for options_bitmask mean the following on a frame received from a call to recvfrom():

XBS_OPT_RX_ACK Received as unicast (should be renamed, as it does not indicate acknowledgement).
XBS_OPT_RX_BCADDR Received via broadcast.
XBS_OPT_RX_BCPAN Received on broadcast PAN.
XBS_OPT_RX_APSSEC Received using APS end-to-end security.

Note that certain receive options are only valid when using certain XBee modules and firmware combinations (e.g. such as the XBee Series 2 Smart Energy firmware).

Values for options_bitmask mean the following on a frame transmitted from a call to sendto():

XBS_OPT_TX_NOACK Disable end-to-end acknowledgement.
XBS_OPT_TX_NOREPEAT Do not repeat this packet.
XBS_OPT_TX_BCPAN Send to broadcast PAN.
XBS_OPT_TX_TRACERT Invoke traceroute.
XBS_OPT_TX_PURGE Purge if delayed by duty cycle.
XBS_OPT_TX_APSSEC Transmit using APS end-to-end security.

Values for transmission_id must be greater than zero if the XBS_SO_EP_TX_STATUS option is used to track an individual transmission status.

Notes on Transmit Status

If a user sets the XBS_SO_EP_TX_STATUS flag using a call to setsockopt() with a level of XBE_SOL_ENDPOINT subsequent calls to recvfrom() will produce information pertaining to the transmit status of previous XBee transmissions created with prior calls to the sendto() function. Here is an example of enabling transmit status frames using a call on an XBee socket object called xbee_sd:

# Enable transmission status reports on calls to recvfrom()
xbee_sd.setsockopt(XBS_SOL_EP, XBS_SO_EP_TX_STATUS, 1)

In order to correlate a specific transmission with a transmit status frame received via the recvfrom() call one must use the transmit_id of the address tuple when addressing a frame in a call to sendto(). For example:

# Send hello world to remote XBee using transmit ID of 0x42
xbee_sd.sendto("Hello, World!", 0, ('[00:13:a2:00:40:0a:07:a5]!', 0xe8, 0xc105, 0x11, 0, 0x42))

Subsequent calls to recvfrom() may then receive a transmit status frame and additional logic may detect one of several types of transmit status frames:

# Receive message, detect if transmit status frame:
buf, addr = xbee_sd.recvfrom(84)
cluster_id = addr[3]
xmit_id = addr[5]
 
if cluster_id == 0x89:
    # X-API transmit status frame, TX_STATUS 3rd byte:
    tx_status = ord(buf[2])
elif cluster_id == 0x8b:
    # X-API ZigBee transmit status frame, TX_STATUS 6th byte:
    tx_status = ord(buf[5])
elif cluster_id == 0:
    # XBee driver status indication:
    tx_status = struct.unpack("i", buf)[0]
else:
	# must be a regular data frame:
	handleRegularData(buf, addr)
 
if tx_status == 0:
    # Transmission successful!
    accountForGoodTransmission(xmit_id)

For reference on the possible values of the X-API transmit status frames, please refer to the XBee OEM Module Manual.

XBee Sockets Examples

Send “Hello, World!”

# 
# This example sends "Hello, World!" using the Digi
# proprietary mesh transport to a fixed node address.
# 
 
 
# include the sockets module into the namespace:
from socket import * 
 
 
# The Format of the tuple is:
# (address_string, endpoint, profile_id, cluster_id)
# 
# The values for the endpoint, profile_id, and
# cluster_id given below are the values used to write
# to the serial port on an Ember-based XBee module.
# For 802.15.4 use 0,0,0
DESTINATION=("00:0d:6f:00:00:06:89:29!", \
            0xe8, 0xc105, 0x11) 
 
# Create the socket, datagram mode, proprietary transport:
sd = socket(AF_XBEE, SOCK_DGRAM, XBS_PROT_TRANSPORT) 
 
 
# Bind to endpoint 0xe8 (232) for ZB/DigiMesh, but 0x00 for 802.15.4
sd.bind(("", 0xe8, 0, 0)) 
 
 
# Send "Hello, World!" to the destination node, endpoint,
# using the profile_id and cluster_id specified in
# DESTINATION: 
sd.sendto("Hello, World!", 0, DESTINATION)
socket.bind() Notes
  • If the bind fails with socket.error:(22, 'Invalid argument'), then double check that another running Python script has not already bound to the end-point you selected - for example, check the Python auto-start settings. Only one Python script can bind to a specific end-point, such as the 0xE8 (232) in this example.
  • The last 2 parameters are critical for the sendto(), but ignored by the bind(). The sd.bind() will return any packets received on end-point 0xE8 regardless of cluster or profile codes.

Reading and Writing

# 
# This example binds to application endpoint 0xe8,
# receives a single frames at this endpoint and then# sends the frame's payload back to the originator
# using the radio's proprietary mesh transport.
# 
 
# include the sockets module into the namespace:
from socket import * 
 
# Create the socket, datagram mode, proprietary transport:
sd = socket(AF_XBEE, SOCK_DGRAM, XBS_PROT_TRANSPORT) 
 
# Bind to endpoint 0xe8 (232) for ZB/DigiMesh, but 0x00 for 802.15.4
sd.bind(("", 0xe8, 0, 0)) 
 
# Block until a single frame is received, up to 255 bytes:
payload, src_addr = sd.recvfrom(255) 
 
# Send the payload back to the source we received it from:
sd.sendto(payload, 0, src_addr)

Note: the maximum size messages returned by recvfrom() varies based on technology. At times it is 72 or 75 bytes, or it might be 84, 100, or even 220 bytes. Keep in mind this socket layer mimics UDP, so reading 72 bytes from the socket when 75 bytes are waiting causes the 3 orphaned bytes to be discarded.

Non-Blocking I/O

# This example gives a simple demonstration of how
# to set and use ZigBee sockets configured for
# non-blocking I/O with select. This application
# echoes packets back to the originator.
# 
# The socket is marked for reading only if
# the payload buffer is empty; if the buffer is
# non-empty then the socket is marked for writing.
# Select is used to arbitrate when a socket is 
# ready to be read or written.
# 
# This example could be easily extended to operate
# on multiple sockets.
# 
 
 
# Include the socket and select modules: 
from socket import *
from select import * 
 
 
# Create the socket, datagram mode, proprietary transport:
sd = socket(AF_XBEE, SOCK_DGRAM, XBS_PROT_TRANSPORT)
# Bind to endpoint 0xe8 (232) for ZB/DigiMesh, but 0x00 for 802.15.4
sd.bind(("", 0xe8, 0, 0))
# Configure the socket for non-blocking operation:
sd.setblocking(0) 
 
 
try:
    # Initialize state variables: 
    payload = ""
    src_addr = () 
 
    # Forever: 
 
    while 1: 
        # Reset the ready lists:
        rlist, wlist = ([], [])
        if len(payload) == 0:
 
 
            # If the payload buffer is empty,
            # add socket to read list: 
            rlist = [sd]
 
        else: 
            # Otherwise, add the socket to the
            # write list: 
            wlist = [sd] 
 
 
        # Block on select: 
        rlist, wlist, xlist = select(rlist, wlist, []) 
 
 
        # Is the socket readable? 
        if sd in rlist: 
            # Receive from the socket: 
            payload, src_addr = sd.recvfrom(72)
            # If the packet was "quit", then quit:
            if payload == "quit": 
                raise Exception, "quit received"
 
        # Is the socket writable? 
        if sd in wlist: 
            # Send to the socket: 
            count = sd.sendto(payload, 0, src_addr)
            # Slice off count bytes from the buffer,
            # useful for if this was a partial write:
            payload = payload[count:] 
 
except Exception, e:
    # upon an exception, close the socket:
    sd.close()
Personal tools
Wiki Editing