/* * Copyright 2007-2008 Volker Fritzsch * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package motej; import javax.swing.event.EventListenerList; import motej.event.AccelerometerEvent; import motej.event.AccelerometerListener; import motej.event.CoreButtonEvent; import motej.event.CoreButtonListener; import motej.event.DataEvent; import motej.event.DataListener; import motej.event.ExtensionEvent; import motej.event.ExtensionListener; import motej.event.IrCameraEvent; import motej.event.IrCameraListener; import motej.event.MoteDisconnectedEvent; import motej.event.MoteDisconnectedListener; import motej.event.StatusInformationListener; import motej.request.CalibrationDataRequest; import motej.request.MotionPlusActivateRequest; import motej.request.MotionPlusDeactivateRequest; import motej.request.PlayerLedRequest; import motej.request.RawByteRequest; import motej.request.ReadRegisterRequest; import motej.request.ReportModeRequest; import motej.request.RumbleRequest; import motej.request.StatusInformationRequest; import motej.request.WriteRegisterRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * <p> * @author <a href="mailto:vfritzsch@users.sourceforge.net">Volker Fritzsch</a> */ public class Mote { private Logger log = LoggerFactory.getLogger(Mote.class); private OutgoingThread outgoing; private IncomingThread incoming; private ExtensionProvider extensionProvider = new ExtensionProvider(); private Extension currentExtension; private StatusInformationReport statusInformationReport; private CalibrationDataReport calibrationDataReport; private EventListenerList listenerList = new EventListenerList(); private String bluetoothAddress; public Mote(String bluetoothAddress) { try { this.bluetoothAddress = bluetoothAddress; // I'm interested if one of the Thread is disconnected addMoteDisconnectedListener(new MoteDisconnectedListener<Mote>(){ public void moteDisconnected(MoteDisconnectedEvent<Mote> evt) { // Something goes wrong with one of my Thread // Try to properly cleanup everything disconnect(); } }); Thread.sleep(300l); outgoing = new OutgoingThread(this, bluetoothAddress); incoming = new IncomingThread(this, bluetoothAddress); incoming.start(); outgoing.start(); outgoing.sendRequest(new StatusInformationRequest()); outgoing.sendRequest(new CalibrationDataRequest()); } catch (Exception ex) { throw new RuntimeException(ex.fillInStackTrace()); } } public void addAccelerometerListener(AccelerometerListener<Mote> listener) { listenerList.add(AccelerometerListener.class, listener); } public void addCoreButtonListener(CoreButtonListener listener) { listenerList.add(CoreButtonListener.class, listener); } public void addDataListener(DataListener listener) { listenerList.add(DataListener.class, listener); } public void addExtensionListener(ExtensionListener listener) { listenerList.add(ExtensionListener.class, listener); } public void addIrCameraListener(IrCameraListener listener) { listenerList.add(IrCameraListener.class, listener); } public void addMoteDisconnectedListener(MoteDisconnectedListener<Mote> listener) { listenerList.add(MoteDisconnectedListener.class, listener); } public void addStatusInformationListener(StatusInformationListener listener) { listenerList.add(StatusInformationListener.class, listener); } public void disableIrCamera() { // 1. Disable IR Camera outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 19, 0 })); // 2. Disable IR Camera 2 outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 26, 0 })); } public void disconnect() { if (log.isInfoEnabled()) { log.info("Disconnecting mote " + bluetoothAddress); } if (outgoing != null) { outgoing.disconnect(); try { outgoing.join(5000l); } catch (InterruptedException ex) { log.error(ex.getMessage(), ex); } } if (incoming != null) { incoming.disconnect(); try { incoming.join(5000l); } catch (InterruptedException ex) { log.error(ex.getMessage(), ex); } } } /** * Enables the IR Camera in basic mode with Marcan sensitivity. */ public void enableIrCamera() { enableIrCamera(IrCameraMode.BASIC, IrCameraSensitivity.WII_LEVEL_3); } // public void enableIrCamera(IrCameraMode mode, IrCameraSensitivity sensitivity) { // // 1. Enable IR Camera (Send 0x04 to Output Report 0x13) // outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 19, 4 })); // // // 2. Enable IR Camera 2 (Send 0x04 to Output Report 0x1a) // outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 26, 4 })); // // // 3. Write 0x08 to register 0xb00030 // outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0, // 0x00, 0x30 }, new byte[] { 0x08 })); // // // 4. Write Sensitivity Block 1 to registers at 0xb00000 // outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0, // 0x00, 0x00 }, sensitivity.block1())); // // // 5. Write Sensitivity Block 2 to registers at 0xb0001a // outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0, // 0x00, 0x1a }, sensitivity.block2())); // // // 6. Write Mode Number to register 0xb00033 // outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0, // 0x00, 0x33 }, new byte[] { mode.modeAsByte() })); // } public void enableIrCamera(IrCameraMode mode, IrCameraSensitivity sensitivity) { // 1. Enable IR Pixel Clock (Send 0x06 to Output Report 0x13) outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 0x13, 0x06 })); // 2. Enable IR Logic (Send 0x06 to Output Report 0x1a) outgoing.sendRequest(new RawByteRequest(new byte[] { 82, 0x1a, 0x06 })); // 3. Write 0x01 to register 0xb00030 outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0, 0x00, 0x30 }, new byte[] { 0x01 })); // 4. Write Sensitivity Block 1 to registers at 0xb00000 outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0, 0x00, 0x00 }, sensitivity.block1())); // 5. Write Sensitivity Block 2 to registers at 0xb0001a outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0, 0x00, 0x1a }, sensitivity.block2())); // 6. Write Mode Number to register 0xb00033 outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0, 0x00, 0x33 }, new byte[] { mode.modeAsByte() })); // 7. Write 0x08 to register 0xb00030 outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xb0, 0x00, 0x30 }, new byte[] { 0x08 })); } @Override public boolean equals(Object obj) { if (!(obj instanceof Mote)) return false; return hashCode() == obj.hashCode(); } @SuppressWarnings("unchecked") protected void fireMoteDisconnectedEvent() { MoteDisconnectedListener<Mote>[] listeners = listenerList.getListeners(MoteDisconnectedListener.class); MoteDisconnectedEvent<Mote> evt = new MoteDisconnectedEvent<Mote>(this); for (MoteDisconnectedListener<Mote> l : listeners) { l.moteDisconnected(evt); } } @SuppressWarnings("unchecked") protected void fireAccelerometerEvent(double xAcceleration, double yAcceleration, double zAcceleration) { AccelerometerListener<Mote>[] listeners = listenerList.getListeners(AccelerometerListener.class); AccelerometerEvent<Mote> evt = new AccelerometerEvent<Mote>(this, xAcceleration, yAcceleration, zAcceleration); for (AccelerometerListener<Mote> l : listeners) { l.accelerometerChanged(evt); } } protected void fireCoreButtonEvent(int modifiers) { CoreButtonListener[] listeners = listenerList.getListeners(CoreButtonListener.class); CoreButtonEvent evt = new CoreButtonEvent(this, modifiers); for (CoreButtonListener l : listeners) { l.buttonPressed(evt); } } protected void fireExtensionConnectedEvent() { ExtensionListener[] listeners = listenerList.getListeners(ExtensionListener.class); ExtensionEvent evt = new ExtensionEvent(this, currentExtension); for (ExtensionListener l : listeners) { l.extensionConnected(evt); } } protected void fireExtensionDisconnectedEvent() { ExtensionListener[] listeners = listenerList.getListeners(ExtensionListener.class); ExtensionEvent evt = new ExtensionEvent(this); for (ExtensionListener l : listeners) { l.extensionDisconnected(evt); } } protected void fireIrCameraEvent(IrCameraMode mode, IrPoint p0, IrPoint p1, IrPoint p2, IrPoint p3) { IrCameraListener[] listeners = listenerList.getListeners(IrCameraListener.class); IrCameraEvent evt = new IrCameraEvent(this, mode, p0, p1, p2, p3); for (IrCameraListener l : listeners) { l.irImageChanged(evt); } } protected void fireReadDataEvent(byte[] address, byte[] payload, int error) { if (calibrationDataReport == null && error == 0 && address[0] == 0x00 && address[1] == 0x20) { // calibration data (most probably) if (log.isDebugEnabled()) { log.debug("Received Calibration Data Report."); } //CalibrationDataReport report = new CalibrationDataReport(payload[0] & 0xff, payload[1] & 0xff, payload[2] & 0xff, // payload[4] & 0xff, payload[5] & 0xff, payload[6] & 0xff); CalibrationDataReport report = new CalibrationDataReport( ((payload[4] & 0xFF) << 2) + (payload[7] & 0x3), ((payload[5] & 0xFF) << 2) + ((payload[7] & 0xC) >> 2), ((payload[6] & 0xFF) << 2) + ((payload[7] & 0x30) >> 4), ((payload[0] & 0xFF) << 2) + (payload[3] & 0x3), ((payload[1] & 0xFF) << 2) + ((payload[3] & 0xC) >> 2), ((payload[2] & 0xFF) << 2) + ((payload[3] & 0x30) >> 4) ); calibrationDataReport = report; } if (currentExtension == null && error == 0 && address[0] == 0x00 && (address[1] & 0xff) == 0xfe && payload.length == 2) { // extension ID (most probably) if (log.isDebugEnabled()) { String id0 = Integer.toHexString(payload[0] & 0xff); String id1 = Integer.toHexString(payload[1] & 0xff); log.debug("Received Extension ID: " + (id0.length() == 1 ? "0x0" + id0 : "0x" + id0) + " " + (id1.length() == 1 ? "0x0" + id1 : "0x" + id1)); } if ((payload[0] & 0xff) == 0xff && (payload[1] & 0xff) == 0xff) { log.debug("Connection not completed, re-requesting extension id."); outgoing.sendRequest(new ReadRegisterRequest(new byte[] { (byte) 0xa4, 0x00, (byte) 0xfe }, new byte[] { 0x00, 0x02 })); } else { currentExtension = extensionProvider.getExtension(payload); if (log.isInfoEnabled()) { log.info("Found extension: " + currentExtension == null ? "null" : currentExtension.toString()); } if (currentExtension != null) { currentExtension.setMote(this); currentExtension.initialize(); incoming.setExtension(currentExtension); fireExtensionConnectedEvent(); } } } DataListener[] listeners = listenerList.getListeners(DataListener.class); DataEvent evt = new DataEvent(address, payload, error); for (DataListener l : listeners) { l.dataRead(evt); } } protected void fireStatusInformationChangedEvent(StatusInformationReport report) { // decide if we should query the extension port boolean extensionChanged; if (statusInformationReport == null) { extensionChanged = report.isExtensionControllerConnected(); } else { extensionChanged = statusInformationReport.isExtensionControllerConnected() != report.isExtensionControllerConnected(); } statusInformationReport = report; StatusInformationListener[] listeners = listenerList.getListeners(StatusInformationListener.class); for (StatusInformationListener l : listeners) { l.statusInformationReceived(report); } if (extensionChanged) { if (!report.isExtensionControllerConnected()) { currentExtension = null; fireExtensionDisconnectedEvent(); } else { // 1. initialize peripheral (writing zero to 0xa40040) outgoing.sendRequest(new WriteRegisterRequest(new byte[] { (byte) 0xa4, 0x00, 0x40 }, new byte[] { 0x00 })); // 2. read extension ID bytes from wii register (0xa400fe) outgoing.sendRequest(new ReadRegisterRequest(new byte[] { (byte) 0xa4, 0x00, (byte) 0xfe }, new byte[] { 0x00, 0x02 })); } } } public String getBluetoothAddress() { return bluetoothAddress; } public CalibrationDataReport getCalibrationDataReport() { return calibrationDataReport; } public StatusInformationReport getStatusInformationReport() { return statusInformationReport; } @Override public int hashCode() { return bluetoothAddress.hashCode(); } public void removeAccelerometerListener(AccelerometerListener<Mote> listener) { listenerList.remove(AccelerometerListener.class, listener); } public void removeCoreButtonListener(CoreButtonListener listener) { listenerList.remove(CoreButtonListener.class, listener); } public void removeDataListener(DataListener listener) { listenerList.remove(DataListener.class, listener); } public void removeExtensionListener(ExtensionListener listener) { listenerList.remove(ExtensionListener.class, listener); } public void removeIrCameraListener(IrCameraListener listener) { listenerList.remove(IrCameraListener.class, listener); } public void remoteMoteDisconnectedListener(MoteDisconnectedListener<Mote> listener) { listenerList.remove(MoteDisconnectedListener.class, listener); } public void removeStatusInformationListener(StatusInformationListener listener) { listenerList.remove(StatusInformationListener.class, listener); } public void requestStatusInformation() { outgoing.sendRequest(new StatusInformationRequest()); } public void rumble(long millis) { outgoing.sendRequest(new RumbleRequest(millis)); } public void setPlayerLeds(boolean[] leds) { outgoing.sendRequest(new PlayerLedRequest(leds)); } public void setReportMode(byte mode) { outgoing.sendRequest(new ReportModeRequest(mode)); } public void setReportMode(byte mode, boolean continuous) { outgoing.sendRequest(new ReportModeRequest(mode, continuous)); } public void readRegisters(byte[] offset, byte[] size) { outgoing.sendRequest(new ReadRegisterRequest(offset, size)); } public void writeRegisters(byte[] offset, byte[] payload) { outgoing.sendRequest(new WriteRegisterRequest(offset, payload)); } @SuppressWarnings("unchecked") public <T extends Extension> T getExtension() { return (T) currentExtension; } @Override public String toString() { return "Mote[" + bluetoothAddress + "]"; } /** * Methode to activate the already plugged-in MotionPlus */ public void activateMotionPlus() { this.outgoing.sendRequest(new MotionPlusActivateRequest()); } /** * Deactivate the MotionPlus-device. */ public void deactivateMotionPlus() { this.outgoing.sendRequest(new MotionPlusDeactivateRequest()); } }