Kyle Lemons
/
XBeeLib
Diff: XBee.hpp
- Revision:
- 0:86ff0a55c978
- Child:
- 1:eefaa22e4b2c
diff -r 000000000000 -r 86ff0a55c978 XBee.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/XBee.hpp Tue Nov 30 21:28:18 2010 +0000 @@ -0,0 +1,401 @@ +#ifndef XBEE_HPP +#define XBEE_HPP + +#include <queue> +#include <cctype> +#include <string> +#include <vector> +#include <cstring> + +#include "mbed.h" +#include "handler.hpp" + +#define PRINT(c) ((isprint(c))?(c):('.')) + +/** This class encapsulates a simplistic XBee packet. + * 1. The packet is 0x01 delimited. + * 2. The packet has a source and destination, which are (nominally) 8-byte identifiers + */ +class XBeePacket +{ +private: + string m_data; + int m_data_len; + string m_source; + string m_dest; + bool m_packed; + +public: + /// Construct an empty (invalid) XBeePacket + inline XBeePacket() + : m_data(""), m_data_len(0), m_source("_INVALID"), m_dest("********"), m_packed(false) + { + } + + /// Create an XBeePacket with the given data + inline XBeePacket(const string data) + : m_data(data), m_data_len(data.length()), m_source("_INVALID"), m_dest("********"), m_packed(false) + { + } + + /// Create an XBeePacket with the given source and data + inline XBeePacket(const string src, const string data) + : m_data(data), m_data_len(data.length()), m_source(src), m_dest("********"), m_packed(false) + { + } + + /// Create an XBeePacket with the given source, destination, and data + inline XBeePacket(const string src, const string dst, const string data) + : m_data(data), m_data_len(data.length()), m_source(src), m_dest(dst), m_packed(false) + { + } + + /// Append the given data to the packet + inline void append(const string data) + { + m_data += data; + m_data_len += data.length(); + } + + /// Get the packet source (if set or unpacked) + inline string source() { return m_source; } + + /// Set the packet source (before packing) + inline void source(string src) { m_source = src; } + + /// Get the packet destination (if set or unpacked) + inline string dest() { return m_dest; } + + /// Set the packet destination (before packing) + inline void dest(string dst) { m_dest = dst; } + + /// Get the packet data (if set or unpacked) + inline string data() { return m_data; } + + /// Set the packet data (before packing) + inline void data(string dat) { m_data = dat; } + + /// Get a vector of the 0x01-delimited pieces of the data (after unpacking) + inline vector<string> split_data() + { + vector<string> pieces; + int begin = 0; + int end = 0; + while (end < m_data_len && (end = m_data.find(0x01, begin)) != string::npos) + { + pieces.push_back( m_data.substr(begin, end-begin) ); + begin = end+1; + } + pieces.push_back( m_data.substr(begin) ); + return pieces; + } + + /// Clear the packet (zero the length, and reset the source and destination) + inline void clear() + { + m_data = ""; + m_data_len = 0; + m_source = "_CLEARED"; + m_dest = "********"; + } + + /** Pack the packet with header, etc. + * This function can be called separately to include the headers in, e.g., a dump() + */ + inline void pack() + { + string header = "iHvZ\001" + m_source + "\001" + m_dest + "\001"; + m_data = header + m_data; + m_data_len += header.length(); + m_packed = true; + } + + /** Unpack the packet header + * This function should be called with received packets to extract the header + */ + inline bool unpack() + { + if (m_data.substr(0, 5) != "iHvZ\001") return false; + int source_idx = m_data.find(0x01, 0) + 1; + int dest_idx = m_data.find(0x01, source_idx) + 1; + int data_idx = m_data.find(0x01, dest_idx) + 1; + if (source_idx == string::npos || + dest_idx == string::npos || + data_idx == string::npos) return false; + m_source = m_data.substr(source_idx, dest_idx - source_idx - 1); + m_dest = m_data.substr(dest_idx, data_idx - dest_idx - 1); + m_data = m_data.substr(data_idx); + m_data_len = m_data.length(); + return true; + } + + /// Send the packet on the given Serial port (automatically packs if unpacked) + inline void send(Serial &out) + { + if (!m_packed) pack(); + + out.puts(m_data.c_str()); + out.putc('\n'); + } + + /// Dump a text version (call before packing) of the packet to the serial port + inline void dump(Serial &out) + { + out.printf("[%s] -> [%s] : [", m_source.c_str(), m_dest.c_str()); + for (int i = 0; i < m_data_len; ++i) + { + char c = m_dest[i]; + if (isprint(c)) + out.putc(c); + else + out.printf("\\x%02X", c); + } + out.printf("] %d bytes\r\n", m_data_len); + } + + /// If it is a broadcast packet (destionation ********) return true + inline bool is_broadcast() { return m_dest == "********"; } +}; + +/** Manage the pins connected to an XBee chip in transparent (broadcast) mode. + * This is an interface for communicating XBeePackets across the XBee chip in transparent mode. + * Receipt of a new packet can be either polled or notified via the attach() functions. + */ +class XBee +{ +private: + /* Unit ID */ + string m_uid; + + /* Pins */ + PinName m_xout, m_xin, m_xrstn, m_xassoc, m_xon; + + /* Pin handlers */ + DigitalOut m_resetn; + DigitalIn m_ison; + Serial m_comm; + + /* USB logging */ + Serial m_usb; + + /* Tickers and Timers */ + Timeout m_timeout; + + /* Packet incoming queue */ + queue<XBeePacket> m_queue; + handler<void> *m_read_handler; + +public: + /** Create an XBee chip connect + * uid - The unit ID of the XBee chip (used in all outgoing XBeePackets) + * xbee_din - The MBED pin connected to the XBEE DIN pin + * xbee_dout - The MBED pin connected to the XBEE DOUT pin + * xbee_rst - The MBED pin connected to the XBEE nRST pin + * xbee_on - The MBED pin connected to the XBEE ON/nSLEEP pin + */ + inline XBee(string uid, PinName xbee_din, PinName xbee_dout, PinName xbee_rst, PinName xbee_on) + : m_uid(uid), m_xout(xbee_din), m_xin(xbee_dout), m_xrstn(xbee_rst), m_xon(xbee_on), + m_resetn(xbee_rst), m_ison(xbee_on), m_comm(xbee_din, xbee_dout), + m_usb(USBTX, USBRX), m_read_handler(NULL) + { + m_usb.printf("Resetting XBee...\r\n"); + // Reset the XBEE module + m_resetn = 0; + wait(.1); + m_resetn = 1; + wait(1); + + // Set up serial communications (9600 baud by default) + m_comm.baud(9600); + + // Set up a handler for serial communications + m_comm.attach(this, &XBee::rxirq); + + // Check sleep status + m_usb.printf("XBee is %s\r\n", (m_ison)?"ON":"SLEEP"); + + setup(); + } + + inline void broadcast(string data) + { + XBeePacket pkt(data); + pkt.source(m_uid); + pkt.send(m_comm); + } + + inline void send(string to, string data) + { + XBeePacket pkt(data); + pkt.source(m_uid); + pkt.dest(to); + pkt.send(m_comm); + } + +private: + + inline void setup() + { + #if _XBEE_DEBUG > 0 + m_usb.printf("Setting up XBee...\r\n"); + #endif + + // Turns out, we can only really use this in broadcast mode. + //commandMode(); + //command("VR"); + //command("AP", 1); + //command("CN"); + //wait(1); + //m_usb.printf("Setup Complete.\r\n"); + + m_timeout.attach(this, &XBee::tickirq, .1); + } + + /** Enter command mode. Send command("CN") to exit command mode. */ + inline void commandMode() + { + wait(1.1); + m_comm.printf("+++"); + wait(1.1); + } + + inline void command(const char *cmd) + { + m_comm.printf("AT%s\r", cmd); + wait(.001); + } + + inline void command(const char *cmd, int arg) + { + m_comm.printf("AT%s%X\r", cmd, arg); + wait(.001); + } + + inline void rxirq() + { + static XBeePacket nextPacket; + static char buffer[256]; + char *p = buffer; + bool finished = false; + + // Receive all characters in buffer + do + { + // Receive character + *p = m_comm.getc(); + + // Check if it's end-of-packet + if (*p == '\n') { + finished = true; + break; + } + + // Move on to the next character + ++p; + } while (m_comm.readable() && p-buffer < 255); + + // Null terminate + *p = '\0'; + + // Append the characters to the packet + nextPacket.append(buffer); + + if (finished) + { + // Unpack the headers + if (!nextPacket.unpack()) { + m_usb.printf("Received garbled packet; dropping."); + return; + } + + if (nextPacket.is_broadcast() || nextPacket.dest() == m_uid) + { + #if _XBEE_DEBUG > 0 + // Print the packet + m_usb.printf("Received: "); + nextPacket.dump(m_usb); + #endif + + // Add it to the queue + m_queue.push(nextPacket); + } + else + { + #if _XBEE_DEBUG > 0 + m_usb.printf("Dropping: "); + nextPacket.dump(m_usb); + #endif + } + + // Clear the packet + nextPacket = XBeePacket(); + } + } + + inline void tickirq() + { + //m_usb.printf("Scanning...\r\n"); + //XBeePacket pkt = XBeePacket::Command("ND"); + //XBeePacket pkt = XBeePacket::Broadcast("Test"); + //pkt.dump(m_usb); + //pkt.send(m_comm); + //m_comm.printf("TEST\r"); + //XBeePacket pkt("TestPacket"); + //m_usb.printf("Sending: "); + //pkt.dump(m_usb); + //pkt.send(m_comm); + + while (!m_queue.empty() && m_read_handler) + (*m_read_handler)(); + + // Set the timeout again (we aren't going to use a ticker just in case the read handlers take awhile) + m_timeout.attach(this, &XBee::tickirq, 0.1); + } + + +public: + + /// Return whether or not another XBeePacket is ready + inline bool readable() + { + return !m_queue.empty(); + } + + /// Get the next XBeePacket in line (make sure it's readable()!) + inline XBeePacket read() + { + XBeePacket pkt = m_queue.front(); + m_queue.pop(); + return pkt; + } + + /// Set the Unit ID + inline string uid() { return m_uid; } + + /// Get the Unit ID + inline void uid(string uid) { m_uid = uid; } + + /// Attach a member function to be called on all TCP packets + template <class T> + inline void attach(T *inst, void (T::*func)()) + { + delete m_read_handler; + m_read_handler = new member_handler<T,void>(inst, func); + } + + /// Attach a non-member function to be called on all TCP packets + inline void attach(void (*func)()) + { + delete m_read_handler; + m_read_handler = new function_handler<void>(func); + } + + /// Detach the handler + inline void detach() + { + delete m_read_handler; + m_read_handler = NULL; + } +}; + +#endif /* XBEE_HPP */ \ No newline at end of file