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: Mouse Control Enabled
But first, this is what my script looks like at this stage:
scriptId = 'com.thalmic.examples.racethesun' scriptTitle = "Race The Sun" scriptDetailsUrl = "" centreYaw = 0 centreRoll = 0 deltaRoll = 0 YAW_DEADZONE = .1 TWOPI = math.pi * 2 flyingLeft = false flyingRight = false printCount = 0 function onForegroundWindowChange(app, title) myo.debug("onForegroundWindowChange: " .. app .. ", " .. title) local titleMatch = string.match(title, "Race The Sun") ~= nil or string.match(title, "RaceTheSun") ~= nil myo.debug("Race the Sun: " .. tostring(titleMatch)) return titleMatch; end function onPoseEdge(pose, edge) myo.debug("onPoseEdge: " .. pose .. ", " .. edge) if (edge == "on") then if (pose == "fist") then centre() elseif (pose == "fingersSpread") then escape() end end end function activeAppName() return "Race The Sun" end function centre() myo.debug("Centred") centreYaw = myo.getYaw() myo.vibrate("short") end function escape() myo.debug("Escape!") centreYaw = 0 myo.keyboard("escape","press") end function onPeriodic() if (centreYaw == 0) then return end local currentYaw = myo.getYaw() local currentRoll = myo.getRoll() local deltaYaw = calculateDeltaRadians(currentYaw, centreYaw) deltaRoll = calculateDeltaRadians(currentRoll, centreRoll); printCount = printCount + 1 if printCount >= 200 then myo.debug("deltaYaw = " .. deltaYaw .. ", centreYaw = " .. centreYaw .. ", currentYaw = " .. currentYaw) myo.debug("deltaRoll = " .. deltaRoll) printCount = 0 end if (deltaYaw > YAW_DEADZONE) then flyLeft() elseif (deltaYaw < -YAW_DEADZONE) then flyRight() else flyNeutral() end end function flyLeft() if (flyingRight) then myo.debug("Not flying right"); myo.keyboard("right_arrow","up") flyingRight = false end if (not flyingLeft) then myo.debug("Flying left"); myo.keyboard("left_arrow","down") flyingLeft = true; end end function flyRight() if (flyingLeft) then myo.debug("Not flying left"); myo.keyboard("left_arrow","up") flyingLeft = false end if (not flyingRight) then myo.debug("Flying right"); myo.keyboard("right_arrow","down") flyingRight = true end end function flyNeutral() if (flyingLeft) then myo.debug("Not flying left"); myo.keyboard("left_arrow","up") flyingLeft = false end if (flyingRight) then myo.debug("Not flying right"); myo.keyboard("right_arrow","up") flyingRight = false end end function calculateDeltaRadians(currentYaw, centreYaw) local deltaYaw = currentYaw - centreYaw if (deltaYaw > math.pi) then deltaYaw = deltaYaw - TWOPI elseif(deltaYaw < -math.pi) then deltaYaw = deltaYaw + TWOPI end return deltaYaw end
You know what sucks? Switching between using the Myo armband to play Race The Sun, and then back to the mouse to try and navigate menus or restart the game.
Wouldn’t it be nice to be able to use your Myo to control the mouse?
Yes, Myo Script has cursor control built right in. It’s trivial to enable, too: myo.controlMouse(enabled).
Set that to true in a script and you’ll immediately notice the cursor being moved around by your Myo armband. You will then notice immediately after that there is no way to “lift up” the mouse and center it again, so to move further and further left you will have to start spinning in your chair. The way you solve this is by letting the user turn off mouse control, move their arm, and then turn it back on again.
In other situations you might consider a “clutch” approach where when the user is holding a certain pose the mouse control is disabled, allowing them to recenter their arm. We actually only really want mouse control on in certain situations though, so we’re going to just enable and disable it with the wave out pose. Try that now. Also go ahead and make wave in fire a left click. It’s up to you if you want to switch these based on what arm the user is wearing their Myo on, but a wave in is usually much easier to do without jostling your arm (thereby moving the cursor), so I’m not going to. Remember it should still fire jump (only) on wave “up” or “down”.
You’ll probably find that works pretty well, but you may end up accidentally turning the mouse on while playing the game. Let’s turn this into a timed gesture (i.e. more than just a pose).
Instead, let’s try a hold. We’re going to ask the user to hold the wave out gesture for about 400 milliseconds before we toggle mouse control on or off. We’re nearing the end of our Myo Script tutorials, so see if you can work out how to do that without any more advice.
Need a hint? Try highlighting one of these if you get stuck:
- You’ll need to capture the current time when the user starts double tap
- You can get the current time in milliseconds with myo.getTimeMilliseconds()
- If the user has started wave out, you need to periodically check if the current time is bigger than the double tap start time, plus 400 milliseconds.
- “periodically” means in onPeriodic
- Don’t forget to clear the start time once the mouse is toggled
- What should happen if wave out’s edge is “off”?
- It should also clear the start time
That’s the meat of this script. There are a few more bits of polish we should apply to make it really work well. When mouse control is enabled, the rudder should lose its centre so things don’t go crazy when it’s disabled. You should also make sure the left and right arrows are pressed “up”, or they will break the menus. Fist should disable mouse control in addition to centring the yaw and roll. Once everything is working, you can turn off all the debug output.
Try that now. Tomorrow, we’ll be back in our final part of this tutorial with the complete script and some thoughts on where to go next.