1 /*
2 This source is part of the
3 _____ ___ ____
4 __ / / _ \/ _ | / __/___ _______ _
5 / // / , _/ __ |/ _/_/ _ \/ __/ _ `/
6 \___/_/|_/_/ |_/_/ (_)___/_/ \_, /
7 /___/
8 repository. It is in the public domain.
9 Contact BoD@JRAF.org for more information.
10
11 $Id: IBuddy.java 265 2008-09-07 14:37:02Z bod $
12 */
13 package org.jraf.jlibibuddy;
14
15 import ch.ntb.usb.Device;
16 import ch.ntb.usb.USB;
17 import ch.ntb.usb.USBException;
18
19 /**
20 * Main entry point to i-Buddy commanding. Note: currently, one and only one i-Buddy can be manipulated with this
21 * library. Therefore, this class should be used as a singleton, even if this not enforced by the code.
22 */
23 public class IBuddy {
24 /**
25 * Usb device vendor id.
26 */
27 private static final short DEVICE_VENDOR = 0x1130;
28
29 /**
30 * Usb device product id.
31 */
32 private static final short DEVICE_PRODUCT = 0x0001;
33
34 /**
35 * Usb init command.
36 */
37 private static final byte[] INIT = new byte[]{0x22, 0x09, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00};
38
39 /**
40 * Usb command header.
41 */
42 private static final byte[] COMMAND_HEADER = new byte[]{0x55, 0x53, 0x42, 0x43, 0x00, 0x40, 0x02};
43
44 /**
45 * Command to turn everything off on the i-Buddy.
46 */
47 private static final byte ALL_OFF = (byte) 0xff;
48
49 /**
50 * Bit controlling the heart LED (0 is ON, 1 is OFF).
51 */
52 private static final byte HEART = (byte) 0x80;
53
54 /**
55 * Bit controlling the head blue LED (0 is ON, 1 is OFF).
56 */
57 private static final byte HEAD_BLUE = 0x40;
58
59 /**
60 * Bit controlling the head green LED (0 is ON, 1 is OFF).
61 */
62 private static final byte HEAD_GREEN = 0x20;
63
64 /**
65 * Bit controlling the head red LED (0 is ON, 1 is OFF).
66 */
67 private static final byte HEAD_RED = 0x10;
68
69 /**
70 * Bit controlling the wings down state (0 is not down, 1 is down). Should not be flipped to 1 if {@link #WINGS_UP}
71 * is also 1.
72 */
73 private static final byte WINGS_DOWN = 0x08;
74
75 /**
76 * Bit controlling the wings up state (0 is not up, 1 is up). Should not be flipped to 1 if {@link #WINGS_DOWN} is
77 * also 1.
78 */
79 private static final byte WINGS_UP = 0x04;
80
81 /**
82 * Bit controlling the rotate right state (0 is do not rotate right, 1 rotate right). Should not be flipped to 1 if
83 * {@link #ROTATE_LEFT} is also 1.
84 */
85 private static final byte ROTATE_RIGHT = 0x02;
86
87 /**
88 * Bit controlling the rotate left state (0 is do not rotate left, 1 rotate left). Should not be flipped to 1 if
89 * {@link #ROTATE_RIGHT} is also 1.
90 */
91 private static final byte ROTATE_LEFT = 0x01;
92
93 /**
94 * Different colors for the head (all possible combinations of the blue, green and red LEDs).
95 */
96 public enum Color {
97 OFF,
98 RED,
99 GREEN,
100 BLUE,
101 YELLOW,
102 PURPLE,
103 CYAN,
104 WHITE
105 }
106
107 /**
108 * Different wing positions.
109 */
110 public enum Wings {
111 AT_EASE,
112 DOWN,
113 UP
114 }
115
116 /**
117 * Different rotation states.
118 */
119 public enum Rotate {
120 AT_EASE,
121 LEFT,
122 RIGHT
123 }
124
125 private Device device;
126
127 /*
128 * i-Buddy's current state.
129 */
130 private boolean heart;
131 private boolean headBlue;
132 private boolean headGreen;
133 private boolean headRed;
134 private boolean wingsDown;
135 private boolean wingsUp;
136 private boolean rotateRight;
137 private boolean rotateLeft;
138
139 private IBuddy() {
140 }
141
142 /**
143 * Gets the i-Buddy.
144 * @return the i-Buddy.
145 */
146 public static IBuddy getIBuddy() {
147 return new IBuddy();
148 }
149
150 private static byte[] getCommandMessage(byte command) {
151 byte[] res = new byte[COMMAND_HEADER.length + 1];
152 System.arraycopy(COMMAND_HEADER, 0, res, 0, COMMAND_HEADER.length);
153 res[COMMAND_HEADER.length] = command;
154 return res;
155 }
156
157 private static byte getCommand(boolean heart, boolean headBlue, boolean headGreen, boolean headRed, boolean wingsDown, boolean wingsUp, boolean rotateRight, boolean rotateLeft) {
158 byte res = (byte) 0xF0;
159 if (heart) {
160 res ^= HEART;
161 } else {
162 res |= HEART;
163 }
164 if (headBlue) {
165 res ^= HEAD_BLUE;
166 } else {
167 res |= HEAD_BLUE;
168 }
169 if (headGreen) {
170 res ^= HEAD_GREEN;
171 } else {
172 res |= HEAD_GREEN;
173 }
174 if (headRed) {
175 res ^= HEAD_RED;
176 } else {
177 res |= HEAD_RED;
178 }
179 if (wingsDown) {
180 res |= WINGS_DOWN;
181 } else {
182 res &= ~WINGS_DOWN;
183 }
184 if (wingsUp) {
185 res |= WINGS_UP;
186 } else {
187 res &= ~WINGS_UP;
188 }
189 if (rotateRight) {
190 res |= ROTATE_RIGHT;
191 } else {
192 res &= ~ROTATE_RIGHT;
193 }
194 if (rotateLeft) {
195 res |= ROTATE_LEFT;
196 } else {
197 res &= ~ROTATE_LEFT;
198 }
199 return res;
200 }
201
202 /**
203 * Main low level send method.
204 *
205 * @param command the command byte.
206 */
207 private synchronized void sendMessage(byte command) throws IBuddyException {
208 open();
209 try {
210 device.controlMsg(USB.REQ_TYPE_TYPE_CLASS | USB.REQ_TYPE_RECIP_INTERFACE, USB.REQ_SET_CONFIGURATION, 0x02, 0x01, INIT, INIT.length, 100, true);
211 } catch (USBException e) {
212 close();
213 throw new IBuddyException("Could not send message to i-Buddy", e);
214 }
215 byte[] commandMessage = getCommandMessage(command);
216 try {
217 device.controlMsg(USB.REQ_TYPE_TYPE_CLASS | USB.REQ_TYPE_RECIP_INTERFACE, USB.REQ_SET_CONFIGURATION, 0x02, 0x01, commandMessage, commandMessage.length, 100, true);
218 } catch (USBException e) {
219 close();
220 throw new IBuddyException("Could not send message to i-Buddy", e);
221 }
222 close();
223 }
224
225 /**
226 * General purpose send state method.
227 *
228 * @param heart {@code true} to turn the heart LED on, {@code false} to turn it off.
229 * @param headBlue {@code true} to turn the head blue LED on, {@code false} to turn it off.
230 * @param headGreen {@code true} to turn the head green LED on, {@code false} to turn it off.
231 * @param headRed {@code true} to turn the head red LED on, {@code false} to turn it off.
232 * @param wingsDown {@code true} to turn the "wings down" state on, {@code false} to turn it off.
233 * @param wingsUp {@code true} to turn the "wings up" state on, {@code false} to turn it off.
234 * @param rotateRight {@code true} to turn the "rotate right" state on, {@code false} to turn it off.
235 * @param rotateLeft {@code true} to turn the "rotate left" state on, {@code false} to turn it off.
236 *
237 * @throws IBuddyException in case of problem while sending the usb command.
238 * @throws IllegalArgumentException if {@code wingsUp} and {@code wingsDown} are both {@code true}, or if {@code
239 * rotateRight} and {@code rotateLeft} are both {@code true}.
240 */
241 public void sendState(boolean heart, boolean headBlue, boolean headGreen, boolean headRed, boolean wingsDown, boolean wingsUp, boolean rotateRight, boolean rotateLeft) throws IBuddyException {
242 if (wingsUp && wingsDown) {
243 throw new IllegalArgumentException("wingsUp and wingsDown cannot be both true at the same time");
244 }
245 if (rotateRight && rotateLeft) {
246 throw new IllegalArgumentException("rotateRight and rotateLeft cannot be both true at the same time");
247 }
248
249 this.heart = heart;
250 this.headBlue = headBlue;
251 this.headGreen = headGreen;
252 this.headRed = headRed;
253 this.wingsDown = wingsDown;
254 this.wingsUp = wingsUp;
255 this.rotateRight = rotateRight;
256 this.rotateLeft = rotateLeft;
257 sendMessage(getCommand(heart, headBlue, headGreen, headRed, wingsDown, wingsUp, rotateRight, rotateLeft));
258 }
259
260 /**
261 * Turns everything off.
262 *
263 * @throws IBuddyException in case of problem while sending the usb command.
264 */
265 public void sendAllOff() throws IBuddyException {
266 heart = false;
267 headBlue = false;
268 headGreen = false;
269 headRed = false;
270 wingsDown = false;
271 wingsUp = false;
272 rotateRight = false;
273 rotateLeft = false;
274 sendMessage(ALL_OFF);
275 }
276
277 /**
278 * Turns the heart LED on or off.
279 *
280 * @param heart whether to turn the heart LED on ({@code true}) or off ({@code false}).
281 *
282 * @throws IBuddyException in case of problem while sending the usb command.
283 */
284 public void sendHeart(boolean heart) throws IBuddyException {
285 sendState(heart, headBlue, headGreen, headRed, wingsDown, wingsUp, rotateRight, rotateLeft);
286 }
287
288 /**
289 * Turns the head blue LED on or off.
290 *
291 * @param headBlue whether to turn the head blue LED on ({@code true}) or off ({@code false}).
292 *
293 * @throws IBuddyException in case of problem while sending the usb command.
294 */
295 public void sendHeadBlue(boolean headBlue) throws IBuddyException {
296 sendState(heart, headBlue, headGreen, headRed, wingsDown, wingsUp, rotateRight, rotateLeft);
297 }
298
299 /**
300 * Turns the head green LED on or off.
301 *
302 * @param headGreen whether to turn the head green LED on ({@code true}) or off ({@code false}).
303 *
304 * @throws IBuddyException in case of problem while sending the usb command.
305 */
306 public void sendHeadGreen(boolean headGreen) throws IBuddyException {
307 sendState(heart, headBlue, headGreen, headRed, wingsDown, wingsUp, rotateRight, rotateLeft);
308 }
309
310 /**
311 * Turns the head red LED on or off.
312 *
313 * @param headRed whether to turn the head red LED on ({@code true}) or off ({@code false}).
314 *
315 * @throws IBuddyException in case of problem while sending the usb command.
316 */
317 public void sendHeadRed(boolean headRed) throws IBuddyException {
318 sendState(heart, headBlue, headGreen, headRed, wingsDown, wingsUp, rotateRight, rotateLeft);
319 }
320
321 /**
322 * Turns the head LEDs on or off.
323 *
324 * @param headBlue whether to turn the head blue LED on ({@code true}) or off ({@code false}).
325 * @param headGreen whether to turn the head green LED on ({@code true}) or off ({@code false}).
326 * @param headRed whether to turn the head red LED on ({@code true}) or off ({@code false}).
327 *
328 * @throws IBuddyException in case of problem while sending the usb command.
329 */
330 public void sendHeadColor(boolean headBlue, boolean headGreen, boolean headRed) throws IBuddyException {
331 sendState(heart, headBlue, headGreen, headRed, wingsDown, wingsUp, rotateRight, rotateLeft);
332 }
333
334 /**
335 * Sets a color on the head by turning the LEDs on or off.
336 *
337 * @param color the color to set on the head.
338 *
339 * @throws IBuddyException in case of problem while sending the usb command.
340 */
341 public void sendHeadColor(Color color) throws IBuddyException {
342 switch (color) {
343 case OFF:
344 sendHeadColor(false, false, false);
345 break;
346 case RED:
347 sendHeadColor(false, false, true);
348 break;
349 case GREEN:
350 sendHeadColor(false, true, false);
351 break;
352 case BLUE:
353 sendHeadColor(true, false, false);
354 break;
355 case YELLOW:
356 sendHeadColor(false, true, true);
357 break;
358 case PURPLE:
359 sendHeadColor(true, false, true);
360 break;
361 case CYAN:
362 sendHeadColor(true, true, false);
363 break;
364 case WHITE:
365 sendHeadColor(true, true, true);
366 break;
367 }
368 }
369
370 /**
371 * Turns the "wings down" state on or off. This also automatically turns the "wings up" state to the opposite
372 * value.
373 *
374 * @param wingsDown whether to turn the "wings down" on ({@code true}) or off ({@code false}).
375 *
376 * @throws IBuddyException in case of problem while sending the usb command.
377 */
378 public void sendWingsDown(boolean wingsDown) throws IBuddyException {
379 sendState(heart, headBlue, headGreen, headRed, wingsDown, !wingsDown, rotateRight, rotateLeft);
380 }
381
382 /**
383 * Turns the "wings up" state on or off. This also automatically turns the "wings down" state to the opposite
384 * value.
385 *
386 * @param wingsUp whether to turn the "wings up" on ({@code true}) or off ({@code false}).
387 *
388 * @throws IBuddyException in case of problem while sending the usb command.
389 */
390 public void sendWingsUp(boolean wingsUp) throws IBuddyException {
391 sendState(heart, headBlue, headGreen, headRed, !wingsUp, wingsUp, rotateRight, rotateLeft);
392 }
393
394 /**
395 * Turns the "wings up" and "wings down" states on or off.
396 *
397 * @param wingsUp whether to turn the "wings up" on ({@code true}) or off ({@code false}).
398 * @param wingsDown whether to turn the "wings down" on ({@code true}) or off ({@code false}).
399 *
400 * @throws IllegalArgumentException if {@code wingsUp} and {@code wingsDown} are both {@code true}.
401 * @throws IBuddyException in case of problem while sending the usb command.
402 */
403 public void sendWings(boolean wingsDown, boolean wingsUp) throws IBuddyException {
404 if (wingsDown && wingsUp) {
405 throw new IllegalArgumentException("wingsDown and wingsUp cannot be both true at the same time");
406 }
407 sendState(heart, headBlue, headGreen, headRed, wingsDown, wingsUp, rotateRight, rotateLeft);
408 }
409
410 /**
411 * Sets the wings state.
412 *
413 * @param wings the wings state.
414 *
415 * @throws IBuddyException in case of problem while sending the usb command.
416 */
417 public void sendWings(Wings wings) throws IBuddyException {
418 switch (wings) {
419 case AT_EASE:
420 sendWings(false, false);
421 break;
422 case DOWN:
423 sendWings(true, false);
424 break;
425 case UP:
426 sendWings(false, true);
427 break;
428 }
429 }
430
431 /**
432 * Turns the "rotate right" state on or off. This also automatically turns the "rotate left" state to the opposite
433 * value.
434 *
435 * @param rotateRight whether to turn the "rotate right" on ({@code true}) or off ({@code false}).
436 *
437 * @throws IBuddyException in case of problem while sending the usb command.
438 */
439 public void sendRotateRight(boolean rotateRight) throws IBuddyException {
440 sendState(heart, headBlue, headGreen, headRed, wingsDown, wingsUp, rotateRight, !rotateRight);
441 }
442
443 /**
444 * Turns the "rotate left" state on or off. This also automatically turns the "rotate right" state to the opposite
445 * value.
446 *
447 * @param rotateLeft whether to turn the "rotate left" on ({@code true}) or off ({@code false}).
448 *
449 * @throws IBuddyException in case of problem while sending the usb command.
450 */
451 public void sendRotateLeft(boolean rotateLeft) throws IBuddyException {
452 sendState(heart, headBlue, headGreen, headRed, wingsDown, wingsUp, rotateLeft, !rotateLeft);
453 }
454
455 /**
456 * Turns the "rotate right" and "rotate left" states on or off.
457 *
458 * @param rotateRight whether to turn the "rotate right" on ({@code true}) or off ({@code false}).
459 * @param rotateLeft whether to turn the "rotate left" on ({@code true}) or off ({@code false}).
460 *
461 * @throws IllegalArgumentException if {@code rotateRight} and {@code rotateLeft} are both {@code true}.
462 * @throws IBuddyException in case of problem while sending the usb command.
463 */
464 public void sendRotate(boolean rotateRight, boolean rotateLeft) throws IBuddyException {
465 if (rotateRight && rotateLeft) {
466 throw new IllegalArgumentException("rotateRight and rotateLeft cannot be both true at the same time");
467 }
468 sendState(heart, headBlue, headGreen, headRed, wingsDown, wingsUp, rotateRight, rotateLeft);
469 }
470
471 /**
472 * Sets the rotate state.
473 *
474 * @param rotate the rotate state.
475 *
476 * @throws IBuddyException in case of problem while sending the usb command.
477 */
478 public void sendRotate(Rotate rotate) throws IBuddyException {
479 switch (rotate) {
480 case AT_EASE:
481 sendRotate(false, false);
482 break;
483 case LEFT:
484 sendRotate(false, true);
485 break;
486 case RIGHT:
487 sendRotate(true, false);
488 break;
489 }
490 }
491
492 /**
493 * Opens the usb connection to the i-Buddy and initializes its state.
494 *
495 * @throws IBuddyException in case of problem while opening the usb device or if the i-Buddy was already open.
496 */
497 private synchronized void open() throws IBuddyException {
498 device = USB.getDevice(DEVICE_VENDOR, DEVICE_PRODUCT);
499 try {
500 device.open(1, 0, 0);
501 } catch (USBException e) {
502 throw new IBuddyException("Could not open i-Buddy", e);
503 }
504 }
505
506 /**
507 * Closes the usb connection to the i-Buddy.
508 *
509 * @throws IBuddyException in case of problem while closing the usb device.
510 */
511 private synchronized void close() throws IBuddyException {
512 try {
513 device.close();
514 } catch (USBException e) {
515 throw new IBuddyException("Could not close i-Buddy", e);
516 }
517 }
518 }