package stamp.rf;

import stamp.core.*;

/**
 * This class provides an interface to the XBee
 * Wireless communication module.
 *
 * @author Jon Keinath, www.jonkeinath.com, (jonkeinath@gmail.com)
 * @version 0.5  November 24, 2006
 */
public class XBee
  {
  //------------------------------------CONSTANTS----------------------------
  private static final String CR = "\r";                //0x0D;

  public static final int baud9600  = 3;               //Default Baud
  public static final int baud1200  = 0;               //Standard Bauds
  public static final int baud2400  = 1;               //     |
  public static final int baud4800  = 2;               //     |
  public static final int baud19200 = 4;               //     |
  public static final int baud38400 = 5;               //     |
  public static final int baud57600 = 6;               //     |
                                         //dBm Levels  Default = 4
                                         //XBEE-PRO | XBEE
  public static final int dBm10n10 = 0;  // 10dBm   | -10dBm
  public static final int dBm12n6  = 1;  // 12dBm   | - 6dBm
  public static final int dBm14n4  = 2;  // 14dBm   | - 4dBm
  public static final int dBm16n2  = 3;  // 16dBm   | - 2dBm
  public static final int dBm18n0  = 4;  // 18dBm   |   0dBm

  //--------------------------------VARIABLES--------------------------------
  private static Uart rx, tx;                           //Communication Uarts
  private static int rxPin;                             //rx Communication Pin
  private static int txPin;                             //tx Communication Pin
  private static int myAddr;                            //Node Address in Hex
  private static int destAddr;                          //Destination  in Hex

  private static int guardTime = 20000;                 //Guard Time

  private static int rtsPin;                            //RTS Pin
  private static StringBuffer buff = new StringBuffer(32); //Buffer for Commands

  //------------------------TEMPORARY VARIABLES------------------------------


  private static int data;                              //Data variable
  private static int db;                                //db
  private static int i = 0;                             //i
  private static Timer t = new Timer();                 //Timeout Timer

  /**
   * Creates a XBee Wireless Communication Object.
   *
   * @param rxIn Serial Receive Uart
   * @param txIn Serial Transmitter Uart
   * @param myAddress Current XBee Node Address
   * @param name Name for Current XBee Node
   * @param rtsIn Serial Ready to Send Pin
   */
  public XBee(int txPinIn,int rxPinIn,int myAddress,String name,int rtsIn)
    {
    tx = new Uart(Uart.dirTransmit,                     //Uart for sending data
                       txPinIn,                         //to XBee
                       Uart.dontInvert,
                       Uart.speed9600,
                       Uart.stop1);
    rx = new Uart(Uart.dirReceive,                      //Uart for receiving
                       rxPinIn,                         //data from XBee
                       Uart.dontInvert,
                       Uart.speed9600,
                       Uart.stop1);
    rtsPin = rtsIn;                                     //Ready To Send Pin
    guardTime = getGuardTime() * 100;                   //Read Guard Time
    setAddr((byte)myAddress);                           //Set Address of XBee
    enableRTS();                                        //Enable RTS control
    rxPin = rxPinIn;                                    //Receive Pin
    txPin = txPinIn;                                    //Transmit Pin
    setIdentify(name);                                  //Identifier of Node
    }

  /**
   * Creates a XBee Wireless Communication Object.
   *
   * @param rxIn Serial Receive Uart
   * @param txIn Serial Transmitter Uart
   * @param myAddress Current XBee Node Address
   * @param name Name for Current XBee Node
   */
  public XBee(int txPinIn,int rxPinIn,int myAddress,String name)
    {
    tx = new Uart(Uart.dirTransmit,                     //Uart for sending data
                       txPinIn,                         //to XBee
                       Uart.dontInvert,
                       Uart.speed9600,
                       Uart.stop1);
    rx = new Uart(Uart.dirReceive,                      //Uart for receiving
                       rxPinIn,                         //data from XBee
                       Uart.dontInvert,
                       Uart.speed9600,
                       Uart.stop1);
    setGuardTime(5);
    setAddr((byte)myAddress);                           //Set Address of XBee
    rxPin = rxPinIn;                                    //Receive Pin
    txPin = txPinIn;                                    //Transmit Pin
    setIdentify(name);                                  //Identifier of Node
    }

  /**
   * Sends a String.
   *
   * @param toSend a string to send
   */
  public void send(String toSend)
    {
    tx.sendString(toSend);
    }

  /**
   * Sends a string to a specific Node
   *
   * @param toSend a string to send
   * @param destNode Destination Address
   */
  public void send(String toSend,int destNode)
    {
    if (destNode != destAddr) setDestNode(destNode);
    send(toSend);                                    //use generic send method
    }

  /**
   * Sends a StringBuffer.
   *
   * @param toSend a StringBuffer to send
   */
  public void send(StringBuffer toSend)
    {
    for (int i = 0; i < toSend.length(); i++)        //Send String Buffer
      tx.sendByte((char)toSend.charAt(i));           //Split into bytes
    }

  /**
   * Sends a Int
   *
   * @param toSend an Int to send
   */
  public void send(int toSend)
    {
    buff.clear();
    buff.append(toSend);
    send(buff);
    }

  /**
   * Sends a StringBuffer to a specific Node.
   *
   * @param toSend a StringBuffer to send
   * @param destNode Destination Address
   */
  public void send(StringBuffer toSend,int destNode)
    {
    if (destNode != destAddr) setDestNode(destNode); //change destnation if
    send(toSend);                                    //                required
    }

  /**
   * Set the Guard Time for entering Command Mode.
   *
   * @param time Guard Time
   */
  public void setGuardTime(int time)
    {
    enterCmd();
    send("ATGT ");
    send(time);
    send(CR);
    waitOK();
    exitCmd();
    guardTime = time * 100;
    }

  /**
   * Set the destination Node of the XBee.
   *
   * @param node Node Address see Xbee Docs for Values
   */
   public void setDestNode(int node)
    {
    enterCmd();
    send("ATDL ");
    send(node);
    send(CR);
    waitOK();
    exitCmd();
    }

  /**
   * Change communication speed of XBee.
   *
   * @param XbeeBaud 0-6, see CONSTANTS Above
   * @param uartBaud "Uart.speed#####"
   */
   public void setCommSpeed(int XbeeBaud, int uartBaud)
    {
    enterCmd();
    send("ATBD ");
    send(XbeeBaud);
    send(CR);
    waitOK();
    exitCmd();
    tx.restart(Uart.dirTransmit,txPin,Uart.dontInvert,uartBaud,Uart.stop1);
    rx.restart(Uart.dirReceive ,rxPin,Uart.dontInvert,uartBaud,Uart.stop1);
    }

  /**
   * setPacketSize for Transmission
   *
   * @param packetSize See XBee docs for details
   */
   public void setPackets(int packetSize)
    {
    if (packetSize < 0xFF)
      {
      enterCmd();
      send("ATRO ");
      send(packetSize);
      send(CR);
      waitOK();
      exitCmd();
      }
    }

  /**
   * Discover nodes in network.
   *
   * Returns info to debug screen (for now)
   */
   public void discover()
    {
    enterCmd();
    send("ATND");
    send(CR);
    System.out.println("\nDISCOVERING NODES...");
    while (rx.byteAvailable())
      {
      data = (char)rx.receiveByte();
      if (data == 0x0D ) System.out.print("\n");
      else System.out.print((char)data);
      }
    exitCmd();
    System.out.println("\n...DISCOVERY COMPLETE");
    }

  /**
   * Set Power Level for Transmission.
   *
   * @param level 0-4, see vars above for details
   */
  public void setPowerLevel(int level)
    {
    enterCmd();
    send("ATPL");
    send(level);
    send(CR);
    waitOK();
    exitCmd();
    }

  /**
   * Get RF Level of last Received Byte
   *
   * @return RF Level of last Received Byte
   */
  public int getRFLevel()
    {
    enterCmd();
    send("ATDB");
    send(CR);
    db = 0;
    data = rx.receiveByte();
    while (isDigit(data))
      {
      db = 10 * db + (data - '0');
      data = rx.receiveByte();
      }
    exitCmd();
    return db;
    }

  /**
   * Save settings into nonvolital memory for use on next startup.
   *
   */
  public void save()
    {
    enterCmd();
    send("ATWR");
    send(CR);
    waitOK();
    exitCmd();
    }

  /**
   * Set XBee back to default values.
   *
   */
  public void defaults()
    {
    enterCmd();
    send("ATRE");
    send(CR);
    waitOK();
    exitCmd();
    guardTime = getGuardTime() * 100;
    }

  /**
   * Get Guard Time from the XBee.
   *
   * @return The Guard Time Value
   */
  public int getGuardTime()
    {
    enterCmd();
    send("ATGT");
    send(CR);
    db = 0;
    data = rx.receiveByte();
    while (isDigit(data))
      {
      db = 10 * db + (data - '0');
      data = rx.receiveByte();
      }
    exitCmd();
    return db;
    }

  /**
   * Check if a byte is waiting in the RX Uart buffer.
   *
   * @return True if byte is available
   */
  public boolean byteAvailable()
    {
    return rx.byteAvailable();
    }

  /**
   * Wait for a Value and Return it.
   *
   * @return byte received via XBee and rx
   */
  public int rxByte()
    {
    return ((int)rx.receiveByte());
    }
//==========================PRIVATE METHODS BELOW==============================
  /**
   * Set Identify of XBee Node.
   *
   * @param name Node Name for XBee
   */
  private void setIdentify(String name)
    {
    enterCmd();
    send("ATNI ");
    send(name);
    send(CR);
    waitOK();
    exitCmd();
    }

  /**
   * Enable RTS mode on XBee.
   *
   */
  private void enableRTS()
    {
    enterCmd();
    send("ATD6 1");
    send(CR);
    waitOK();
    exitCmd();
    }

  /**
   * Set Node Address on XBee.
   *
   * @param address Node Address for XBee
   */
  private void setAddr(int address)
    {
    enterCmd();
    send("ATMY ");
    send(address);
    send(CR);
    waitOK();
    exitCmd();
    }

  /**
   * Enter Command Mode on XBee.
   *
   */
  private void enterCmd()
    {
    CPU.delay(guardTime);                       //Command Mode Guard Time
    send("+++");                       //Enter Command Mode
    CPU.delay(guardTime);                       //Command Mode Guard Time
    waitOK();                                   //wait for XBee to send 'OK'
    }
  /**
   * Exit Command Mode on XBee.
   *
   */
  private void exitCmd()
    {
    send("ATCN");                     //Exit Command Mode String
    send(CR);                         //<CR>
    waitOK();
    }

  /**
   * Test if character is a digit.
   *
   * @param ch Character to be tested
   * @return True if ch is a digit
   */
  public static boolean isDigit(int ch)
    {
    return ch<='F' && ch>='0';                     //between 0 and 15
    }

   /**
   * Wait for an 'OK' from the XBee
   *
   */
  private static void waitOK()
    {
    t.mark();                                      //Clear timer for timeout chk
    while (rx.receiveByte() != 'O' | t.timeout( 8000) ); // wait for 'O'
    while (rx.receiveByte() != 'K' | t.timeout(16000) ); // wait for 'K'
    if (!t.timeout(16000)) rx.receiveByte();             // remove <CR>
    if (t.timeout(16000)) System.out.println("waitOK TIMEOUT");
    }

  }// End Class
