I’m going down much the same road with my new widgets as I did with the Wixels- depending on the software loaded and the application, a Widget can be a client or controller. A client can be a locomotive, turnout or any other device that is controlled via servos or discrete outputs. The controler can be a hand-held interface and/or a computer. In my case this is my Raspberry Pi, configured as a web server/web sockets host.
To that end, I coded up a quick Xbee interface for the Raspberry Pi in python that works with the datagrams in my set of train/control widgets. It’s extremely minimalistic (of course) but allows you to send standard ‘AT’ commands to the Xbee and also send advanced API datapackets. This code assumes that you have an Xbee module configured with X-CTU. I’m using the Parallax Xbee USB board to do this. You will need to set the interface baud rate of the Xbee to 38400 baud and place the Xbee in API mode. Once configured, move the Parallax board to the Raspberry Pi and that’s it. Destination Xbee modules (configured as above) will receive this data (based on it’s destination address of course) and pass this out the serial port to the host. In this case, the host is (will be) my train widget boards. Once they are available that is, perhaps early March (I hope)
Anyhow, you can now control Train Widgets with the following code or adapt it to your own xbee design. XbeeTransmitDataFrame is the primary control interface. All servo, sound and sensor data uses this fixed length datagram. It’s compact and fast and offers 65K of possible command codes (I’ve implemented 3 so far).
xbeeSendDataQuery is used to do a standard ‘AT’ command using the API mode. For example, you can pass ‘M’ and ‘Y’ to this method and it will return the 16 bit node address of the Xbee. Any of the other AT commands can be used as well but you will have to get the data using serial.read() and know where in the packet the return value and how long it is.
xbeeTransmitDataFrame is used to send the 16 byte packets I’ve made up for my implementation. You can see how these are constructed from the C structures below.
import serial
class xbeeController:
def __init__(self):
usbPort = '/dev/ttyUSB0'
self.sp = serial.Serial(usbPort, 38400)
def xbeeReturnResult(self, datalength):
return(self.sp.read(datalength))
def xbeeDataQuery(self, cmdh, cmdl):
frame = []
c0 = ord(cmdl)
c1 = ord(cmdh)
frame.append(0x7e) # header
frame.append(0) # our data is always fixed size
frame.append(4) # this is all data except header, length and checksum
frame.append(0x08) # AT COMMAND - send Query to Xbee module
frame.append(0x52) # frame ID for ack- 0 = disable
frame.append(c1) # Command high character
frame.append(c0) # low character
frame.append(0) # zero checksum location
cks = 0;
for i in range(3,7): # compute checksum
cks = cks + frame[i]
i = (255-cks) & 0x00ff
frame[7] = i # and put it in the message
for i in range(0,8): # send it out the serial port to the xbee
self.sp.write(chr(frame[i]))
def xbeeTransmitDataFrame(self, dest, data):
frame = []
frame.append(0x7e) # header
frame.append(0) # our data is always fixed size
frame.append(21) # this is all data except header, length and checksum
frame.append(0x01) # TRANSMIT REQUEST - send Query to Xbee module
frame.append(0) # frame ID for ack- 0 = disable
frame.append( (dest>>8) & 0x00ff) # Destination address
frame.append( (dest & 0xff))
frame.append(0) # disable ack for fastest transmission
for i in data: # move data to transmit buffer
frame.append(i)
frame.append(0) # checksum position
cks = 0;
for i in range(3,25): # compute checksum
cks += frame[i]
i = (255-cks) & 0x00ff
frame[24] = i
for i in range(0,25): # send it out the serial port to the xbee
self.sp.write(chr(frame[i]))
This is the xbee header information that is in the Widgets. This defines the three control packets I’m sending with the Xbee. These are the 16 bit fixed length packets that ride in the transport area of the above datagram. By keeping them small, I’m getting a 7ms packet transmit time which is very fast.
#define CONTROLLER 0x0000 // XbeeCTRLPacket{}
#define RFIDCOMMAND 0x0010 // XbeeRFIDPacket{}
#define SOUNDCOMMAND 0x0011 // XbeeSOUNDPacket{}
typedef struct
{
uint16_t destinationAddress; // Xbee destination address for this client
uint16_t sourceAddress; // This node's address
uint16_t commandID; // 16 bit command ID
uint16_t Sound0; // 16 Bits of Sound Triggers
uint16_t Sound1;
uint16_t Sound2;
uint16_t Sound3;
uint16_t Sound4;
} XbeeSOUNDPacket;
typedef struct
{
uint16_t destinationAddress; // Xbee destination address for this client
uint16_t sourceAddress; // This node's address
uint16_t commandID; // 16 bit command ID
uint16_t RFID0; // 12 bytes of ascii RFID info
uint16_t RFID1;
uint16_t RFID2;
uint16_t RFID3;
uint16_t misc1;
} XbeeRFIDPacket;
typedef struct
{
uint16_t destinationAddress; // Xbee destination address for this client
uint16_t sourceAddress; // This node's address
uint16_t commandID; // 16 bit command ID
uint16_t Servo0; // Servo 0
uint16_t Servo1;
uint16_t Servo2;
uint16_t Discrete;
uint16_t misc1;
} XbeeCTRLPacket;