Let's talk about Hub.run(unsigned int duration_ms). If you're writing an application using the Windows or Mac SDK (or some of the tools and bindings built with it), this is for you.

Generally, the Myo SDK is pretty easy and straightforward (as long as you are comfortable with quaternions). The one thing I do see a lot of persistent confusion over is what exactly you should be passing into Hub.run() and what it means. Let's break it down.

This is what is in the HelloMyo.cpp sample:

  


// Finally we enter our main loop.
        while (1) {

// In each iteration of our main loop, we run the Myo event loop for a set number of milliseconds.

// In this case, we wish to update our display 20 times a second, so we run for 1000/20 milliseconds.
            hub.run(1000/20);

// After processing events, we call the print() member function we defined above to print out the values we've

// obtained from any events that have occurred.
            collector.print();
        }

As it says in the comments, the value you pass in to hub.run() sets how long the hub.run() call will block and process Myo events for. That's it. It does NOT affect the sensor frequency. The IMU always runs at 50Hz and the EMG data is always at 200Hz. The only thing that changes is how often the rest of the code in the loop gets executed.

So if we use hub.run(50) like the sample, it will block for 50 milliseconds, and process all Myo events during that time. Once 50ms has elapsed, run will exit and the rest of your code will execute. In HelloMyo.cpp, we call collector.print(). That just updates the display. Since we get IMU data every 20ms, we are actually throwing out three out of five samples. That's fine because were are just trying to give the user a rough idea of what their armband is doing. We've already reduced the resolutions down to a handful of *s, no one is going to be able to tell that the display is only updated at 20fps.

What about if we we use hub.run(1)? In that case, most of the time hub.run(1) will block for 1ms, during which time no Myo events will be received. Then the rest of the loop will get executed, "updating" the display with the same data. Every 20ms or so, an IMU reading will come in, and the display will actually update with the new information.

This means you'd be updating up to 20x more often than necessary. That's a big waste of time, especially since updating the display isn't free.

Maybe you think "Ok, so I should get IMU data every 20ms. Maybe I should try hub.run(20), since that's frequency the IMU runs at."

That's basically fine, though you are only guaranteed that the samples will be read every 20ms. It could take a variable amount of time for them to actually be delivered. If you want to make sure the code in your loop runs once for each set of data, that could be a problem.

If that's you hub.run(1) may not even save you. The big problem is EMG data. Due to limitations in Bluetooth LE, though the readings are spaced out every 5ms, you actually get two samples at at time. No matter what, with hub.run() only the second set of EMG data will be the newest.

That's where hub.runOnce(int duration) comes in. runOnce(duration) will block for up to duration milliseconds, but it returns as soon as a single event is processed. So if you are using runOnce(), you trade knowing exactly how often the call will block for in return for never missing a shot at an event.

Now, you can also obviously just do what you need to do in the DeviceListener callbacks (including building your own buffer to be handled in your loop), but if your architecture requires processing things in the loop itself, think about runOnce(). Or at least, think about what hub.run() is really doing.

See you next time!

Newsletter

Enter your email address and get all latest content delivered to your inbox every now and then.