Kyle Lemons
/
XBeeLib
XBee.hpp
- Committer:
- etherealflaim
- Date:
- 2010-12-01
- Revision:
- 2:2a826741387f
- Parent:
- 1:eefaa22e4b2c
File content as of revision 2:2a826741387f:
#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_data[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) { #if _XBEE_DEBUG > 0 m_usb.printf("Resetting XBee...\r\n"); #endif // 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 #if _XBEE_DEBUG > 0 m_usb.printf("XBee is %s\r\n", (m_ison)?"ON":"SLEEP"); #endif 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 */