package de.voidplus.myo; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; import com.thalmic.myo.FirmwareVersion; import com.thalmic.myo.enums.StreamEmgType; import processing.core.PApplet; import processing.core.PVector; public class Myo { // Processing private PApplet parent; // private PrintStream stream; // Logging private boolean verbose; private int verboseLevel; // Myo-Lifecycle private com.thalmic.myo.Myo myo; private com.thalmic.myo.Hub hub; private Collector collector; private int frequency; // Myo-Basics private String firmware; protected Arm arm; protected Pose pose; protected LockingPolicy lockingPolicy; protected PVector orientation, accelerometer, gyroscope; protected int rssi; protected int[] emg; protected boolean withEmg; private final static String NAME = "Myo"; private final static String VERSION = "0.8.2.1"; private final static String MYO_SDK_VERSION = "0.8.1"; private final static String MYO_FIRMWARE_VERSION = "1.1.755"; private final static String MYO_FIRMWARE_VERSION_ALPHA = "1.1.5"; private final static String REPOSITORY = "https://github.com/nok/myo-processing"; public Myo(PApplet parent) { PApplet.println("# "+Myo.NAME+" v"+Myo.VERSION+" - Support: Myo SDK v"+Myo.MYO_SDK_VERSION+", Firmware v"+Myo.MYO_FIRMWARE_VERSION+", Alpha Firmware v"+Myo.MYO_FIRMWARE_VERSION_ALPHA+" - "+Myo.REPOSITORY); // Add myo.framework to Java library path manually if (System.getProperty("os.name").toLowerCase().contains("mac")) { try { String framework = new File(Myo.class.getProtectionDomain() .getCodeSource().getLocation().toURI()).getParentFile() .toString() + File.separator + "macosx" + File.separator + "myo.framework"; System.setProperty("java.library.path", framework); // stream = System.err; // System.setErr(new PrintStream(new ByteArrayOutputStream())); } catch (Exception e) { e.printStackTrace(); } } parent.registerMethod("pre", this); // parent.registerMethod("post", this); parent.registerMethod("dispose", this); this.parent = parent; this.setVerbose(false) .setVerboseLevel(1) .setFrequency(30); this.hub = new com.thalmic.myo.Hub(); this.myo = hub.waitForMyo(10000); // if (System.getProperty("os.name").toLowerCase().contains("mac")) { // System.setErr(stream); // } if (this.myo == null) { throw new RuntimeException("Unable to find a Myo!"); } this.log("Connected to a Myo armband."); if (this.myo != null) { new Thread() { public void run() { while (true) { hub.run(frequency); } } }.start(); } this.collector = new Collector(this); this.hub.addListener(this.collector); this.withEmg = false; this.arm = new Arm(); this.pose = new Pose(); this.setLockingPolicy(Myo.LockingPolicy.STANDARD); this.orientation = new PVector(); this.accelerometer = new PVector(); this.gyroscope = new PVector(); } // ------------------------------------------------------------------------------ // Lifecycle of PApplet sketch public void pre(){ if (this.myo != null) { this.hub.runOnce(this.frequency); } } public void dispose(){ if (this.myo != null) { this.hub.removeListener(this.collector); } } // ------------------------------------------------------------------------------ // Raw access /** * Get access to the raw instance of class Myo. * @return Active instance of class com.thalmic.myo.Myo. */ public com.thalmic.myo.Myo getRawMyo() { return this.myo; } /** * Get access to the raw instance of class Hub. * @return Active instance of class com.thalmic.myo.Hub. */ public com.thalmic.myo.Hub getRawHub() { return this.hub; } // ------------------------------------------------------------------------------ // Interface /** * Intern method to route a specific callback with dynamic data. * * @param object Object, which has to implement the method. * @param method Name of method, which will be called. * @param classes Array of classes, which the method has to implement as signature. * @param objects Array of objects, which stores valuable data for callback. */ protected void dispatch(Object object, String method, Class[] classes, Object[] objects, int logLevel){ boolean success = false; if (method == null) { method = "myoOn"; } if (classes.length == objects.length) { try { object.getClass().getMethod( method, classes ).invoke( this.parent, objects ); success = true; } catch (Exception e) { // e.printStackTrace(); } finally { if(success){ this.log("Method: "+method+"(...); has been called.", logLevel); } } } } protected void dispatch(String method, Class[] classes, Object[] objects, int logLevel){ this.dispatch(this.parent, method, classes, objects, logLevel); } protected void dispatch(String method, Class[] classes, Object[] objects){ this.dispatch(this.parent, method, classes, objects, 1); } protected void dispatch(Class[] classes, Object[] objects, int logLevel){ this.dispatch(this.parent, null, classes, objects, logLevel); } protected void dispatch(Class[] classes, Object[] objects){ this.dispatch(this.parent, null, classes, objects, 1); } // ------------------------------------------------------------------------------ // Commands /** * The device will vibrate for haptic feedback. * @param level Set the level of vibration duration [1,2,3]. * @return */ public Myo vibrate(int level) { switch (level) { case 1: this.log("Vibrating short ..."); this.myo.vibrate(com.thalmic.myo.enums.VibrationType.VIBRATION_SHORT); case 2: this.log("Vibrating medium ..."); this.myo.vibrate(com.thalmic.myo.enums.VibrationType.VIBRATION_MEDIUM); case 3: this.log("Vibrating long ..."); this.myo.vibrate(com.thalmic.myo.enums.VibrationType.VIBRATION_LONG); } return this; } public Myo vibrate(){ return this.vibrate(2); } // ------------------------------------------------------------------------------ // Locking /** * Force the Myo to lock immediately. * @return */ public Myo lock() { this.myo.lock(); return this; } /** * Unlock the Myo. * @param mode * @return */ public Myo unlock(Unlock mode) { switch (mode) { case HOLD: this.myo.unlock(com.thalmic.myo.enums.UnlockType.UNLOCK_HOLD); break; case TIMED: default: this.myo.unlock(com.thalmic.myo.enums.UnlockType.UNLOCK_TIMED); break; } return this; } /** * Set the locking policy for Myos connected to the Hub. * @param policy * @return */ public Myo setLockingPolicy(LockingPolicy policy){ switch (policy) { case NONE: this.hub.setLockingPolicy(com.thalmic.myo.enums.LockingPolicy.LOCKING_POLICY_NONE); break; case STANDARD: default: this.hub.setLockingPolicy(com.thalmic.myo.enums.LockingPolicy.LOCKING_POLICY_STANDARD); break; } return this; } // ------------------------------------------------------------------------------ // EMG /** * Enable EMG mode. * @return */ public Myo withEmg() { this.emg = new int[8]; this.myo.setStreamEmg(StreamEmgType.STREAM_EMG_ENABLED); this.withEmg = true; return this; } /** * Disable EMG mode. * @return */ public Myo withoutEmg() { this.myo.setStreamEmg(StreamEmgType.STREAM_EMG_DISABLED); this.withEmg = false; return this; } /** * Get raw data of EMG sensors. * * @return */ public int[] getEmg() { return this.emg; } // ------------------------------------------------------------------------------ // Setters & getters /** * Set the duration to access data. * * @param frequency Time in milliseconds. * @return */ public Myo setFrequency(int frequency) { this.frequency = frequency; return this; } /** * Set the firmware of device. * * @param firmwareVersion * @return */ protected Myo setFirmware(FirmwareVersion firmwareVersion) { if (firmwareVersion == null) { this.firmware = ""; } else { this.firmware = firmwareVersion.getFirmwareVersionMajor() +"."+firmwareVersion.getFirmwareVersionMinor() +"."+firmwareVersion.getFirmwareVersionPath(); } return this; } /** * Get the firmware of device. * * @return */ public String getFirmware() { if (this.firmware == null) { return ""; } return this.firmware; } /** * Get the name of the latest pose. * * @return Name of latest pose. */ public String getPose() { return this.pose.getType().toString().toUpperCase(); } /** * Get the type of recognized arm. * * @return Type of recognized arm. */ public String getArm() { return this.arm.getType().toString().toUpperCase(); } /** * Arm recognized? * * @return */ public boolean hasArm(){ return this.arm.hasArm(); } /** * Left arm? * * @return */ public Boolean isArmLeft(){ return this.arm.isLeft(); } /** * Right arm? * * @return */ public Boolean isArmRight(){ return !this.isArmLeft(); } /** * Get orientation values of device. * * @return Orientation as PVector, where 'x' is the 'roll' value, 'y' is the 'pitch' value and 'z' the 'yaw' value. */ public PVector getOrientation(){ return this.orientation; } /** * Get gyroscope values of device. * * @return */ public PVector getAccelerometer(){ return this.accelerometer; } /** * Get gyroscope values of device. * * @return */ public PVector getGyroscope(){ return this.gyroscope; } // ------------------------------------------------------------------------------ // Verbose & logging /** * Print debug information to the console. * * @param verbose * @return */ public Myo setVerbose(boolean verbose){ this.verbose = verbose; return this; } /** * Set the level of the log level. * * @param level Set the level of the log level [1,2,3]. Three (3) will print lightweight events, too. */ public Myo setVerboseLevel(int level) { if (level > 0 && level < 4) { this.verboseLevel = level; } else { this.verboseLevel = 1; } return this; } /** * Print log messages to the console. * * @param message Set the readable message of that log. * @param verboseLevel Set the priority level of that log. * @return */ protected Myo log(String message, int verboseLevel){ if (this.verbose == true && verboseLevel <= this.verboseLevel) { PApplet.println("# " + Myo.NAME + ": LOG (" + verboseLevel + "): " + message); } return this; } protected Myo log(String message){ return this.log(message, 1); } // ------------------------------------------------------------------------------ // Enums public enum Event { PAIR, UNPAIR, CONNECT, DISCONNECT, ARM_SYNC, ARM_UNSYNC, POSE, ORIENTATION, ACCELEROMETER, GYROSCOPE, RSSI, EMG, LOCK, UNLOCK } public enum LockingPolicy { NONE, STANDARD } public enum Unlock { HOLD, TIMED } }