North (formerly Thalmic Labs), the creator of the Myo armband, was acquired by Google in June 2020. Myo sales ended in October 2018 and Myo software, hardware and SDKs are no longer available or supported. Learn more.
Part Six: The End, and the Beginning
Well, I hope you did your homework because it’s the last you’ll get in this series. We’ve talked about what Myo Scripts are, walked you through your first one, detected poses, done keyboard input, and handled vibration and locking. Today, we’ll finish off with (most of) the other callbacks, and give you the completed script.
But first, the solution from last time:
if (pose ~= "rest" and pose ~= "unknown") then -- hold if edge is on, timed if edge is off myo.unlock(edge == "off" and "timed" or "hold") end
Recognize that? I hope so, it’s the technique I mentioned last time. On any pose except rest and unknown, if
edge is off call
myo.unlock(“timed”), otherwise call
myo.unlock(“hold”). Again you could have done it with an If block or a function, but this is the most concise.
Comments are the other thing to note. If you haven’t seen them before, you can comment a line in Lua with two dashes (
–). This means the line won’t get executed, so it’s good for leaving notes for yourself or temporarily disabling parts of your code you may want to re-enable later.
The Penultimate Callbacks
We’ll go into the last callback,
onPeriodic, in the next tutorial series, but before that let’s briefly discuss the other two we haven’t talked about yet:
activeAppName is called when Myo Connect wants to know what app your script is CURRENTLY controlling. All you really need to do for this is to store the app title you get from
onForegroundWindowChange when you detect an app you want to control, and return it in
activeAppName(). You can also return
scriptTitle if it’s the same name as the application and you only control one.
onActiveChange' is even easier. You probably don’t even need to implement it. The idea is it will get called with
false when your script is not longer active. If there is any cleanup you need to do at that point, this is where you put it. You probably won’t have any though. This function also gets called with
true when your script becomes active, but since you triggered that in
onForegroundWindowChange you can do whatever setup you need in there.
onLock and onUnlock are pretty straightforward as well. If you need to do something when the armband is locked or unlocked, do it in there. If you want to create a script where the armband is unlocked with double tap and then not locked again until the user double taps again (like for a mouse control script or something), you would implement onUnlock so it calls
myo.unlock(“hold”) (and turns on the mouse):
function onUnlock() myo.unlock("hold") myo.controlMouse(true) end
We’ll talk about mouse control more in the next tutorial.
onForegroundWindowChange and implementing
activeAppName. We’re not going to use
For our simple script that tries to control everything, mine looks like this:
appTitle = "" function onForegroundWindowChange(app, title) myo.debug("onForegroundWindowChange: " .. app .. ", " .. title) appTitle = title return true end function activeAppName() return appTitle end
The Completed Script
And that’s it. Your finished script should look something like this:
scriptId = 'com.thalmic.examples.myfirstscript' scriptTitle = "My First Script" scriptDetailsUrl = "" locked = true appTitle = "" function onForegroundWindowChange(app, title) myo.debug("onForegroundWindowChange: " .. app .. ", " .. title) appTitle = title return true end function activeAppName() return appTitle end function onPoseEdge(pose, edge) myo.debug("onPoseEdge: " .. pose .. ": " .. edge) pose = conditionallySwapWave(pose) if (pose ~= "rest" and pose ~= "unknown") then -- hold if edge is on, timed if edge is off myo.unlock(edge == "off" and "timed" or "hold") end local keyEdge = edge == "off" and "up" or "down" if (pose == "waveOut") then onWaveOut(keyEdge) elseif (pose == "waveIn") then onWaveIn(keyEdge) elseif (pose == "fist") then onFist(keyEdge) elseif (pose == "fingersSpread") then onFingersSpread(keyEdge) end end function onWaveOut(keyEdge) myo.debug("Next") --myo.vibrate("short") myo.keyboard("tab", keyEdge) end function onWaveIn(keyEdge) myo.debug("Previous") --myo.vibrate("short") --myo.vibrate("short") myo.keyboard("tab",keyEdge,"shift") end function onFist(keyEdge) myo.debug("Enter") --myo.vibrate("medium") myo.keyboard("return",keyEdge) end function onFingersSpread(keyEdge) myo.debug("Escape") --myo.vibrate("long") myo.keyboard("escape", keyEdge) end function conditionallySwapWave(pose) if myo.getArm() == "left" then if pose == "waveIn" then pose = "waveOut" elseif pose == "waveOut" then pose = "waveIn" end end return pose end
It’s pretty basic, but shows you how to do key things like print to the debug console and press certain keys in response to specific poses. That’s all you need to Myo-enable a lot of interesting apps.
One thing to note is that, while these scripts are written in Lua, the final version should have a .myo extension. This will let users double click on them to have them automatically loaded into the Application Manager. The downside of this is if you try and edit a .myo file it won’t have any of that nice pretty syntax highlighting without a bit of work. In Notepad++, you can go to Settings -> Style Configurator and add .myo as a user filetype for Lua.
Our next tutorial is going to target a specific game (Race The Sun, if you’re curious) and show you how to use
onPeriodic and the IMU, which opens up even more possibilities. Until then, get scripting, and good luck! If you have any questions post them in the Developer Forums, or hit me up on Twitter at @thalmicdev!