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 Five: Shake It
And we’re back with the penultimate post in our tutorial series on Getting Started With Myo Scripts (Parts one, two, three and four here)! Last time I asked you to implement and use a
conditionallySwapWave function. Did you get it?
This is what I did:
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
and here is what
onPoseEdge now looks like:
function onPoseEdge(pose, edge) myo.debug("onPoseEdge: " .. pose .. ": " .. edge) pose = conditionallySwapWave(pose) if (edge == "on") then if (pose == "waveOut") then onWaveOut() elseif (pose == "waveIn") then onWaveIn() elseif (pose == "fist") then onFist() elseif (pose == "fingersSpread") then onFingersSpread() end end end
This is a pretty standard technique, so you will probably see that exact function in many example scripts. Keep it in your back pocket for your own use in the future.
Watching the debug output isn’t always easy when you are trying to build a script. A useful technique is to give yourself a little vibration feedback whenever a gesture is detected. The best way to do that is with
myo.notifyUserAction(). You can make the Myo armband vibrate with the
myo.vibrate(vibrationType) function, where
vibrationType can be one of
long. Try putting in different combinations of vibration in each of the pose functions we created.
Once you’ve played around with it a bit, check out what I did:
function onWaveOut() myo.debug("Next") myo.vibrate("short") myo.keyboard("tab", "press") end function onWaveIn() myo.debug("Previous") myo.vibrate("short") myo.vibrate("short") myo.keyboard("tab","press","shift") end function onFist() myo.debug("Enter") myo.notifyUserAction() myo.keyboard("return","press") end function onFingersSpread() myo.debug("Escape") myo.vibrate("long") myo.keyboard("escape", "press") end
In a real script, you probably don’t want to vibrate so much. Generally, a gesture a user performs will have an immediately visible result on screen. If that’s the case, think about whether you really need to vibrate or not.
Locking and Unlocking
By default, until a user does the unlock gesture with an application running that can accept Myo input, none of the other poses are detected or let through. This is the standard unlock policy, and it’s pretty handy. If that flow doesn’t work for your script (for example, if you are controlling a game where the Myo should be active at all times), you can disable this behaviour by setting the lock policy:
You can turn it back on with
We’re not going to do that though. In fact, the standard unlock policy on it’s own is perfectly good for this little script, and we could stop here if we wanted. The standard policy is designed to easily let the user input a command before the armband locks again, and that’s all we’re doing. Sometimes though, you may want to let them hold a single gesture (like to adjust the volume) or enter a series of commands.
This is pretty straightforward to accomplish with the
myo.unlock(unlockType) function. This will let you programmatically unlock (or keep unlocked) a Myo armband. You can send either
timed will reset the clock and keep the armband unlocked for a few more seconds, and
hold will keep it unlocked until you manually lock it (or set it back to
timed and wait).
Let’s tweak our script a bit so that the user can hold any of our key presses as long as they hold the pose that triggers them. To do that, we’re going to modify
onPoseEdge and our pose handlers (
onWaveOut, etc) so that we use the edge value of
onPoseEdge to control the edge value of the key press.
The first problem is that
onPoseEdge gives you either
myo.keyboard() needs either
up. What we need to do is write some code that converts the one into the other and saves it in a local variable. Give that a try now.
This is a reasonable solution you could have come up with:
local keyEdge if (edge == "on") then keyEdge = "down" else keyEdge = "up" end
“Local” means that the variable only exists in the current “scope” (ie, in the current execution of the function or code block we’re in. After that it’s gone forever and you can reuse the name). All the arguments to a function like pose and edge are local automatically. Every other variable is available anywhere in your script, which can get a bit confusing. It’s best to keep things local if you can.
If you came up with a “convertEdge” function similar to
conditionallySwapWave that would also be a perfectly reasonable idea.
There are actually a bunch of ways you could reasonably do this. But I’m going to show you one line crazy that means the same thing:
local keyEdge = edge == "off" and "up" or "down"
Yeah, just trust me. It relies on some quirks of how Lua handles logical operators and order of operations. You can think of it as a one line if/else statement of the form
I tell you because you’ll probably see this in other scripts since it’s a really concise way to convert our pose edge to what we need for
myo.keyboard(). Feel free to use it or not, but now you should at least recognize it in the future.
Anyway, our updated
onPoseEdge and pose functions now looks like this:
function onPoseEdge(pose, edge) myo.debug("onPoseEdge: " .. pose .. ": " .. edge) pose = conditionallySwapWave(pose) 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
If you try and use that, you’ll notice the armband locking in the middle of holding a pose, and you can still only do one or two of them.
To keep the Myo armband unlocked while it’s still in use, every time a valid pose is detected we want to call
myo.unlock(unlockType). Unlock type will be either
hold if the user is holding a pose, or
timed if they’ve released it.
Homework time! See if you can figure out how to do that. Join me tomorrow for the answer, in the final part of our Getting Started with Myo Scripts tutorial!