package lejos.nxt.comm; import java.util.*; import javax.bluetooth.DeviceClass; import javax.bluetooth.DiscoveryListener; import javax.bluetooth.RemoteDevice; import lejos.nxt.SystemSettings; import lejos.util.Delay; /** * Provides Bluetooth communications. * Allows inbound and outbound connections. * Provides access to to device registration. */ public class Bluetooth extends NXTCommDevice { public static final byte MSG_BEGIN_INQUIRY = 0; public static final byte MSG_CANCEL_INQUIRY = 1; public static final byte MSG_CONNECT = 2; public static final byte MSG_OPEN_PORT = 3; public static final byte MSG_LOOKUP_NAME = 4; public static final byte MSG_ADD_DEVICE = 5; public static final byte MSG_REMOVE_DEVICE = 6; public static final byte MSG_DUMP_LIST = 7; public static final byte MSG_CLOSE_CONNECTION = 8; public static final byte MSG_ACCEPT_CONNECTION = 9; public static final byte MSG_PIN_CODE = 10; public static final byte MSG_OPEN_STREAM = 11; public static final byte MSG_START_HEART = 12; public static final byte MSG_HEARTBEAT = 13; public static final byte MSG_INQUIRY_RUNNING = 14; public static final byte MSG_INQUIRY_RESULT = 15; public static final byte MSG_INQUIRY_STOPPED = 16; public static final byte MSG_LOOKUP_NAME_RESULT = 17; public static final byte MSG_LOOKUP_NAME_FAILURE = 18; public static final byte MSG_CONNECT_RESULT = 19; public static final byte MSG_RESET_INDICATION = 20; public static final byte MSG_REQUEST_PIN_CODE = 21; public static final byte MSG_REQUEST_CONNECTION = 22; public static final byte MSG_LIST_RESULT = 23; public static final byte MSG_LIST_ITEM = 24; public static final byte MSG_LIST_DUMP_STOPPED = 25; public static final byte MSG_CLOSE_CONNECTION_RESULT = 26; public static final byte MSG_PORT_OPEN_RESULT = 27; public static final byte MSG_SET_DISCOVERABLE = 28; public static final byte MSG_CLOSE_PORT = 29; public static final byte MSG_CLOSE_PORT_RESULT = 30; public static final byte MSG_PIN_CODE_ACK = 31; public static final byte MSG_SET_DISCOVERABLE_ACK = 32; public static final byte MSG_SET_FRIENDLY_NAME = 33; public static final byte MSG_SET_FRIENDLY_NAME_ACK = 34; public static final byte MSG_GET_LINK_QUALITY = 35; public static final byte MSG_LINK_QUALITY_RESULT = 36; public static final byte MSG_SET_FACTORY_SETTINGS = 37; public static final byte MSG_SET_FACTORY_SETTINGS_ACK = 38; public static final byte MSG_GET_LOCAL_ADDR = 39; public static final byte MSG_GET_LOCAL_ADDR_RESULT = 40; public static final byte MSG_GET_FRIENDLY_NAME = 41; public static final byte MSG_GET_DISCOVERABLE = 42; public static final byte MSG_GET_PORT_OPEN = 43; public static final byte MSG_GET_FRIENDLY_NAME_RESULT = 44; public static final byte MSG_GET_DISCOVERABLE_RESULT = 45; public static final byte MSG_GET_PORT_OPEN_RESULT = 46; public static final byte MSG_GET_VERSION = 47; public static final byte MSG_GET_VERSION_RESULT = 48; public static final byte MSG_GET_BRICK_STATUSBYTE_RESULT = 49; public static final byte MSG_SET_BRICK_STATUSBYTE_RESULT = 50; public static final byte MSG_GET_BRICK_STATUSBYTE = 51; public static final byte MSG_SET_BRICK_STATUSBYTE = 52; public static final byte MSG_GET_OPERATING_MODE = 53; public static final byte MSG_SET_OPERATING_MODE = 54; public static final byte MSG_OPERATING_MODE_RESULT = 55; public static final byte MSG_GET_CONNECTION_STATUS = 56; public static final byte MSG_CONNECTION_STATUS_RESULT = 57; public static final byte MSG_GOTO_DFU_MODE = 58; public static final byte MSG_ANY = -1; public static final byte BT_PENDING_INPUT = 1; public static final byte BT_PENDING_OUTPUT = 2; public static final String PIN = "lejos.bluetooth_pin"; static final int BUFSZ = 256; private static final byte CHANCNT = 4; private static final byte RS_OFF = -2; private static final byte RS_INIT = -1; private static final byte RS_IDLE = 0; private static final byte RS_CMD = 1; private static final byte RS_WAIT = 2; private static final byte RS_REPLY = 3; private static final byte RS_REQUESTCONNECT = 4; private static final byte RS_ERROR = 5; private static final byte IO_TIME = 100; private static final byte CMD_TIME = 50; private static final short TO_SWITCH = 500; private static final short TO_REPLY = 250; private static final short TO_CHECK = 500; private static final short TO_SHORT = 2000; private static final short TO_LONG = 30000; private static final short TO_RESET = 5000; private static final byte TO_CLOSE = 100; private static final byte TO_FORCERESET = -1; private static final byte TO_NONE = 0; private static final short TO_FLUSH = 500; private static final byte TO_SWITCH_WAIT = 75; private static final byte CN_NONE = -1; private static final int CN_IDLE = 0x7ffffff; private static final byte IS_IDLE = 0; private static final byte IS_RUNNING = 1; private static final byte IS_COMPLETE = 2; private static final byte IS_CANCELED = 3; static BTConnection [] Chans = new BTConnection[CHANCNT]; static byte [] cmdBuf = new byte[128]; static byte [] replyBuf = new byte[256]; static int cmdTimeout; static int reqState = RS_OFF; static int savedState; static boolean listening = false; static int connected; static int resetCnt; static boolean powerOn; static boolean publicPowerOn = false; private static byte [] pin; static final Object sync = new Object(); static String cachedName; static String cachedAddress; static byte inquiryState = IS_IDLE; static boolean notifyToken = false; /** * Low-level method to write BT data * * @param buf the buffer to send * @param off the offset to start the write from. * @param len the number of bytes to send * @return number of bytes actually written */ public static native int btWrite(byte[] buf, int off, int len); /** * Low-level method to read BT data * * @param buf the buffer to read data into * @param off the offset at which to start the transfer * @param len the number of bytes to read * @return number of bytes actually read */ public static native int btRead(byte[] buf, int off, int len); /** *Low-Level method to access the Bluetooth interface. Bitwise values returned. * @return 0 No data pending * 0x1 input pending * 0x2 output pending */ public static native int btPending(); /** * Low-level method to switch BC4 chip between command * and data (stream) mode. * * @param mode 0=data mode, 1=command mode */ public static native void btSetArmCmdMode(int mode); /** * Low-level method to get the BC4 chip mode * * @return the current mode */ public static native int btGetBC4CmdMode(); /** * Low-level method to take the BC4 reset line low */ public static native void btSetResetLow(); /** * Low-level method to take the BC4 reset line high */ public static native void btSetResetHigh(); /** * Low-level method to send a BT command or data * * @param buf the buffer to send * @param len the number of bytes to send */ public static native void btSend(byte[] buf, int len); /** * Low-level method to receive BT replies or data * * @param buf the buffer to receive data in */ public static native void btReceive(byte[] buf); public static native void btEnable(); public static native void btDisable(); /** * Prevent users from instantiating this (all static members). */ private Bluetooth() { } private static void cmdInit(int cmd, int len, int param1, int param2) { // Helper function. Setup a simple command in the buffer ready to go. cmdBuf[0] = (byte)len; cmdBuf[1] = (byte)cmd; cmdBuf[2] = (byte)param1; cmdBuf[3] = (byte)param2; } private static void startTimeout(int period) { // Start a command timeout cmdTimeout = (int)System.currentTimeMillis() + period; } private static boolean checkTimeout() { return (cmdTimeout > 0 && (int)System.currentTimeMillis() > cmdTimeout); } private static void cancelTimeout() { cmdTimeout = -1; } /** * The main Bluetooth control thread. This controls access to the Bluetooth * interface. It controls and performs all low level access to the device. * Switches it between data channels and command streams as required. */ static class BTThread extends Thread { static final int MO_STREAM = 0; static final int MO_CMD = 1; static final int MO_UNKNOWN = -1; private int mode; private int curChan; public BTThread() { mode = MO_CMD; reqState = RS_INIT; curChan = CN_NONE; resetCnt = 0; btEnable(); for(int i = 0; i < CHANCNT; i++) Chans[i] = new BTConnection(i); connected = 0; listening = false; cancelTimeout(); // Load the pin etc. loadSettings(); reset(); setDaemon(true); start(); // Setup initial state powerOn = false; setPower(true); setOperatingMode((byte)1); closePort(); cachedName = getFriendlyName(); cachedAddress = getLocalAddress(); } private void sendCommand() { // Command should be all setup and ready to go in cmdBuf int checkSum = 0; int len = (int)cmdBuf[0] & 0xff; //1 RConsole.print("send cmd " + (int)cmdBuf[1] + "\n"); for(int i=0;i<len;i++) { checkSum += cmdBuf[i+1]; } checkSum = -checkSum; cmdBuf[len+1] = (byte) ((checkSum >> 8) & 0xff); cmdBuf[len+2] = (byte) checkSum; cmdBuf[0] = (byte)(len + 2); btWrite(cmdBuf,0, len+3); } private int recvReply() { // Read a reply and place it in replyBuf if (checkTimeout()) return -1; int cnt = Bluetooth.btRead(replyBuf, 0, 1); if (cnt <= 0) return 0; int len = (int)replyBuf[0] & 0xff; if (len < 3 ||len >= replyBuf.length) { //1 RConsole.print("Bad packet len " + len + " cnt " + cnt + "\n"); return -1; } int timeout = (int)System.currentTimeMillis() + TO_REPLY; while (cnt < len+1) { cnt += Bluetooth.btRead(replyBuf, cnt, len + 1 - cnt); if ((int)System.currentTimeMillis() > timeout) { //1 RConsole.print("recvReply timeout\n"); return -1; } } int csum = len; len -= 2; for(int i = 0; i < len; i++) csum += (int)replyBuf[i+1] & 0xff; csum = -csum; //1 RConsole.print("Got reply " + replyBuf[1] + "\n"); if (((byte) csum == replyBuf[len+2]) && ((byte)(csum >> 8) == replyBuf[len+1])) return len; else { //1 RConsole.print("Bad csum\n"); return -1; } } /** * Perform a hardware reset of the BlueCore chip. * */ private void reset() { synchronized(Bluetooth.sync) { int len; //RConsole.print("hardware reset\n"); for(int resetCnt = 0; resetCnt < 2; resetCnt++) { // Ditch any pending data in the input buffer startTimeout(TO_FLUSH); while (!checkTimeout()) { recvReply(); } // RConsole.print("End of flush\n"); // BC4 reset seq. First take the reset line low... btSetArmCmdMode(MO_CMD); btSetResetLow(); // Keep it that way for 100ms and discard any input startTimeout(100); while (!checkTimeout()) { recvReply(); } // Now bring it high btSetResetHigh(); // Now wait for either 5 seconds or for a RESET_INDICATION startTimeout(TO_RESET); while ((len = recvReply()) == 0 || ( len > 0 && replyBuf[1] != MSG_RESET_INDICATION)) Thread.yield(); //1 if (len < 0) RConsole.print("Reset timed out"); // Check things are working //1 RConsole.print("Send mode cmd\n"); cmdInit(MSG_GET_OPERATING_MODE, 1, 0, 0); sendCommand(); startTimeout(TO_SHORT); while ((len = recvReply()) == 0 || (len > 0 && replyBuf[1] != MSG_OPERATING_MODE_RESULT)) Thread.yield(); //1 if (len < 0) RConsole.print("mode had timed out\n"); // if we got the response without a timeout we are done! if (len > 0) break; } // We are now in command mode mode = MO_CMD; // Now reset everything else that is going on gulp! for(int i = 0; i < CHANCNT; i++) Chans[i].reset(); //1 RConsole.print("reset complete state is " + reqState + "\n"); listening = false; connected = 0; curChan = CN_NONE; cancelTimeout(); // Tell anyone that is waiting if (reqState > RS_IDLE) reqState = RS_ERROR; Bluetooth.sync.notifyAll(); resetCnt++; } } private void processReply() { // Read and process and command replies from the bc4 // If the reply buffer is free, look to see if we have a new reply synchronized(Bluetooth.sync) { //RConsole.print("processn reply " + reqState + "\n"); int len; while (reqState < RS_REPLY && (len = recvReply()) != 0) { //RConsole.print("process request\n"); // Got a message. We only deal with the messages we have to deal // with here. In general we allow the calling thread to decide // what to do (this includes ignoring the message!). //RConsole.print("got request " + (int)replyBuf[1] + " state " + reqState + "\n"); if (len < 0 || replyBuf[1] == MSG_RESET_INDICATION) { //1 RConsole.print("Got reply error\n"); reset(); break; } else if (replyBuf[1] == MSG_CLOSE_CONNECTION_RESULT) { if (replyBuf[3] >= 0 && replyBuf[3] < Chans.length) { if (Chans[replyBuf[3]].disconnected()) connected--; if (replyBuf[3] == (byte)curChan) curChan = CN_NONE; } } else if (replyBuf[1] == MSG_REQUEST_CONNECTION) { if (listening) { // Push the current state savedState = reqState; reqState = RS_REQUESTCONNECT; } else { // No one wants to know so reject it. // Note: Doing this seems to cause a device reset! cmdInit(MSG_ACCEPT_CONNECTION, 2, 0, 0); sendCommand(); continue; } } else if (replyBuf[1] == MSG_REQUEST_PIN_CODE) { // If we have no pin then nothing to do if (pin == null) continue; // Otherwise send the pin as requested cmdInit(MSG_PIN_CODE, 24, 0, 0); System.arraycopy(replyBuf, 2, cmdBuf, 2, 7); for(int i = 0; i < 16; i++) cmdBuf[i + 9] = (i < pin.length ? pin[i] : 0); sendCommand(); continue; } else if (replyBuf[1] == MSG_PIN_CODE_ACK) continue; // All other messages give the caller it to deal with if (reqState == RS_WAIT) reqState = RS_REPLY; if (reqState >= RS_REPLY) Bluetooth.sync.notifyAll(); } } //RConsole.print("process reply end\n"); } private void processCommands() { // Process commands. Return when we should consider switching to // stream mode. //RConsole.print("Process cmd1\n"); switchToCmd(); int cmdEnd = (int)System.currentTimeMillis() + CMD_TIME; while (cmdEnd > (int)System.currentTimeMillis() || reqState > RS_IDLE) { //RConsole.print("ProcessCommands state " + reqState + "\n"); synchronized(Bluetooth.sync) { if (reqState == RS_CMD) { // Have a command ready to go so send it sendCommand(); reqState = RS_WAIT; } processReply(); } Thread.yield(); } //RConsole.print("Process cmd end\n"); } private int selectChan() { // Select the next channel to be processed if (connected == 0) return -1; int i; int cur = curChan; for(i = 0; i < Chans.length; i++) { cur = (cur + 1) % Chans.length; if (Chans[cur].needsAttention()) { // if (cur != curChan) RConsole.print("Selected " + cur + "\n"); return cur; } } // No active channel found say we are idle return CN_IDLE; } private void processStreams() { // Process the streams. Return when we should switch to command mode // RConsole.print("PS cur " + curChan + " state " + reqState + "\n"); while (true) { synchronized(Bluetooth.sync) { //RConsole.print("Process streams " + reqState + "\n"); if (reqState != RS_IDLE) return; int next = selectChan(); if (next < 0) return; if (next != CN_IDLE) { if (!switchToStream(next)) return; // Perform I/O on the current stream. Switching from one stream // to another is a slow process, so we spend at least IO_TIME ms // on each stream before switching away. //RConsole.print("Process streams 2" + reqState + "\n"); int ioEnd = (int)System.currentTimeMillis() + IO_TIME; while (ioEnd > (int)System.currentTimeMillis() && Chans[curChan].state >= BTConnection.CS_CONNECTED) { if (bc4Mode() != MO_STREAM) return; Chans[curChan].send(); Chans[curChan].recv(); Thread.yield(); } } else { //1 RConsole.print("Stream idle\n"); if (bc4Mode() != mode) return; } //RConsole.print("Process streams 3" + reqState + "\n"); // Do we need to switch back to command mode? if (listening) return; } Thread.yield(); } } private int waitSwitch(int target, boolean flush) { // Wait for the BC4 to switch to mode, or timeout... int timeout = (int) System.currentTimeMillis() + TO_SWITCH; while (timeout > (int)System.currentTimeMillis()) { if (bc4Mode() == target) return target; // Need to flush input when switching to command mode if (flush && curChan >= 0) Chans[curChan].flushInput(); } //RConsole.print("Failed to switch\n"); mode = MO_UNKNOWN; curChan = CN_NONE; return bc4Mode(); } private boolean switchToStream(int chan) { // Decide which (if any) stream to switch to if (mode == MO_STREAM && chan == curChan) return true; switchToCmd(); //RConsole.print("Switch to chan " + chan + " handle " + Chans[chan].handle + "\n"); cmdInit(MSG_OPEN_STREAM, 2, Chans[chan].handle, 0); sendCommand(); // Now wait for the BC4 to switch if (waitSwitch(MO_STREAM, false) != MO_STREAM) return false; //RConsole.print("In stream mode\n"); // Make sure we process any remaining command input that may have arrived processReply(); // Finally switch the ARM over btSetArmCmdMode(MO_STREAM); mode = MO_STREAM; curChan = chan; return true; } private void switchToCmd() { // Need to switch back into command mode // First step send any pending data if (mode == MO_CMD) return; //RConsole.println("switch to cmd " + btPending() + " " + bc4Mode()); if (mode == MO_STREAM && bc4Mode() == MO_CMD && curChan >= 0) { //1 RConsole.print("Trying early flush\n"); Chans[curChan].flushInput(); } // wait for any output to drain. If this times out we are probably // in big trouble and heading for a reset. int timeout = (int) System.currentTimeMillis() + TO_FLUSH; while(((btPending() & BT_PENDING_OUTPUT) != 0) && (timeout > (int)System.currentTimeMillis())) Thread.yield(); //RConsole.println("Flush complete " + btPending()); if ((btPending() & BT_PENDING_OUTPUT) != 0) { // Failed to flush, reset and give up. reset(); return; } // Need to have a minimum period of no output to the BC4 Delay.msDelay(TO_SWITCH_WAIT); btSetArmCmdMode(MO_CMD); // If there is any input data left we could be in trouble. Try and // flush everything. if (waitSwitch(MO_CMD, true) != MO_CMD) { //RConsole.print("Failed to switch to cmd\n"); reset(); return; } mode = MO_CMD; } private int bc4Mode() { // return the current mode of the BC4 chip int ret = btGetBC4CmdMode(); // > 512 indicates a high logic level which is mode 0! if (ret > 512) return MO_STREAM; else return MO_CMD; } private void waitInit() { synchronized (Bluetooth.sync) { reqState = RS_INIT; processCommands(); reqState = RS_IDLE; Bluetooth.sync.notifyAll(); } } public void run() { //1 RConsole.print("Thread running\n"); waitInit(); //1 RConsole.print("Init complete\n"); while(true) { processCommands(); processStreams(); Thread.yield(); } } } private static BTThread btThread = null; static private int waitState(int target) { // RConsole.print("Wait state " + target + "\n"); synchronized (Bluetooth.sync) { // Initialize if we need to if (reqState == RS_OFF) btThread = new BTThread(); // Wait for the system to enter the specified state (or timeout) while (reqState != target && reqState != RS_ERROR) try{Bluetooth.sync.wait();}catch(Exception e){} if (reqState == RS_ERROR) return -1; else return 0; } } private static void cmdStart() { // Wait for the system to be idle. Ignore timeout errors. while (waitState(RS_IDLE) < 0) try{Bluetooth.sync.wait();}catch(Exception e){} } private static void cmdComplete() { // command now complete. Reset to idle (clears any timeout state) synchronized(Bluetooth.sync) { reqState = RS_IDLE; cancelTimeout(); Bluetooth.sync.notifyAll(); } } private static int cmdWait(int state, int waitState, int msg, int timeout) { //1 RConsole.print("Cmd wait\n"); synchronized (Bluetooth.sync) { // Check we have power if not fail the request if (!powerOn) return -1; if (waitState > 0) reqState = waitState; if (timeout != TO_NONE) startTimeout(timeout); while (true) { if (waitState(state) < 0) return -1; if (msg == MSG_ANY || replyBuf[1] == msg) return 0; // Ignore any unwanted message if (reqState == RS_REPLY) reqState = RS_WAIT; } } } /** * Set the pin to be used for pairing/connecting to the system * * @param newPin the new pin code * */ public static void setPin(byte[] newPin) { pin = newPin; } /** * Return the pin to be used for pairing/connecting to the system * * @return The current pin code * */ public static byte[] getPin() { return pin; } /** * Close an open connection * * @param handle the handle for the connection * @return the status 0 = success */ public static int closeConnection(byte handle) { int ret = -1; //1 RConsole.print("Close connection state " + reqState + "\n"); synchronized (Bluetooth.sync) { cmdStart(); // We can have a race condition when both ends of the connection // close at the same time. This can mean we try and close an already // closed connection. If we do this the BC4 goes into reset mode. // To try and avoid this happening we insert a different length // delay between outbound and inbound streams. We then wait to see // if the other end has already closed things... int timeout = (handle == 3 ? 5*TO_CLOSE : TO_CLOSE); reqState = RS_WAIT; try{Bluetooth.sync.wait(timeout);}catch(Exception e){} reqState = RS_IDLE; // There is a small chance that we may have had a reset so make sure // that we still have an open channel to close! byte [] status = getConnectionStatus(); //1 RConsole.print("Conn status " + status[handle]); if (status == null || status[handle] != 2) return -1; if (Chans[handle].state != BTConnection.CS_DISCONNECTING) return -1; cmdInit(MSG_CLOSE_CONNECTION, 2, handle, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_CLOSE_CONNECTION_RESULT, TO_SHORT) >= 0) ret = (int)replyBuf[2]; do { // We may have a race condition here, or have triggered a reset // wait for things to settle reqState = RS_WAIT; try{Bluetooth.sync.wait(TO_REPLY);}catch(Exception e){} reqState = RS_IDLE; } while (getConnectionStatus() == null); cmdComplete(); return ret; } } /** * Opens the port to allow incoming connections. * * @return an array of three bytes: success, handle, ps_success */ public static byte[] openPort() { byte[] result = new byte[3]; synchronized (Bluetooth.sync) { cmdStart(); cmdInit(MSG_OPEN_PORT, 1, 0, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_PORT_OPEN_RESULT, TO_SHORT) < 0) result = null; else System.arraycopy(replyBuf, 2, result, 0, 3); //1 RConsole.print("Port open handle " + (int)replyBuf[3] + " status " + (int)replyBuf[2] + "\n"); cmdComplete(); return result; } } /** * Closes the port to disallow incoming connections. * * @return an array of two bytes: success, ps_success */ public static byte [] closePort() { byte [] result = new byte[2]; //1 RConsole.print("Close port\n"); synchronized (Bluetooth.sync) { cmdStart(); // The Lego doc says the handle should always be 3! cmdInit(MSG_CLOSE_PORT, 2, 3, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_CLOSE_PORT_RESULT, TO_SHORT) < 0) result = null; else System.arraycopy(replyBuf, 2, result, 0, 2); cmdComplete(); return result; } } /** * Wait for a remote device to connect. * @param timeout time in ms to wait for connection, 0 == wait for ever * @param mode the I/O mode to be used for this connection. <code>NXTConnection.RAW, .LCP, or .PACKET</code> * @param pin the pin to use, null use current default * @return a BTConnection */ public static BTConnection waitForConnection(int timeout, int mode, byte[] pin) { //1 RConsole.print("waitForConnection\n"); synchronized (Bluetooth.sync) { BTConnection ret = null; // only one listener at once if (listening) return null; // First open up the listening port byte [] port = openPort(); if (port == null || port[0] != 1 || port[1] >= Chans.length || port[1] < 0) { //1 RConsole.print("Failed to open port\n"); return null; } // Now in listening mode listening = true; byte []savedPin = null; if (pin != null) { savedPin = getPin(); setPin(pin); } if (timeout == 0) timeout = 0x7fffffff; // Wait for special connect indication while (listening && reqState != RS_REQUESTCONNECT) { try{ Bluetooth.sync.wait(timeout < 1000 ? timeout : 1000); } catch(InterruptedException e){listening=false;} timeout -= 1000; if (timeout <= 0) listening = false; } if (listening) { //1 RConsole.print("Got connect request\n"); // !Kludge Alert! Extract address here: byte [] addr = new byte[ADDRESS_LEN]; System.arraycopy(replyBuf, 2, addr, 0, ADDRESS_LEN); //RConsole.print("waitForConnect() Address: " + Bluetooth.addressToString(addr) + "\n"); // Restore state reqState = savedState; // and wait until we have control cmdStart(); // Acknowledge the request cmdInit(MSG_ACCEPT_CONNECTION, 2, 1, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECT_RESULT, TO_LONG) >= 0) { //1 RConsole.print("Connect result " + (int)replyBuf[2] + " handle " + (int)replyBuf[3] + "\n"); if (replyBuf[2] == 1) { byte handle = replyBuf[3]; // Got a connection if (handle >= 0 && handle < Chans.length) { // Assert(Chans[handle].state == CS_IDLE); Chans[handle].bind(handle, addressToString(addr), mode); // now have one more connected connected++; ret = Chans[handle]; } } } listening = false; cmdComplete(); } if (savedPin != null) setPin(savedPin); closePort(); return ret; } } /** * Uses the current default PIN * @return the Bluetooth connection */ public static BTConnection waitForConnection() { return waitForConnection(0, 0, null); } /** * Uses the current default PIN * @param timeout time in ms to wait for connection, 0 == wait for ever * @param mode the I/O mode to be used for this connection. <code>NXTConnection.RAW, .LCP, or .PACKET</code> * @return the Bluetooth connection */ public static BTConnection waitForConnection(int timeout, int mode) { return waitForConnection(timeout, mode, null); } /** * Connects to a remote device. Uses the current default pin. * * @param remoteDevice remote device * @return BTConnection Object or null */ public static BTConnection connect(RemoteDevice remoteDevice) { if (remoteDevice == null) return null; return connect(stringToAddress(remoteDevice.getDeviceAddr())); } /** * Connect to the specified device, either by name or address * @param target String name or address * @param mode I/O mode for this connection. <code>NXTConnection.RAW, .LCP, or .PACKET</code> * @param pin The pin to use for this connection * @return BTConnection object or null */ public static BTConnection connect(String target, int mode, byte[] pin) { if (target == null) return null; if (isAddress(target)) return connect(stringToAddress(target), mode, pin); else { // We have a device name. Try and locate it in the list of known // devices RemoteDevice dev = getKnownDevice(target); if (dev != null) // Found a match connect to it return connect(stringToAddress(dev.getBluetoothAddress()), mode, pin); return null; } } /** * Connect to the specified device, either by name or address * @param target String name or address * @param mode I/O mode for this connection. <code>NXTConnection.RAW, .LCP, or .PACKET</code> * @return BTConnection object or null */ public static BTConnection connect(String target, int mode) { return connect(target, mode, null); } /** * Connects to a Device by it's Byte-Device-Address Array * Uses the current default pin * * @param device_addr byte-Array with device-Address * @return BTConnection Object or null */ private static BTConnection connect(byte[] device_addr) { return connect(device_addr, 0, null); } /** * Connects to a Device by it's Byte-Device-Address Array * * @param device_addr byte-Array with device-Address * @param mode The data mode. Either PACKET, LCP, or RAW found in <code>NXTConnection</code> * @param pin the pin to use. Must be ASCII code, so '0' = 48 * @return BTConnection Object or null */ private static BTConnection connect(byte[] device_addr, int mode, byte[] pin) { //1 RConsole.print("Connect\n"); synchronized(Bluetooth.sync) { BTConnection ret = null; byte[] savedPin = null; if (pin != null) { savedPin = getPin(); setPin(pin); } cmdStart(); cmdInit(MSG_CONNECT, 8, 0, 0); System.arraycopy(device_addr, 0, cmdBuf, 2, ADDRESS_LEN); if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECT_RESULT, TO_LONG) >= 0) { //1 RConsole.print("Connect result " + (int)replyBuf[2] + " handle " + (int)replyBuf[3] + "\n"); if (replyBuf[2] != 0) { byte handle = replyBuf[3]; // Now need to check that the connection is not closed imm reqState = RS_WAIT; try{Bluetooth.sync.wait(300);}catch(Exception e){} if (reqState == RS_WAIT && handle >= 0 && handle < Chans.length) { // Got a connection Chans[handle].bind(handle, addressToString(device_addr), mode); // now have one more connected connected++; ret = Chans[handle]; } } } cmdComplete(); if (savedPin != null) setPin(savedPin); return ret; } } /** * Get the Bluetooth signal strength (link quality) * Higher values mean stronger signal. * * @param handle The handle/channel of the connection * @return link quality value 0 to 255. * */ public static int getSignalStrength(byte handle) { //1 RConsole.print("getSignalStrength\n"); synchronized (Bluetooth.sync) { int ret = -1; cmdStart(); if (Chans[handle].state != BTConnection.CS_CONNECTED) return -1; cmdInit(MSG_GET_LINK_QUALITY, 2, handle, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_LINK_QUALITY_RESULT, TO_SHORT) >= 0) ret = replyBuf[2] & 0xff; cmdComplete(); return ret; } } /** * Get the friendly name of the local device * @return the friendly name */ public static String getFriendlyName() { byte[] result = new byte[NAME_LEN]; //1 RConsole.print("getFriendlyName\n"); synchronized (Bluetooth.sync) { // If we have one return the cached address, saves switching into // command mode, or powering up the device... cmdStart(); if (cachedName == null) { cmdInit(MSG_GET_FRIENDLY_NAME, 1, 0, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_FRIENDLY_NAME_RESULT, TO_SHORT) < 0) result = null; else { System.arraycopy(replyBuf, 2, result, 0, NAME_LEN); cachedName = nameToString(result); } } cmdComplete(); return cachedName; } } /** * Set the name of the local device * @param strName the friendly name for the device * @return true if ok false if there is an error */ public static boolean setFriendlyName(String strName) { //1 RConsole.print("setFriendlyName\n"); synchronized (Bluetooth.sync) { boolean ret=false; cmdStart(); cmdInit(MSG_SET_FRIENDLY_NAME, 17, 0, 0); byte[] name = stringToName(strName); System.arraycopy(name, 0, cmdBuf, 2, NAME_LEN); if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_FRIENDLY_NAME_ACK, TO_LONG) >= 0) ret = true; cmdComplete(); if (ret) cachedName = nameToString(name); return ret; } } /** * get the Bluetooth address of the local device * @return the local address */ public static String getLocalAddress() { byte[] result = new byte[ADDRESS_LEN]; //1 RConsole.print("getLocalAddress\n"); synchronized (Bluetooth.sync) { // If we have one return the cached address... Saves switching // into command mode. cmdStart(); if (cachedAddress == null) { cmdInit(MSG_GET_LOCAL_ADDR, 1, 0, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_LOCAL_ADDR_RESULT, TO_SHORT) < 0) result = null; else { System.arraycopy(replyBuf, 2, result, 0, ADDRESS_LEN); cachedAddress = addressToString(result); } } cmdComplete(); return cachedAddress; } } /** * The internal Chip has a list of already paired Devices. This Method returns a * Vector-List which contains all the known Devices on the List. These need not be reachable. * To connect to a "not-known"-Device, you should use the Inquiry-Process. * The pairing-Process can also be done with the original Lego-Firmware. The List of known * devices will not get lost, when installing the LeJOS Firmware. * * @return Vector with List of known Devices */ public static Vector getKnownDevicesList() { //1 RConsole.print("getKnownDevicesList\n"); synchronized(Bluetooth.sync) { int state = RS_CMD; byte[] device = new byte[ADDRESS_LEN]; byte[] devclass = new byte[4]; byte[] name = new byte[NAME_LEN]; Vector retVec = new Vector(1); RemoteDevice curDevice; cmdStart(); cmdInit(MSG_DUMP_LIST, 1, 0, 0); while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0) { state = RS_WAIT; if (replyBuf[1] == MSG_LIST_ITEM) { System.arraycopy(replyBuf, 2, device, 0, ADDRESS_LEN); System.arraycopy(replyBuf, 9, name, 0, NAME_LEN); System.arraycopy(replyBuf, 25, devclass, 0, 4); curDevice = new RemoteDevice(nameToString(name), addressToString(device), devclass); //1 RConsole.print("got name " + curDevice.getFriendlyName() + "\n"); retVec.addElement(curDevice); } else if (replyBuf[1] == MSG_LIST_DUMP_STOPPED) break; } cmdComplete(); return retVec; } } /** * Gets a Device of the BC4-Chips internal list of known Devices * (those who have been paired before) into the BTDevice Object. * @param fName Friendly-Name of the device * @return BTDevice Object or null, if not found. */ public static RemoteDevice getKnownDevice(String fName) { RemoteDevice btd = null; //look the name up in List of Known Devices Vector devList = getKnownDevicesList(); if (devList.size() > 0) { for (int i = 0; i < devList.size(); i++) { btd = (RemoteDevice) devList.elementAt(i); if (btd.getFriendlyName(false).equals(fName)) { return btd; } } } return null; } /** * Add device to known devices * @param d Remote Device * @return true if add was successful */ public static boolean addDevice(RemoteDevice d) { String addr = d.getDeviceAddr(); String name = d.getFriendlyName(false); byte[] cod = d.getDeviceClass(); //1 RConsole.print("addDevice " + name + "\n"); synchronized (Bluetooth.sync) { boolean ret=false; cmdStart(); cmdInit(MSG_ADD_DEVICE, 28, 0, 0); System.arraycopy(stringToAddress(addr), 0, cmdBuf, 2, ADDRESS_LEN); System.arraycopy(cod, 0, cmdBuf, 25, 4); System.arraycopy(stringToName(name), 0, cmdBuf, 9, NAME_LEN); if (cmdWait(RS_REPLY, RS_CMD, MSG_LIST_RESULT, TO_LONG) >= 0) ret = replyBuf[2] == 0x50; cmdComplete(); return ret; } } /** * Remove device from known devices * @param d Remote Device * @return true if remove was successful */ public static boolean removeDevice(RemoteDevice d) { String addr = d.getDeviceAddr(); synchronized (Bluetooth.sync) { boolean ret = false; cmdStart(); cmdInit(MSG_REMOVE_DEVICE, 8, 0, 0); System.arraycopy(stringToAddress(addr), 0, cmdBuf, 2, ADDRESS_LEN); if (cmdWait(RS_REPLY, RS_CMD, MSG_LIST_RESULT, TO_LONG) >= 0) ret = replyBuf[2] == 0x53; cmdComplete(); return ret; } } /** * Cancel a Bluetooth inquiry process that has been started using startInquire * @return true if the request is cancelled false if there is an error. */ public static boolean cancelInquiry() { synchronized (Bluetooth.sync) { // Are we running an inquiry? if (inquiryState != IS_RUNNING) return false; // Make sure we are in a "safe" state to issue the cancel while (reqState != RS_WAIT && inquiryState == IS_RUNNING) try {Bluetooth.sync.wait(1);}catch(Exception e){} // Make sure we are still doing the inquiry if (inquiryState != IS_RUNNING) return false; // Issue the cancel command and return, the state will be reset to // RS_WAIT once the command has been sent. cmdInit(MSG_CANCEL_INQUIRY, 1, 0, 0); reqState = RS_CMD; // Mark the inquiry as being aborted inquiryState = IS_CANCELED; return true; } } /** * Start a Bluetooth inquiry process and notify listener of each device found. This method * is primarily used by DiscoveryAgent.startInquiry() and is run in a separate thread. * * @param maxDevices the maximum number of devices to discover * @param timeout the timeout value in units of 1.28 seconds * @param listy The listener to notify */ /* * DEV NOTES: * This method contains some redundant code in inquire(int, int, byte []). * It seemed better to use redundant code rather than try to amalgamate these methods because * the menu system does not use any javax.bluetooth classes (other than RemoteDevice) and * therefore the linker will cut this method out, resulting in more efficient memory use. */ public static void inquireNotify(int maxDevices, int timeout, final DiscoveryListener listy) { byte [] cod = {0,0,0,0}; // find all devices byte[] device = new byte[ADDRESS_LEN]; byte[] name = new byte[NAME_LEN]; byte[] retCod = new byte[4]; synchronized (Bluetooth.sync) { int state = RS_CMD; cmdStart(); cmdInit(MSG_BEGIN_INQUIRY, 8, maxDevices, 0); cmdBuf[4] = (byte)timeout; System.arraycopy(cod, 0, cmdBuf, 5, 4); inquiryState = IS_RUNNING; while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0) { state = RS_WAIT; if (replyBuf[1] == MSG_INQUIRY_RESULT) { System.arraycopy(replyBuf, 2, device, 0, ADDRESS_LEN); System.arraycopy(replyBuf, 9, name, 0, NAME_LEN); System.arraycopy(replyBuf, 25, retCod, 0, 4); int codRecord = (retCod[0] << 8) + retCod[1]; codRecord = (codRecord << 8) + retCod[2]; codRecord = (codRecord << 8) + retCod[3]; final DeviceClass deviceClass = new DeviceClass(codRecord); final RemoteDevice rd = new RemoteDevice(nameToString(name), addressToString(device), retCod); // Spawn a separate thread to notify in case doesn't return immediately: Thread t = new Thread() { public void run() { // Make sure only one notify thread is running at a time using notifyToken while(notifyToken) {Thread.yield();} notifyToken = true; // indicate this notify is executing if(inquiryState != IS_CANCELED) listy.deviceDiscovered(rd, deviceClass); notifyToken = false; // when notify returns set back to false } }; // TODO: Set Daemon? t.setDaemon(true); t.start(); } else if (replyBuf[1] == MSG_INQUIRY_STOPPED) { if (inquiryState == IS_RUNNING) inquiryState = IS_COMPLETE; break; } } // At this stage we have completed the inquiry. inquiryState will // tell us how we have completed things... // IS_RUNNING: Some sort of error (typically a reset). // IS_CANCELED: Inquiry was aborted. // IS_COMPLETE: Normal completion. // Spawn a separate thread to notify in case doesn't return immediately: Thread t; if (inquiryState == IS_COMPLETE) t = new Thread() { public void run() { listy.inquiryCompleted(DiscoveryListener.INQUIRY_COMPLETED); } }; else if (inquiryState == IS_CANCELED) t = new Thread() { public void run() { listy.inquiryCompleted(DiscoveryListener.INQUIRY_TERMINATED); } }; else t = new Thread() { public void run() { listy.inquiryCompleted(DiscoveryListener.INQUIRY_ERROR); } }; // TODO: Daemon thread? t.setDaemon(true); t.start(); inquiryState = IS_IDLE; cmdComplete(); } } /** * Start a Bluetooth inquiry process * * @param maxDevices the maximum number of devices to discover * @param timeout the timeout value in units of 1.28 seconds * @param cod the class of device to look for * @return a vector of all the devices found */ public static Vector inquire(int maxDevices, int timeout, byte[] cod) { Vector retVec = new Vector(); byte[] device = new byte[ADDRESS_LEN]; byte[] name = new byte[NAME_LEN]; byte[] retCod = new byte[4]; synchronized (Bluetooth.sync) { int state = RS_CMD; cmdStart(); cmdInit(MSG_BEGIN_INQUIRY, 8, maxDevices, 0); cmdBuf[4] = (byte)timeout; System.arraycopy(cod, 0, cmdBuf, 5, 4); while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0) { state = RS_WAIT; if (replyBuf[1] == MSG_INQUIRY_RESULT) { System.arraycopy(replyBuf, 2, device, 0, ADDRESS_LEN); System.arraycopy(replyBuf, 9, name, 0, NAME_LEN); System.arraycopy(replyBuf, 25, retCod, 0, 4); // add the Element to the Vector List retVec.addElement(new RemoteDevice(nameToString(name), addressToString(device), retCod)); } else if (replyBuf[1] == MSG_INQUIRY_STOPPED) { cmdComplete(); // Fill in the names for (int i = 0; i < retVec.size(); i++) { RemoteDevice btrd = ((RemoteDevice) retVec.elementAt(i)); String s = btrd.getFriendlyName(false); if (s.length() == 0) { String nm = lookupName(btrd.getDeviceAddr()); btrd.setFriendlyName(nm); } } return retVec; } } cmdComplete(); return retVec; } } /** * Look up the name of a device using its address * * @param addr device address * @return friendly name of device */ public static String lookupName(String addr) { char[] name = new char[NAME_LEN]; synchronized (Bluetooth.sync) { String ret = ""; int state = RS_CMD; cmdStart(); cmdInit(MSG_LOOKUP_NAME, 8, 0, 0); System.arraycopy(stringToAddress(addr), 0, cmdBuf, 2, ADDRESS_LEN); while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0) { state = RS_WAIT; if (replyBuf[1] == MSG_LOOKUP_NAME_RESULT) { int len = 0; for(; len < NAME_LEN && replyBuf[len+9] != 0; len++) name[len] = (char)replyBuf[len+9]; ret = new String(name, 0, len); break; } else if (replyBuf[1] == MSG_LOOKUP_NAME_FAILURE) break; } cmdComplete(); return ret; } } /** * Get the status of all connections * * @return byte array of status for each handle */ public static byte[] getConnectionStatus() { byte[] result = new byte[4]; //1 RConsole.print("getConnectionStatus\n"); synchronized (Bluetooth.sync) { cmdStart(); cmdInit(MSG_GET_CONNECTION_STATUS, 1, 0, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECTION_STATUS_RESULT, TO_SHORT) < 0) result = null; else System.arraycopy(replyBuf, 5, result, 0, 4); cmdComplete(); return result; } } /** * Get the major and minor version of the BlueCore code * * @return an array of two bytes: major version, minor version */ public static byte[] getVersion() { byte [] version = new byte[2]; //1 RConsole.print("getVersion\n"); synchronized (Bluetooth.sync) { cmdStart(); cmdInit(MSG_GET_VERSION, 1, 0, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_VERSION_RESULT, TO_SHORT) < 0) version = null; else System.arraycopy(replyBuf, 2, version, 0, 2); cmdComplete(); return version; } } /** * Get the persistent status value from the BC4 chip * * @return the byte value */ public static int getStatus() { //1 RConsole.print("getStatus\n"); synchronized (Bluetooth.sync) { int ret = -1; cmdStart(); cmdInit(MSG_GET_BRICK_STATUSBYTE, 1, 0, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_BRICK_STATUSBYTE_RESULT, TO_SHORT) >= 0) ret = ((int)replyBuf[2] & 0xff) | (((int)replyBuf[3] & 0xff) << 8); cmdComplete(); return ret; } } /** * Set the persistent status byte for the BC4 chip * * @param status the byte status value * @return < 0 Error */ public static int setStatus(int status) { //1 RConsole.print("setStatus\n"); synchronized (Bluetooth.sync) { int ret = -1; cmdStart(); cmdInit(MSG_SET_BRICK_STATUSBYTE, 3, status & 0xff, (status >> 8) & 0xff); if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_BRICK_STATUSBYTE_RESULT, TO_SHORT) >= 0) ret = 0; cmdComplete(); return ret; } } /** * Get the visibility (discoverable) status of the device * * @return 1 = visible, 0 = invisible */ public static int getVisibility() { //1 RConsole.print("getVisibility\n"); synchronized (Bluetooth.sync) { int ret = -1; cmdStart(); cmdInit(MSG_GET_DISCOVERABLE, 1, 0, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_DISCOVERABLE_RESULT, TO_SHORT) >= 0) ret = replyBuf[2]; cmdComplete(); return ret; } } /** * Get the port open status, * i.e whether connections are being accepted * * @return 1 if the port is open, 0 otherwise */ public static int getPortOpen() { //1 RConsole.print("getPortOpen\n"); synchronized (Bluetooth.sync) { int ret = -1; cmdStart(); cmdInit(MSG_GET_PORT_OPEN, 1, 0, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_PORT_OPEN_RESULT, TO_SHORT) >= 0) ret = replyBuf[2]; cmdComplete(); return ret; } } /** * Get the operating mode (stream breaking or not) * * @return 0 = stream breaking mode, 1 = don't break stream mode * < 0 Error */ public static int getOperatingMode() { //1 RConsole.print("getOperatingMode\n"); synchronized (Bluetooth.sync) { int ret = -1; cmdStart(); cmdInit(MSG_GET_OPERATING_MODE, 1, 0, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_OPERATING_MODE_RESULT, TO_SHORT) >= 0) ret = replyBuf[2]; cmdComplete(); return ret; } } /** * Set Bluetooth visibility (discoverable) on or off for the local device * * @param visible true to set visibility on, false to set it off * @return < 0 error */ public static int setVisibility(byte visible) { //1 RConsole.print("setVisibility\n"); synchronized (Bluetooth.sync) { int ret = -1; cmdStart(); cmdInit(MSG_SET_DISCOVERABLE, 2, visible, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_DISCOVERABLE_ACK, TO_SHORT) >= 0) ret = 0; cmdComplete(); return ret; } } /** * Reset the settings of the BC4 chip to the factory defaults. * The NXT should be restarted after this. * * @return 0 if ok < 0 if error */ public static int setFactorySettings() { //1 RConsole.print("setFactorySettings\n"); synchronized (Bluetooth.sync) { int ret = -1; cmdStart(); cmdInit(MSG_SET_FACTORY_SETTINGS, 1, 0, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_FACTORY_SETTINGS_ACK, TO_SHORT) >= 0) ret = 0; cmdComplete(); return ret; } } /** * Set the operating mode * * @param mode 0 = Stream breaking, 1 don't break stream * @return < 0 error */ public static int setOperatingMode(byte mode) { //1 RConsole.print("setOperatingMode\n"); synchronized (Bluetooth.sync) { int ret = -1; cmdStart(); cmdInit(MSG_SET_OPERATING_MODE, 2, mode, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_OPERATING_MODE_RESULT, TO_SHORT) >= 0) ret = 0; cmdComplete(); return ret; } } /** * Force a reset of the Bluetooth module. * Notes: * After this call power will be on. * Any existing connections will be closed * Any listening threads will be aborted * */ public static void reset() { synchronized (Bluetooth.sync) { cmdStart(); // Force a timeout and hence a reset cmdWait(RS_REPLY, RS_WAIT, MSG_RESET_INDICATION, TO_FORCERESET); cmdComplete(); } } private static int checkDevice() { synchronized (Bluetooth.sync) { int ret = -1; cmdStart(); cmdInit(MSG_GET_OPERATING_MODE, 1, 0, 0); if (cmdWait(RS_REPLY, RS_CMD, MSG_OPERATING_MODE_RESULT, TO_CHECK) >= 0) ret = replyBuf[2]; cmdComplete(); return ret; } } /** * Set the power to the module * * @param on power on or off */ public static void setPower(boolean on) { synchronized (Bluetooth.sync) { if (powerOn == on) return; if (on) { btSetResetHigh(); powerOn = true; // Now make sure things have settled for(int i =0; i < 5; i++) if (checkDevice() >= 0) break; } else { // Powering off. Do we need to reset things? boolean wasListening = listening; // Wait for any other commands to complete cmdStart(); if (connected > 0 || listening) reset(); // Wait for the listening thread to exit if (wasListening) try{Bluetooth.sync.wait(2000);}catch(Exception e){} //1 RConsole.print("Power going off\n"); btSetResetLow(); powerOn = false; } publicPowerOn = powerOn; } } /** * Return the current state of the module power * * @return power on or off */ public static boolean getPower() { synchronized(Bluetooth.sync) { return publicPowerOn; } } public static int getResetCount() { return resetCnt; } public static void loadSettings() { // Retrieve default PIN from System properties String pinStr = SystemSettings.getStringSetting(PIN, "1234"); byte [] defaultPin = new byte[pinStr.length()]; pin = defaultPin; for(int i=0;i<pinStr.length();i++) pin[i] = (byte)pinStr.charAt(i); } /** * The following are provided for compatibility with the old Bluetooth * class. They should not be used, in new programs and should probably * be removed. */ /** * Read a packet from the stream. Do not block and for small packets * (< 254 bytes), do not return a partial packet. * @param buf Buffer to read data into. * @param len Number of bytes to read. * @return > 0 number of bytes read. * other values see read. */ public static int readPacket(byte buf[], int len) { return Chans[3].readPacket(buf, len); } /** * Send a data packet. * Must be in data mode. * @param buf the data to send * @param bufLen the number of bytes to send */ public static void sendPacket(byte [] buf, int bufLen) { Chans[3].sendPacket(buf, bufLen); } /** * Set the BC4 mode, and wait for that mode to be confirmed by the chip. * * @param mode the requested mode 1 == Command mode 0 == Stream mode */ public static void btSetCmdMode(int mode) { } /** * Class to provide polymorphic access to the connection methods. * Gets returned as a singleton by getConnector and can be used to create * connections. */ static class Connector extends NXTCommConnector { /** * Open a connection to the specified name/address using the given I/O mode * @param target The name or address of the device/host to connect to. * @param mode The I/O mode to use for this connection * @return A NXTConnection object for the new connection or null if error. */ public NXTConnection connect(String target, int mode) { return Bluetooth.connect(target, mode); } /** * Wait for an incoming connection, or for the request to timeout. * @param timeout Time in ms to wait for the connection to be made * @param mode I/O mode to be used for the accepted connection. * @return A NXTConnection object for the new connection or null if error. */ public NXTConnection waitForConnection(int timeout, int mode) { return Bluetooth.waitForConnection(timeout, mode); } } static NXTCommConnector connector = null; /** * Provides access to the singleton connection object. * This object can be used to create new connections. * @return the connector object */ public static NXTCommConnector getConnector() { if (connector == null) connector = new Connector(); return connector; } }