View Javadoc

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 }