1 package lejos.nxt.comm;
6 * Provides Bluetooth comminications.
7 * Allows inbound and outbound connections.
8 * Provides access to to device registration.
10 public class Bluetooth
12 public static final int MSG_BEGIN_INQUIRY = 0;
13 public static final int MSG_CANCEL_INQUIRY = 1;
14 public static final int MSG_CONNECT = 2;
15 public static final int MSG_OPEN_PORT = 3;
16 public static final int MSG_LOOKUP_NAME = 4;
17 public static final int MSG_ADD_DEVICE = 5;
18 public static final int MSG_REMOVE_DEVICE = 6;
19 public static final int MSG_DUMP_LIST = 7;
20 public static final int MSG_CLOSE_CONNECTION = 8;
21 public static final int MSG_ACCEPT_CONNECTION = 9;
22 public static final int MSG_PIN_CODE = 10;
23 public static final int MSG_OPEN_STREAM = 11;
24 public static final int MSG_START_HEART = 12;
25 public static final int MSG_HEARTBEAT = 13;
26 public static final int MSG_INQUIRY_RUNNING = 14;
27 public static final int MSG_INQUIRY_RESULT = 15;
28 public static final int MSG_INQUIRY_STOPPED = 16;
29 public static final int MSG_LOOKUP_NAME_RESULT = 17;
30 public static final int MSG_LOOKUP_NAME_FAILURE = 18;
31 public static final int MSG_CONNECT_RESULT = 19;
32 public static final int MSG_RESET_INDICATION = 20;
33 public static final int MSG_REQUEST_PIN_CODE = 21;
34 public static final int MSG_REQUEST_CONNECTION = 22;
35 public static final int MSG_LIST_RESULT = 23;
36 public static final int MSG_LIST_ITEM = 24;
37 public static final int MSG_LIST_DUMP_STOPPED = 25;
38 public static final int MSG_CLOSE_CONNECTION_RESULT = 26;
39 public static final int MSG_PORT_OPEN_RESULT = 27;
40 public static final int MSG_SET_DISCOVERABLE = 28;
41 public static final int MSG_CLOSE_PORT = 29;
42 public static final int MSG_CLOSE_PORT_RESULT = 30;
43 public static final int MSG_PIN_CODE_ACK = 31;
44 public static final int MSG_SET_DISCOVERABLE_ACK = 32;
45 public static final int MSG_SET_FRIENDLY_NAME = 33;
46 public static final int MSG_SET_FRIENDLY_NAME_ACK = 34;
47 public static final int MSG_GET_LINK_QUALITY = 35;
48 public static final int MSG_LINK_QUALITY_RESULT = 36;
49 public static final int MSG_SET_FACTORY_SETTINGS = 37;
50 public static final int MSG_SET_FACTORY_SETTINGS_ACK = 38;
51 public static final int MSG_GET_LOCAL_ADDR = 39;
52 public static final int MSG_GET_LOCAL_ADDR_RESULT = 40;
53 public static final int MSG_GET_FRIENDLY_NAME = 41;
54 public static final int MSG_GET_DISCOVERABLE = 42;
55 public static final int MSG_GET_PORT_OPEN = 43;
56 public static final int MSG_GET_FRIENDLY_NAME_RESULT = 44;
57 public static final int MSG_GET_DISCOVERABLE_RESULT = 45;
58 public static final int MSG_GET_PORT_OPEN_RESULT = 46;
59 public static final int MSG_GET_VERSION = 47;
60 public static final int MSG_GET_VERSION_RESULT = 48;
61 public static final int MSG_GET_BRICK_STATUSBYTE_RESULT = 49;
62 public static final int MSG_SET_BRICK_STATUSBYTE_RESULT = 50;
63 public static final int MSG_GET_BRICK_STATUSBYTE = 51;
64 public static final int MSG_SET_BRICK_STATUSBYTE = 52;
65 public static final int MSG_GET_OPERATING_MODE = 53;
66 public static final int MSG_SET_OPERATING_MODE = 54;
67 public static final int MSG_OPERATING_MODE_RESULT = 55;
68 public static final int MSG_GET_CONNECTION_STATUS = 56;
69 public static final int MSG_CONNECTION_STATUS_RESULT = 57;
70 public static final int MSG_GOTO_DFU_MODE = 58;
71 public static final int MSG_ANY = -1;
73 public static final int BT_PENDING_INPUT = 1;
74 public static final int BT_PENDING_OUTPUT = 2;
76 private static final int CHANCNT = 4;
77 private static final int RS_INIT = -1;
78 private static final int RS_IDLE = 0;
79 private static final int RS_CMD = 1;
80 private static final int RS_WAIT = 2;
81 private static final int RS_REPLY = 3;
82 private static final int RS_REQUESTCONNECT = 4;
83 private static final int RS_ERROR = 5;
85 private static final int IO_TIME = 100;
86 private static final int CMD_TIME = 50;
87 private static final int TO_SWITCH = 500;
88 private static final int TO_REPLY = 250;
89 private static final int TO_SHORT = 2000;
90 private static final int TO_LONG = 30000;
91 private static final int TO_RESET = 5000;
92 private static final int TO_INIT = 500;
93 private static final int TO_CLOSE = 100;
94 private static final int TO_FORCERESET = -1;
95 private static final int TO_NONE = 0;
96 private static final int TO_FLUSH = 500;
98 static BTConnection [] Chans = new BTConnection[CHANCNT];
99 static byte [] cmdBuf = new byte[128];
100 static byte [] replyBuf = new byte[256];
101 static int cmdTimeout;
102 static int reqState = RS_INIT;
103 static int savedState;
104 static boolean listening = false;
105 static int connected;
107 static boolean powerOn;
108 static boolean publicPowerOn = false;
109 public final static byte[] defaultPin =
110 {(byte) '1', (byte) '2', (byte) '3', (byte) '4'};
111 private static byte [] pin = defaultPin;
112 static Object sync = new Object();
113 static byte[] cachedName;
114 static byte[] cachedAddress;
117 * Low-level method to write BT data
119 * @param buf the buffer to send
120 * @param off the offset to start the write from.
121 * @param len the number of bytes to send
122 * @return number of bytes actually written
124 public static native int btWrite(byte[] buf, int off, int len);
126 * Low-level method to read BT data
128 * @param buf the buffer to read data into
129 * @param off the offset at which to start the transfer
130 * @param len the number of bytes to read
131 * @return number of bytes actually read
133 public static native int btRead(byte[] buf, int off, int len);
135 *Low-Level method to access the Bluetooth interface. Bitwise values returned.
136 * @return 0 No data pending
140 public static native int btPending();
142 * Low-level method to switch BC4 chip between command
143 * and data (stream) mode.
145 * @param mode 0=data mode, 1=command mode
147 public static native void btSetArmCmdMode(int mode);
150 * Low-level method to get the BC4 chip mode
152 public static native int btGetBC4CmdMode();
155 * Low-level method to start ADC converter
158 public static native void btStartADConverter();
161 * Low-level method to take the BC4 reset line low
163 public static native void btSetResetLow();
166 * Low-level method to take the BC4 reset line high
168 public static native void btSetResetHigh();
172 * Low-level method to send a BT command or data
174 * @param buf the buffer to send
175 * @param len the number of bytes to send
177 public static native void btSend(byte[] buf, int len);
180 * Low-level method to receive BT replies or data
182 * @param buf the buffer to receive data in
184 public static native void btReceive(byte[] buf);
191 private static void cmdInit(int cmd, int len, int param1, int param2)
193 // Helper function. Setup a simple command in the buffer ready to go.
194 cmdBuf[0] = (byte)len;
195 cmdBuf[1] = (byte)cmd;
196 cmdBuf[2] = (byte)param1;
197 cmdBuf[3] = (byte)param2;
200 private static void startTimeout(int period)
202 // Start a command timeout
203 cmdTimeout = (int)System.currentTimeMillis() + period;
206 private static boolean checkTimeout()
208 return (cmdTimeout > 0 && (int)System.currentTimeMillis() > cmdTimeout);
211 private static void cancelTimeout()
217 * The main Bluetooth control thread. This controls access to the Bluetooth
218 * interface. It controls and peforms all low level access to the device.
219 * Switches it between data channels and command streams as required.
221 static class BTThread extends Thread
223 static final int MO_STREAM = 0;
224 static final int MO_CMD = 1;
234 // Make sure power is on(may cause a reset!)
236 for(int i = 0; i < CHANCNT; i++)
237 Chans[i] = new BTConnection(i);
243 // Setup initial state
246 setOperatingMode((byte)1);
248 cachedName = getFriendlyName();
249 cachedAddress = getLocalAddress();
253 private void sendCommand()
255 // Command should be all setup and ready to go in cmdBuf
257 int len = (int)cmdBuf[0] & 0xff;
258 //1 Debug.out("send cmd " + (int)cmdBuf[1] + "\n");
259 for(int i=0;i<len;i++)
261 checkSum += cmdBuf[i+1];
263 checkSum = -checkSum;
264 cmdBuf[len+1] = (byte) ((checkSum >> 8) & 0xff);
265 cmdBuf[len+2] = (byte) checkSum;
266 cmdBuf[0] = (byte)(len + 2);
267 btWrite(cmdBuf,0, len+3);
270 private int recvReply()
272 // Read a reply and place it in replyBuf
273 if (checkTimeout()) return -1;
274 int cnt = Bluetooth.btRead(replyBuf, 0, 1);
275 if (cnt <= 0) return 0;
276 int len = (int)replyBuf[0] & 0xff;
277 if (len < 3 ||len >= replyBuf.length)
279 //1 Debug.out("Bad packet len " + len + " cnt " + cnt + "\n");
282 int timeout = (int)System.currentTimeMillis() + TO_REPLY;
285 cnt += Bluetooth.btRead(replyBuf, cnt, len + 1 - cnt);
286 if ((int)System.currentTimeMillis() > timeout)
288 //1 Debug.out("recvReply timeout\n");
295 for(int i = 0; i < len; i++)
296 csum += (int)replyBuf[i+1] & 0xff;
298 //1 Debug.out("Got reply " + replyBuf[1] + "\n");
299 if (((byte) csum == replyBuf[len+2]) && ((byte)(csum >> 8) == replyBuf[len+1]))
303 //1 Debug.out("Bad csum\n");
309 * Perform a hardware reset of the BlueCore chip.
315 synchronized(Bluetooth.sync)
318 //1 Debug.out("hardware reset\n");
319 for(int resetCnt = 0; resetCnt < 2; resetCnt++)
321 // Ditch any pending data in the input buffer
322 startTimeout(TO_FLUSH);
323 while (!checkTimeout())
327 // Debug.out("End of flush\n");
328 // BC4 reset seq. First take the reset line low...
329 btSetArmCmdMode(MO_CMD);
331 // Keep it that way for 100ms and discard any input
333 while (!checkTimeout())
339 // Now wait for either 5 seconds or for a RESET_INDICATION
340 startTimeout(TO_RESET);
341 while ((len = recvReply()) == 0 || ( len > 0 && replyBuf[1] != MSG_RESET_INDICATION))
343 //1 if (len < 0) Debug.out("Reset timed out");
344 // Check things are working
345 //1 Debug.out("Send mode cmd\n");
346 cmdInit(MSG_GET_OPERATING_MODE, 1, 0, 0);
348 startTimeout(TO_SHORT);
349 while ((len = recvReply()) == 0 || (len > 0 && replyBuf[1] != MSG_OPERATING_MODE_RESULT))
351 //1 if (len < 0) Debug.out("mode had timed out\n");
352 // if we got the response without a timeout we are done!
355 // We are now in command mode
357 // Now reset everything else that is going on gulp!
358 for(int i = 0; i < CHANCNT; i++)
360 //1 Debug.out("reset complete state is " + reqState + "\n");
365 // Tell anyone that is waiting
366 if (reqState > RS_IDLE) reqState = RS_ERROR;
367 Bluetooth.sync.notifyAll();
372 private void processReply()
374 // Read and process and command replies from the bc4
375 // If the reply buffer is free, look to see if we have a new reply
376 synchronized(Bluetooth.sync)
378 //Debug.out("processn reply " + reqState + "\n");
380 while (reqState < RS_REPLY && (len = recvReply()) != 0)
382 //Debug.out("process request\n");
383 // Got a message. We only deal with the messages we have to deal
384 // with here. In general we allow the calling thread to decide
385 // what to do (this includes ignoring the message!).
386 //1 Debug.out("got request " + (int)replyBuf[1] + " state " + reqState + "\n");
387 if (len < 0 || replyBuf[1] == MSG_RESET_INDICATION)
389 //1 Debug.out("Got reply error\n");
393 else if (replyBuf[1] == MSG_CLOSE_CONNECTION_RESULT)
395 if (replyBuf[3] >= 0 && replyBuf[3] < Chans.length)
397 if (Chans[replyBuf[3]].disconnected())
399 if (replyBuf[3] == (byte)curChan) curChan = -1;
402 else if (replyBuf[1] == MSG_REQUEST_CONNECTION)
406 // Push the current state
407 savedState = reqState;
408 reqState = RS_REQUESTCONNECT;
412 // No one wants to know so reject it.
413 // Note: Doing this seems to cause a device reset!
414 cmdInit(MSG_ACCEPT_CONNECTION, 2, 0, 0);
419 else if (replyBuf[1] == MSG_REQUEST_PIN_CODE)
421 // If we have no pin then nothing to do
422 if (pin == null) continue;
423 // Otherwise send the pin as requested
424 cmdInit(MSG_PIN_CODE, 24, 0, 0);
425 System.arraycopy(replyBuf, 2, cmdBuf, 2, 7);
426 for(int i = 0; i < 16; i++)
427 cmdBuf[i + 9] = (i < pin.length ? pin[i] : 0);
430 } else if (replyBuf[1] == MSG_PIN_CODE_ACK)
432 // All other messages give the caller it to deal with
433 if (reqState == RS_WAIT)
435 if (reqState >= RS_REPLY)
436 Bluetooth.sync.notifyAll();
439 //Debug.out("process reply end\n");
442 private void processCommands()
444 // Process commands. Return when we should consider switching to
446 //Debug.out("Process cmd1\n");
448 int cmdEnd = (int)System.currentTimeMillis() + CMD_TIME;
449 while (cmdEnd > (int)System.currentTimeMillis() || reqState > RS_IDLE)
451 //Debug.out("ProcessCommands state " + reqState + "\n");
452 synchronized(Bluetooth.sync)
454 if (reqState == RS_CMD)
456 // Have a command ready to go so send it
464 //Debug.out("Process cmd end\n");
467 private int selectChan()
469 // Select the next channel to be processed
470 if (connected == 0) return -1;
473 for(i = 0; i < Chans.length; i++)
475 cur = (cur + 1) % Chans.length;
476 if (Chans[cur].needsAttention())
478 // if (cur != curChan) Debug.out("Selected " + cur + "\n");
482 // Nothing better found so stick with the current channel
486 private void processStreams()
488 // Process the streams. Return when we should switch to command mode
489 // Debug.out("PS cur " + curChan + " state " + reqState + "\n");
492 synchronized(Bluetooth.sync)
494 //Debug.out("Process streams " + reqState + "\n");
495 if (reqState != RS_IDLE) return;
496 int next = selectChan();
497 if (next < 0 || !switchToStream(next)) return;
498 // Perform I/O on the current stream. Switching from one stream
499 // to another is a slow process, so we spend at least IO_TIME ms
500 // on each stream before switching away.
501 //Debug.out("Process streams 2" + reqState + "\n");
502 int ioEnd = (int)System.currentTimeMillis() + IO_TIME;
503 while (ioEnd > (int)System.currentTimeMillis() && Chans[curChan].state >= BTConnection.CS_CONNECTED)
505 if (bc4Mode() != MO_STREAM) return;
506 Chans[curChan].send();
507 Chans[curChan].recv();
510 //Debug.out("Process streams 3" + reqState + "\n");
511 // Do we need to switch back to command mode?
512 if (listening) return;
518 private int waitSwitch(int target, boolean flush)
520 // Wait for the BC4 to switch to mode, or timeout...
521 int timeout = (int) System.currentTimeMillis() + TO_SWITCH;
522 while (timeout > (int)System.currentTimeMillis())
524 if (bc4Mode() == target) return target;
525 // Need to flush input when switching to command mode
526 if (flush && curChan >= 0) Chans[curChan].flushInput();
528 //1 Debug.out("Failed to switch\n");
534 private boolean switchToStream(int chan)
536 // Decide which (if any) stream to switch to
537 if (mode == MO_STREAM && chan == curChan) return true;
539 //1 Debug.out("Switch to chan " + chan + " handle " + Chans[chan].handle + "\n");
540 cmdInit(MSG_OPEN_STREAM, 2, Chans[chan].handle, 0);
542 // Now wait for the BC4 to switch
543 if (waitSwitch(MO_STREAM, false) != MO_STREAM) return false;
544 //1 Debug.out("In stream mode\n");
545 // Make sure we process any remaining command input that may have arrived
547 // Finally switch the ARM over
548 btSetArmCmdMode(MO_STREAM);
554 private void switchToCmd()
556 // Need to switch back into command mode
557 // First step send any pending data
558 if (mode == MO_CMD) return;
559 //1 Debug.out("switch to cmd\n");
560 if (mode == MO_STREAM && bc4Mode() == MO_CMD && curChan >= 0)
562 //1 Debug.out("Trying early flush\n");
563 Chans[curChan].flushInput();
565 // wait for any output to drain
566 while((btPending() & BT_PENDING_OUTPUT) != 0)
568 try{Thread.sleep(100);}catch(Exception e){}
569 btSetArmCmdMode(MO_CMD);
570 // If there is any input data left we could be in trouble. Try and
572 if (waitSwitch(MO_CMD, true) != MO_CMD)
574 //1 Debug.out("Failed to switch to cmd\n");
581 private int bc4Mode()
583 // return the current mode of the BC4 chip
584 int ret = btGetBC4CmdMode();
585 // > 512 indicates a high logic level which is mode 0!
592 private void waitInit()
594 synchronized (Bluetooth.sync)
599 Bluetooth.sync.notifyAll();
605 //1 Debug.out("Thread running\n");
607 //1 Debug.out("Init complete\n");
617 // Create the Bluetooth device thread.
618 private static BTThread btThread = new BTThread();
620 static private int waitState(int target)
622 // Debug.out("Wait state " + target + "\n");
623 synchronized (Bluetooth.sync) {
624 // Wait for the system to enter the specified state (or timeout)
625 while (reqState != target && reqState != RS_ERROR)
626 try{Bluetooth.sync.wait();}catch(Exception e){}
627 if (reqState == RS_ERROR)
634 private static void cmdStart()
636 // Wait for the system to be idle. Ignore timeout errors.
637 while (waitState(RS_IDLE) < 0)
638 try{Bluetooth.sync.wait();}catch(Exception e){}
641 private static void cmdComplete()
643 // command now complete. Reset to idle (clears any timeout state)
644 synchronized(Bluetooth.sync)
648 Bluetooth.sync.notifyAll();
652 private static int cmdWait(int state, int waitState, int msg, int timeout)
654 //1 Debug.out("Cmd wait\n");
655 synchronized (Bluetooth.sync)
657 // Check we have power if not fail the request
658 if (!powerOn) return -1;
659 if (waitState > 0) reqState = waitState;
660 if (timeout != TO_NONE) startTimeout(timeout);
663 if (waitState(state) < 0) return -1;
664 if (msg == MSG_ANY || replyBuf[1] == msg) return 0;
665 // Ignore any unwanted message
666 if (reqState == RS_REPLY) reqState = RS_WAIT;
672 * Set the pin to be used for pairing/connecting to the system
674 * @param newPin the new pin code
677 public static void setPin(byte[] newPin)
683 * Return the pin to be used for pairing/connecting to the system
685 * @return The current pin code
688 public static byte[] getPin()
695 * Close an open connection
697 * @param handle the handle for the connection
698 * @return the status 0 = success
700 public static int closeConnection(byte handle)
703 //1 Debug.out("Close connection state " + reqState + "\n");
704 synchronized (Bluetooth.sync)
707 // We can have a race condition when both ends of the connection
708 // close at the same time. This can mean we try and close an already
709 // closed connection. If we do this the BC4 goes into reset mode.
710 // To try and avoid this happening we insert a different length
711 // delay between outbound and inbound streams. We then wait to see
712 // if the other end has already closed things...
713 int timeout = (handle == 3 ? 5*TO_CLOSE : TO_CLOSE);
715 try{Bluetooth.sync.wait(timeout);}catch(Exception e){}
717 // There is a small chance that we may have had a reset so make sure
718 // that we still have an open channel to close!
719 byte [] status = getConnectionStatus();
720 //1 Debug.out("Conn status " + status[handle]);
721 if (status == null || status[handle] != 2) return -1;
722 if (Chans[handle].state != BTConnection.CS_DISCONNECTING) return -1;
723 cmdInit(MSG_CLOSE_CONNECTION, 2, handle, 0);
724 if (cmdWait(RS_REPLY, RS_CMD, MSG_CLOSE_CONNECTION_RESULT, TO_SHORT) >= 0)
725 ret = (int)replyBuf[2];
728 // We may have a race condition here, or have triggered a reset
729 // wait for things to settle
731 try{Bluetooth.sync.wait(TO_REPLY);}catch(Exception e){}
733 } while (getConnectionStatus() == null);
740 * Opens the port to allow incoming connections.
742 * @return an array of three bytes: success, handle, ps_success
744 public static byte[] openPort() {
745 byte[] result = new byte[3];
746 synchronized (Bluetooth.sync)
749 cmdInit(MSG_OPEN_PORT, 1, 0, 0);
750 if (cmdWait(RS_REPLY, RS_CMD, MSG_PORT_OPEN_RESULT, TO_SHORT) < 0)
753 System.arraycopy(replyBuf, 2, result, 0, 3);
754 //1 Debug.out("Port open handle " + (int)replyBuf[3] + " status " + (int)replyBuf[2] + "\n");
762 * Closes the port to disallow incoming connections.
764 * @return an array of two bytes: success, ps_success
766 public static byte [] closePort() {
767 byte [] result = new byte[2];
768 //1 Debug.out("Close port\n");
769 synchronized (Bluetooth.sync)
772 // The Lego doc says the handle should always be 3!
773 cmdInit(MSG_CLOSE_PORT, 2, 3, 0);
774 if (cmdWait(RS_REPLY, RS_CMD, MSG_CLOSE_PORT_RESULT, TO_SHORT) < 0)
777 System.arraycopy(replyBuf, 2, result, 0, 2);
784 * Wait for a remote device to connect.
786 * @param pin the pin to use
787 * @return a BTConnection
789 public static BTConnection waitForConnection(byte[] pin)
791 //1 Debug.out("waitForConnection\n");
792 synchronized (Bluetooth.sync)
794 BTConnection ret = null;
795 // only one listener at once
796 if (listening) return null;
797 // First open up the listening port
798 byte [] port = openPort();
799 if (port == null || port[0] != 1 || port[1] >= Chans.length || port[1] < 0)
801 //1 Debug.out("Failed to open port\n");
804 // Now in listening mode
806 byte []savedPin = getPin();
808 // Wait for special connect indication
809 while (listening && reqState != RS_REQUESTCONNECT)
810 try{Bluetooth.sync.wait();}catch(Exception e){}
813 //1 Debug.out("Got connect request\n");
815 reqState = savedState;
816 // and wait until we have control
818 // Acknowledge the request
819 cmdInit(MSG_ACCEPT_CONNECTION, 2, 1, 0);
820 if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECT_RESULT, TO_LONG) >= 0)
822 //1 Debug.out("Connect result " + (int)replyBuf[2] + " handle " + (int)replyBuf[3] + "\n");
823 if (replyBuf[2] == 1)
825 byte handle = replyBuf[3];
827 if (handle >= 0 && handle < Chans.length)
829 // Assert(Chans[handle].state == CS_IDLE);
830 Chans[handle].bind(handle);
831 // now have one more connected
847 public static BTConnection waitForConnection()
849 return waitForConnection(defaultPin);
853 * Connects to a remote device
855 * @param remoteDevice remote device
856 * @return BTConnection Object or null
858 public static BTConnection connect(BTRemoteDevice remoteDevice) {
859 if (remoteDevice == null) return null;
860 return connect(remoteDevice.getDeviceAddr());
863 * Connects to a Device by it's Byte-Device-Address Array
864 * Uses default pin "1234"
866 * @param device_addr byte-Array with device-Address
867 * @return BTConnection Object or null
869 public static BTConnection connect(byte[] device_addr) {
870 return connect(device_addr, defaultPin);
874 * Connects to a Device by it's Byte-Device-Address Array
876 * @param device_addr byte-Array with device-Address
877 * @param pin the pin to use
878 * @return BTConnection Object or null
880 public static BTConnection connect(byte[] device_addr, byte[] pin) {
881 //1 Debug.out("Connect\n");
882 synchronized(Bluetooth.sync)
884 BTConnection ret = null;
885 byte[] savedPin = getPin();
888 cmdInit(MSG_CONNECT, 8, 0, 0);
889 System.arraycopy(device_addr, 0, cmdBuf, 2, 7);
890 if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECT_RESULT, TO_LONG) >= 0)
892 //1 Debug.out("Connect result " + (int)replyBuf[2] + " handle " + (int)replyBuf[3] + "\n");
893 if (replyBuf[2] != 0)
895 byte handle = replyBuf[3];
896 // Now need to check that the connection is not closed imm
898 try{Bluetooth.sync.wait(300);}catch(Exception e){}
899 if (reqState == RS_WAIT && handle >= 0 && handle < Chans.length)
902 Chans[handle].bind(handle);
903 // now have one more connected
917 * Get the Bluetooth signal strength (link quality)
918 * Higher values mean stronger signal.
921 * @return link quality value 0 to 255.
924 public static int getSignalStrength(byte handle) {
925 //1 Debug.out("getSignalStrength\n");
926 synchronized (Bluetooth.sync)
930 if (Chans[handle].state != BTConnection.CS_CONNECTED) return -1;
931 cmdInit(MSG_GET_LINK_QUALITY, 2, handle, 0);
932 if (cmdWait(RS_REPLY, RS_CMD, MSG_LINK_QUALITY_RESULT, TO_SHORT) >= 0)
933 ret = replyBuf[2] & 0xff;
941 * Get the friendly name of the local device
942 * @return the friendly name
944 public static byte [] getFriendlyName() {
945 byte[] result = new byte[16];
946 //1 Debug.out("getFriendlyName\n");
947 synchronized (Bluetooth.sync)
949 // If power is off return the cached name.
950 if (!powerOn) return cachedName;
952 cmdInit(MSG_GET_FRIENDLY_NAME, 1, 0, 0);
953 if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_FRIENDLY_NAME_RESULT, TO_SHORT) < 0)
956 System.arraycopy(replyBuf, 2, result, 0, 16);
963 * Set the name of the local device
964 * @param name the friendly name for the device
966 public static boolean setFriendlyName(byte[] name) {
967 //1 Debug.out("setFriendlyName\n");
968 synchronized (Bluetooth.sync)
972 cmdInit(MSG_SET_FRIENDLY_NAME, 17, 0, 0);
973 System.arraycopy(name, 0, cmdBuf, 2, 16);
974 if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_FRIENDLY_NAME_ACK, TO_LONG) >= 0)
982 * get the Bluetooth address of the local device
983 * @return the local address
985 public static byte[] getLocalAddress() {
986 byte[] result = new byte[7];
987 //1 Debug.out("getLocalAddress\n");
988 synchronized (Bluetooth.sync)
990 // If power is off return cached name.
991 if (!powerOn) return cachedAddress;
993 cmdInit(MSG_GET_LOCAL_ADDR, 1, 0, 0);
994 if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_LOCAL_ADDR_RESULT, TO_SHORT) < 0)
997 System.arraycopy(replyBuf, 2, result, 0, 7);
1005 * The internal Chip has a list of already paired Devices. This Method returns a
1006 * Vector-List which contains all the known Devices on the List. These need not be reachable.
1007 * To connect to a "not-known"-Device, you should use the Inquiry-Process.
1008 * The pairing-Process can also be done with the original Lego-Firmware. The List of known
1009 * devices will not get lost, when installing the LeJOS Firmware.
1011 * @return Vector with List of known Devices
1013 public static Vector getKnownDevicesList() {
1014 //1 Debug.out("getKnownDevicesList\n");
1015 synchronized(Bluetooth.sync)
1018 byte[] device = new byte[7];
1019 byte[] devclass = new byte[4];
1020 char[] name = new char[16];
1021 Vector retVec = new Vector(1);
1022 BTRemoteDevice curDevice;
1024 cmdInit(MSG_DUMP_LIST, 1, 0, 0);
1025 while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0)
1028 if (replyBuf[1] == MSG_LIST_ITEM)
1030 System.arraycopy(replyBuf, 2, device, 0, 7);
1032 for(; nl < 16 && replyBuf[nl+9] != 0; nl++)
1033 name[nl] = (char)replyBuf[nl+9];
1034 System.arraycopy(replyBuf, 25, devclass, 0, 4);
1035 curDevice = new BTRemoteDevice(name, nl, device, devclass);
1036 //1 Debug.out("got name " + curDevice.getFriendlyName() + "\n");
1037 retVec.addElement(curDevice);
1039 else if (replyBuf[1] == MSG_LIST_DUMP_STOPPED)
1048 * Gets a Device of the BC4-Chips internal list of known Devices
1049 * (those who have been paired before) into the BTDevice Object.
1050 * @param fName Friendly-Name of the device
1051 * @return BTDevice Object or null, if not found.
1053 public static BTRemoteDevice getKnownDevice(String fName) {
1054 BTRemoteDevice btd = null;
1055 //look the name up in List of Known Devices
1056 Vector devList = getKnownDevicesList();
1057 if (devList.size() > 0) {
1058 for (int i = 0; i < devList.size(); i++) {
1059 btd = (BTRemoteDevice) devList.elementAt(i);
1060 if (btd.getFriendlyName().equals(fName)) {
1069 * Add device to known devices
1070 * @param d Remote Device
1071 * @return true iff add was successful
1073 public static boolean addDevice(BTRemoteDevice d) {
1074 byte [] addr = d.getDeviceAddr();
1075 String name = d.getFriendlyName();
1076 byte[] cod = d.getDeviceClass();
1077 //1 Debug.out("addDevice " + name + "\n");
1078 synchronized (Bluetooth.sync)
1082 cmdInit(MSG_ADD_DEVICE, 28, 0, 0);
1083 System.arraycopy(addr, 0, cmdBuf, 2, 7);
1084 System.arraycopy(cod, 0, cmdBuf, 25, 4);
1085 for(int i=0;i<name.length();i++) cmdBuf[i+9] = (byte) name.charAt(i);
1086 for(int i=name.length(); i < 16; i++) cmdBuf[i+9] = 0;
1087 if (cmdWait(RS_REPLY, RS_CMD, MSG_LIST_RESULT, TO_LONG) >= 0)
1088 ret = replyBuf[2] == 0x50;
1095 * Add device to known devices
1096 * @param d Remote Device
1097 * @return true iff remove was successful
1099 public static boolean removeDevice(BTRemoteDevice d) {
1100 byte [] addr = d.getDeviceAddr();
1101 synchronized (Bluetooth.sync)
1103 boolean ret = false;
1105 cmdInit(MSG_REMOVE_DEVICE, 8, 0, 0);
1106 System.arraycopy(addr, 0, cmdBuf, 2, 7);
1107 if (cmdWait(RS_REPLY, RS_CMD, MSG_LIST_RESULT, TO_LONG) >= 0)
1108 ret = replyBuf[2] == 0x53;
1115 * Start a Bluetooth inquiry process
1117 * @param maxDevices the maximum number of devices to discover
1118 * @param timeout the timeout value in units of 1.28 econds
1119 * @param cod the class of device to look for
1120 * @return a vector of all the devices found
1122 public static Vector inquire(int maxDevices, int timeout, byte[] cod) {
1123 Vector retVec = new Vector();
1124 byte[] device = new byte[7];
1125 char[] name = new char[16];
1126 synchronized (Bluetooth.sync)
1130 cmdInit(MSG_BEGIN_INQUIRY, 8, maxDevices, 0);
1131 cmdBuf[4] = (byte)timeout;
1132 System.arraycopy(cod, 0, cmdBuf, 5, 4);
1133 while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0)
1136 if (replyBuf[1] == MSG_INQUIRY_RESULT)
1138 System.arraycopy(replyBuf, 2, device, 0, 7);
1140 for(;nameLen<16 && replyBuf[9+nameLen] != 0;nameLen++)
1141 name[nameLen] = (char) replyBuf[9+nameLen];
1142 System.arraycopy(replyBuf, 25, cod, 0, 4);
1143 // add the Element to the Vector List
1144 retVec.addElement(new BTRemoteDevice(name, nameLen, device, cod));
1146 else if (replyBuf[1] == MSG_INQUIRY_STOPPED)
1149 // Fill in the names
1150 for (int i = 0; i < retVec.size(); i++) {
1151 BTRemoteDevice btrd = ((BTRemoteDevice) retVec.elementAt(i));
1152 String s = btrd.getFriendlyName();
1153 if (s.length() == 0) {
1154 String nm = lookupName(btrd.getDeviceAddr());
1155 btrd.setFriendlyName(nm.toCharArray(),nm.length());
1167 * Look up the name of a device using its address
1169 * @param addr device address
1170 * @return friendly name of device
1172 public static String lookupName(byte [] addr) {
1173 char[] name = new char[16];
1174 synchronized (Bluetooth.sync)
1179 cmdInit(MSG_LOOKUP_NAME, 8, 0, 0);
1180 System.arraycopy(addr, 0, cmdBuf, 2, 7);
1181 while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0)
1184 if (replyBuf[1] == MSG_LOOKUP_NAME_RESULT)
1187 for(; len < 16 && replyBuf[len+9] != 0; len++)
1188 name[len] = (char)replyBuf[len+9];
1189 ret = new String(name, 0, len);
1192 else if (replyBuf[1] == MSG_LOOKUP_NAME_FAILURE)
1203 * Get the status of all connections
1205 * @return byte array of status for each handle
1207 public static byte[] getConnectionStatus() {
1208 byte[] result = new byte[4];
1209 //1 Debug.out("getConnectionStatus\n");
1210 synchronized (Bluetooth.sync)
1213 cmdInit(MSG_GET_CONNECTION_STATUS, 1, 0, 0);
1214 if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECTION_STATUS_RESULT, TO_SHORT) < 0)
1217 System.arraycopy(replyBuf, 5, result, 0, 4);
1224 * Get the major and minor version of the BlueCore code
1226 * @return an array of two bytes: major version, minor version
1228 public static byte[] getVersion() {
1229 byte [] version = new byte[2];
1230 //1 Debug.out("getVersion\n");
1231 synchronized (Bluetooth.sync)
1234 cmdInit(MSG_GET_VERSION, 1, 0, 0);
1235 if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_VERSION_RESULT, TO_SHORT) < 0)
1238 System.arraycopy(replyBuf, 2, version, 0, 2);
1245 * Get the persistent status value from the BC4 chip
1247 * @return the byte value
1249 public static int getStatus() {
1250 //1 Debug.out("getStatus\n");
1251 synchronized (Bluetooth.sync)
1255 cmdInit(MSG_GET_BRICK_STATUSBYTE, 1, 0, 0);
1256 if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_BRICK_STATUSBYTE_RESULT, TO_SHORT) >= 0)
1257 ret = ((int)replyBuf[2] & 0xff) | (((int)replyBuf[3] & 0xff) << 8);
1264 * Set the persistent status byte for the BC4 chip
1266 * @param status the byte status value
1269 public static int setStatus(int status) {
1270 //1 Debug.out("setStatus\n");
1271 synchronized (Bluetooth.sync)
1275 cmdInit(MSG_SET_BRICK_STATUSBYTE, 3, status & 0xff, (status >> 8) & 0xff);
1276 if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_BRICK_STATUSBYTE_RESULT, TO_SHORT) >= 0)
1284 * Get the visibility (discoverable) status of the device
1286 * @return 1 = visible, 0 = invisible
1288 public static int getVisibility() {
1289 //1 Debug.out("getVisibility\n");
1290 synchronized (Bluetooth.sync)
1294 cmdInit(MSG_GET_DISCOVERABLE, 1, 0, 0);
1295 if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_DISCOVERABLE_RESULT, TO_SHORT) >= 0)
1303 * Get the port open status,
1304 * i.e whether connections are being accepted
1306 * @return 1 if the port is open, 0 otherwise
1308 public static int getPortOpen() {
1309 //1 Debug.out("getPortOpen\n");
1310 synchronized (Bluetooth.sync)
1314 cmdInit(MSG_GET_PORT_OPEN, 1, 0, 0);
1315 if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_PORT_OPEN_RESULT, TO_SHORT) >= 0)
1323 * Get the operating mode (stream breaking or not)
1325 * @return 0 = stream breaking mode, 1 = don't break stream mode
1328 public static int getOperatingMode() {
1329 //1 Debug.out("getOperatingMode\n");
1330 synchronized (Bluetooth.sync)
1334 cmdInit(MSG_GET_OPERATING_MODE, 1, 0, 0);
1335 if (cmdWait(RS_REPLY, RS_CMD, MSG_OPERATING_MODE_RESULT, TO_SHORT) >= 0)
1343 * Set Bluetooth visibility (discoverable) on or off for the local device
1345 * @param visible true to set visibility on, false to set it off
1348 public static int setVisibility(byte visible) {
1349 //1 Debug.out("setVisibility\n");
1350 synchronized (Bluetooth.sync)
1354 cmdInit(MSG_SET_DISCOVERABLE, 2, visible, 0);
1355 if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_DISCOVERABLE_ACK, TO_SHORT) >= 0)
1363 * Reset the settings of the BC4 chip to the factory defaults.
1364 * The NXT should be restarted after this.
1367 public static int setFactorySettings() {
1368 //1 Debug.out("setFactorySettings\n");
1369 synchronized (Bluetooth.sync)
1373 cmdInit(MSG_SET_FACTORY_SETTINGS, 1, 0, 0);
1374 if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_FACTORY_SETTINGS_ACK, TO_SHORT) >= 0)
1382 * Set the operating mode
1384 * @param mode 0 = Stream breaking, 1 don't break stream
1387 public static int setOperatingMode(byte mode) {
1388 //1 Debug.out("setOperatingMode\n");
1389 synchronized (Bluetooth.sync)
1393 cmdInit(MSG_SET_OPERATING_MODE, 2, mode, 0);
1394 if (cmdWait(RS_REPLY, RS_CMD, MSG_OPERATING_MODE_RESULT, TO_SHORT) >= 0)
1402 * Force a reset of the Bluetooth module.
1404 * After this call power will be on.
1405 * Any existing connections will be closed
1406 * Any listening threads will be aborted
1409 public static void reset()
1411 synchronized (Bluetooth.sync)
1414 // Force a timeout and hence a reset
1415 cmdWait(RS_REPLY, RS_WAIT, MSG_RESET_INDICATION, TO_FORCERESET);
1421 * Set the power to the module
1423 * @param on power on or off
1425 public static void setPower(boolean on)
1427 synchronized (Bluetooth.sync)
1429 if (powerOn == on) return;
1434 // Now make sure things have settled
1435 for(int i =0; i < 5; i++)
1436 if (getOperatingMode() >= 0) break;
1440 // Powering off. Do we need to reset things?
1441 boolean wasListening = listening;
1442 // Wait for any other commands to complete
1444 if (connected > 0 || listening)
1446 // Wait for the listening thread to exit
1448 try{Bluetooth.sync.wait(2000);}catch(Exception e){}
1449 //1 Debug.out("Power going off\n");
1453 publicPowerOn = powerOn;
1458 * Return the current state of the module power
1460 * @return power on or off
1462 public static boolean getPower()
1464 synchronized(Bluetooth.sync)
1466 return publicPowerOn;
1471 public static int getResetCount()
1476 * The following are provided for compatibility with the old Bluetooth
1477 * class. They should not be used, in new programs and should probably
1482 * Read a packet from the stream. Do not block and for small packets
1483 * (< 254 bytes), do not return a partial packet.
1484 * @param buf Buffer to read data into.
1485 * @param len Number of bytes to read.
1486 * @return > 0 number of bytes read.
1487 * other values see read.
1489 public static int readPacket(byte buf[], int len)
1491 return Chans[3].readPacket(buf, len);
1495 * Send a data packet.
1496 * Must be in data mode.
1497 * @param buf the data to send
1498 * @param bufLen the number of bytes to send
1500 public static void sendPacket(byte [] buf, int bufLen)
1502 Chans[3].sendPacket(buf, bufLen);
1506 * Set the BC4 mode, and wait for that mode to be confirmed by the chip.
1508 * @param mode the requested mode 1 == Command mode 0 == Stream mode
1510 public static void btSetCmdMode(int mode)