View Javadoc

1   /*
2    * Copyright 2007-2008 Volker Fritzsch
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License. 
15   */
16  package motej;
17  
18  import javax.swing.event.EventListenerList;
19  
20  import motej.event.AccelerometerEvent;
21  import motej.event.AccelerometerListener;
22  import motej.event.CoreButtonEvent;
23  import motej.event.CoreButtonListener;
24  import motej.event.DataEvent;
25  import motej.event.DataListener;
26  import motej.event.ExtensionEvent;
27  import motej.event.ExtensionListener;
28  import motej.event.IrCameraEvent;
29  import motej.event.IrCameraListener;
30  import motej.event.MoteDisconnectedEvent;
31  import motej.event.MoteDisconnectedListener;
32  import motej.event.StatusInformationListener;
33  import motej.request.CalibrationDataRequest;
34  import motej.request.PlayerLedRequest;
35  import motej.request.RawByteRequest;
36  import motej.request.ReadRegisterRequest;
37  import motej.request.ReportModeRequest;
38  import motej.request.RumbleRequest;
39  import motej.request.StatusInformationRequest;
40  import motej.request.WriteRegisterRequest;
41  
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  /**
46   * 
47   * <p>
48   * @author <a href="mailto:vfritzsch@users.sourceforge.net">Volker Fritzsch</a>
49   */
50  public class Mote {
51  	
52  	private Logger log = LoggerFactory.getLogger(Mote.class);
53  
54  	private OutgoingThread outgoing;
55  
56  	private IncomingThread incoming;
57  	
58  	private ExtensionProvider extensionProvider = new ExtensionProvider();
59  	
60  	private Extension currentExtension;
61  
62  	private StatusInformationReport statusInformationReport;
63  
64  	private CalibrationDataReport calibrationDataReport;
65  
66  	private EventListenerList listenerList = new EventListenerList();
67  
68  	private String bluetoothAddress;
69  
70  	public Mote(String bluetoothAddress) {
71  		try {
72  			this.bluetoothAddress = bluetoothAddress;
73  			
74  			// I'm interested if one of the Thread is disconnected
75  			addMoteDisconnectedListener(new MoteDisconnectedListener<Mote>(){
76  				public void moteDisconnected(MoteDisconnectedEvent<Mote> evt) {
77  					// Something goes wrong with one of my Thread
78  					// Try to properly cleanup everything
79  					disconnect();
80  				}
81  			});
82  			
83  			outgoing = new OutgoingThread(this, bluetoothAddress);
84  			incoming = new IncomingThread(this, bluetoothAddress);
85  			
86  			incoming.start();
87  			outgoing.start();
88  			
89  			outgoing.sendRequest(new StatusInformationRequest());
90  			outgoing.sendRequest(new CalibrationDataRequest());
91  		} catch (Exception ex) {
92  			throw new RuntimeException(ex.fillInStackTrace());
93  		}
94  	}
95  
96  	public void addAccelerometerListener(AccelerometerListener<Mote> listener) {
97  		listenerList.add(AccelerometerListener.class, listener);
98  	}
99  
100 	public void addCoreButtonListener(CoreButtonListener listener) {
101 		listenerList.add(CoreButtonListener.class, listener);
102 	}
103 
104 	public void addDataListener(DataListener listener) {
105 		listenerList.add(DataListener.class, listener);
106 	}
107 	
108 	public void addExtensionListener(ExtensionListener listener) {
109 		listenerList.add(ExtensionListener.class, listener);
110 	}
111 
112 	public void addIrCameraListener(IrCameraListener listener) {
113 		listenerList.add(IrCameraListener.class, listener);
114 	}
115 
116 	public void addMoteDisconnectedListener(MoteDisconnectedListener<Mote> listener) {
117 		listenerList.add(MoteDisconnectedListener.class, listener);
118 	}
119 
120 	public void addStatusInformationListener(StatusInformationListener listener) {
121 		listenerList.add(StatusInformationListener.class, listener);
122 	}
123 
124 	public void disableIrCamera() {
125 		// 1. Disable IR Camera
126 		outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 19, 0 }));
127 		
128 		// 2. Disable IR Camera 2
129 		outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 26, 0 }));
130 	}
131 
132 	public void disconnect() {
133 		if (log.isInfoEnabled()) {
134 			log.info("Disconnecting mote " + bluetoothAddress);
135 		}
136 		if (outgoing != null) {
137 			outgoing.disconnect();
138 			try {
139 				outgoing.join(5000l);
140 			} catch (InterruptedException ex) {
141 				log.error(ex.getMessage(), ex);
142 			}
143 		}
144 		if (incoming != null) {
145 			incoming.disconnect();
146 			try {
147 				incoming.join(5000l);
148 			} catch (InterruptedException ex) {
149 				log.error(ex.getMessage(), ex);
150 			}
151 		}
152 	}
153 
154 	/**
155 	 * Enables the IR Camera in basic mode with Marcan sensitivity.
156 	 */
157 	public void enableIrCamera() {
158 		enableIrCamera(IrCameraMode.BASIC, IrCameraSensitivity.WII_LEVEL_3);
159 	}
160 
161 //	public void enableIrCamera(IrCameraMode mode, IrCameraSensitivity sensitivity) {
162 //		// 1. Enable IR Camera (Send 0x04 to Output Report 0x13)
163 //		outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 19, 4 }));
164 //
165 //		// 2. Enable IR Camera 2 (Send 0x04 to Output Report 0x1a)
166 //		outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 26, 4 }));
167 //
168 //		// 3. Write 0x08 to register 0xb00030
169 //		outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0,
170 //				0x00, 0x30 }, new byte[] { 0x08 }));
171 //
172 //		// 4. Write Sensitivity Block 1 to registers at 0xb00000
173 //		outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0,
174 //				0x00, 0x00 }, sensitivity.block1()));
175 //
176 //		// 5. Write Sensitivity Block 2 to registers at 0xb0001a
177 //		outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0,
178 //				0x00, 0x1a }, sensitivity.block2()));
179 //
180 //		// 6. Write Mode Number to register 0xb00033
181 //		outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0,
182 //				0x00, 0x33 }, new byte[] { mode.modeAsByte() }));
183 //	}
184 	
185 	public void enableIrCamera(IrCameraMode mode, IrCameraSensitivity sensitivity) {
186 		// 1. Enable IR Pixel Clock (Send 0x06 to Output Report 0x13)
187 		outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 0x13, 0x06 }));
188 
189 		// 2. Enable IR Logic (Send 0x06 to Output Report 0x1a)
190 		outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 0x1a, 0x06 }));
191 
192 		// 3. Write 0x01 to register 0xb00030
193 		outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0,
194 				0x00, 0x30 }, new byte[] { 0x01 }));
195 
196 		// 4. Write Sensitivity Block 1 to registers at 0xb00000
197 		outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0,
198 				0x00, 0x00 }, sensitivity.block1()));
199 
200 		// 5. Write Sensitivity Block 2 to registers at 0xb0001a
201 		outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0,
202 				0x00, 0x1a }, sensitivity.block2()));
203 
204 		// 6. Write Mode Number to register 0xb00033
205 		outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0,
206 				0x00, 0x33 }, new byte[] { mode.modeAsByte() }));
207 		
208 		// 7. Write 0x08 to register 0xb00030
209 		outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0,
210 				0x00, 0x30 }, new byte[] { 0x08 }));
211 	}
212 
213 	@Override
214 	public boolean equals(Object obj) {
215 		if (!(obj instanceof Mote))
216 			return false;
217 
218 		return hashCode() == obj.hashCode();
219 	}
220 
221 	@SuppressWarnings("unchecked")
222 	protected void fireMoteDisconnectedEvent() {
223 		MoteDisconnectedListener<Mote>[] listeners = listenerList.getListeners(MoteDisconnectedListener.class);
224 		MoteDisconnectedEvent<Mote> evt = new MoteDisconnectedEvent<Mote>(this);
225 		for (MoteDisconnectedListener<Mote> l : listeners) {
226 			l.moteDisconnected(evt);
227 		}
228 	}
229 
230 	@SuppressWarnings("unchecked")
231 	protected void fireAccelerometerEvent(int x, int y, int z) {
232 		AccelerometerListener<Mote>[] listeners = listenerList.getListeners(AccelerometerListener.class);
233 		AccelerometerEvent<Mote> evt = new AccelerometerEvent<Mote>(this, x, y, z);
234 		for (AccelerometerListener<Mote> l : listeners) {
235 			l.accelerometerChanged(evt);
236 		}
237 	}
238 
239 	protected void fireCoreButtonEvent(int modifiers) {
240 		CoreButtonListener[] listeners = listenerList.getListeners(CoreButtonListener.class);
241 		CoreButtonEvent evt = new CoreButtonEvent(this, modifiers);
242 		for (CoreButtonListener l : listeners) {
243 			l.buttonPressed(evt);
244 		}
245 	}
246 	
247 	protected void fireExtensionConnectedEvent() {
248 		ExtensionListener[] listeners = listenerList.getListeners(ExtensionListener.class);
249 		ExtensionEvent evt = new ExtensionEvent(this, currentExtension);
250 		for (ExtensionListener l : listeners) {
251 			l.extensionConnected(evt);
252 		}
253 	}
254 	
255 	protected void fireExtensionDisconnectedEvent() {
256 		ExtensionListener[] listeners = listenerList.getListeners(ExtensionListener.class);
257 		ExtensionEvent evt = new ExtensionEvent(this);
258 		for (ExtensionListener l : listeners) {
259 			l.extensionDisconnected(evt);
260 		}
261 	}
262 
263 	protected void fireIrCameraEvent(IrCameraMode mode, IrPoint p0, IrPoint p1, IrPoint p2, IrPoint p3) {
264 		IrCameraListener[] listeners = listenerList.getListeners(IrCameraListener.class);
265 		IrCameraEvent evt = new IrCameraEvent(this, mode, p0, p1, p2, p3);
266 		for (IrCameraListener l : listeners) {
267 			l.irImageChanged(evt);
268 		}
269 	}
270 
271 	protected void fireReadDataEvent(byte[] address, byte[] payload, int error) {
272 		if (calibrationDataReport == null && error == 0 && address[0] == 0x00 && address[1] == 0x20) {
273 			// calibration data (most probably)
274 			if (log.isDebugEnabled()) {
275 				log.debug("Received Calibration Data Report.");
276 			}
277 			CalibrationDataReport report = new CalibrationDataReport(payload[0] & 0xff, payload[1] & 0xff, payload[2] & 0xff,
278 					payload[4] & 0xff, payload[5] & 0xff, payload[6] & 0xff);
279 			calibrationDataReport = report;
280 		}
281 		
282 		if (currentExtension == null && error == 0 && address[0] == 0x00 && (address[1] & 0xff) == 0xfe && payload.length == 2) {
283 			// extension ID (most probably)
284 			if (log.isDebugEnabled()) {
285 				String id0 = Integer.toHexString(payload[0] & 0xff);
286 				String id1 = Integer.toHexString(payload[1] & 0xff);
287 				log.debug("Received Extension ID: " + (id0.length() == 1 ? "0x0" + id0 : "0x" + id0) + " " + (id1.length() == 1 ? "0x0" + id1 : "0x" + id1));
288 			}
289 			
290 			if ((payload[0] & 0xff) == 0xff
291 					&& (payload[1] & 0xff) == 0xff) {
292 				log.debug("Connection not completed, re-requesting extension id.");
293 				outgoing.sendRequest(new ReadRegisterRequest(new byte[] { (byte) 0xa4, 0x00, (byte) 0xfe }, new byte[] { 0x00, 0x02 }));
294 			} else {
295 			
296 				currentExtension = extensionProvider.getExtension(payload);
297 				if (log.isInfoEnabled()) {
298 					log.info("Found extension: " + currentExtension == null ? "null" : currentExtension.toString());
299 				}
300 				if (currentExtension != null) {
301 					currentExtension.setMote(this);
302 					currentExtension.initialize();
303 					incoming.setExtension(currentExtension);
304 					fireExtensionConnectedEvent();
305 				}
306 			}
307 		}
308 		
309 		DataListener[] listeners = listenerList.getListeners(DataListener.class);
310 		DataEvent evt = new DataEvent(address, payload, error);
311 		for (DataListener l : listeners) {
312 			l.dataRead(evt);
313 		}
314 	}
315 
316 	protected void fireStatusInformationChangedEvent(StatusInformationReport report) {
317 
318 		// decide if we should query the extension port
319 		boolean extensionChanged;
320 		if (statusInformationReport == null) {
321 			extensionChanged = report.isExtensionControllerConnected();
322 		} else {
323 			extensionChanged = statusInformationReport.isExtensionControllerConnected() != report.isExtensionControllerConnected();
324 		}
325 		
326 		statusInformationReport = report;
327 		StatusInformationListener[] listeners = listenerList.getListeners(StatusInformationListener.class);
328 		for (StatusInformationListener l : listeners) {
329 			l.statusInformationReceived(report);
330 		}
331 		
332 		if (extensionChanged) {
333 			if (!report.isExtensionControllerConnected()) {
334 				currentExtension = null;
335 				fireExtensionDisconnectedEvent();
336 			} else {
337 				// 1. initialize peripheral (writing zero to 0xa40040) 
338 				outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xa4, 0x00, 0x40 }, new byte[] { 0x00 }));
339 				
340 				// 2. read extension ID bytes from wii register (0xa400fe)
341 				outgoing.sendRequest(new ReadRegisterRequest(new byte[] { (byte) 0xa4, 0x00, (byte) 0xfe }, new byte[] { 0x00, 0x02 }));
342 			}
343 		}
344 	}
345 
346 	public String getBluetoothAddress() {
347 		return bluetoothAddress;
348 	}
349 
350 	public CalibrationDataReport getCalibrationDataReport() {
351 		return calibrationDataReport;
352 	}
353 
354 	public StatusInformationReport getStatusInformationReport() {
355 		return statusInformationReport;
356 	}
357 
358 	@Override
359 	public int hashCode() {
360 		return bluetoothAddress.hashCode();
361 	}
362 
363 	public void removeAccelerometerListener(AccelerometerListener<Mote> listener) {
364 		listenerList.remove(AccelerometerListener.class, listener);
365 	}
366 
367 	public void removeCoreButtonListener(CoreButtonListener listener) {
368 		listenerList.remove(CoreButtonListener.class, listener);
369 	}
370 
371 	public void removeDataListener(DataListener listener) {
372 		listenerList.remove(DataListener.class, listener);
373 	}
374 	
375 	public void removeExtensionListener(ExtensionListener listener) {
376 		listenerList.remove(ExtensionListener.class, listener);
377 	}
378 
379 	public void removeIrCameraListener(IrCameraListener listener) {
380 		listenerList.remove(IrCameraListener.class, listener);
381 	}
382 
383 	public void remoteMoteDisconnectedListener(MoteDisconnectedListener<Mote> listener) {
384 		listenerList.remove(MoteDisconnectedListener.class, listener);
385 	}
386 
387 	public void removeStatusInformationListener(StatusInformationListener listener) {
388 		listenerList.remove(StatusInformationListener.class, listener);
389 	}
390 
391 	public void requestStatusInformation() {
392 		outgoing.sendRequest(new StatusInformationRequest());
393 	}
394 
395 	public void rumble(long millis) {
396 		outgoing.sendRequest(new RumbleRequest(millis));
397 	}
398 
399 	public void setPlayerLeds(boolean[] leds) {
400 		outgoing.sendRequest(new PlayerLedRequest(leds));
401 	}
402 
403 	public void setReportMode(byte mode) {
404 		outgoing.sendRequest(new ReportModeRequest(mode));
405 	}
406 
407 	public void setReportMode(byte mode, boolean continuous) {
408 		outgoing.sendRequest(new ReportModeRequest(mode, continuous));
409 	}
410 	
411 	public void readRegisters(byte[] offset, byte[] size) {
412 		outgoing.sendRequest(new ReadRegisterRequest(offset, size));
413 	}
414 	
415 	public void writeRegisters(byte[] offset, byte[] payload) {
416 		outgoing.sendRequest(new WriteRegisterRequest(offset, payload));
417 	}
418 	
419 	@SuppressWarnings("unchecked")
420 	public <T extends Extension> T getExtension() {
421 		return (T) currentExtension;
422 	}
423 	
424 	@Override
425 	public String toString() {
426 		return "Mote[" + bluetoothAddress + "]";
427 	}
428 }