Dependencies:   mbed

Dependents:   HvZ

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