Skip to content

cortinico/myonnaise

Repository files navigation

Myonnaise 🍯

CircleCI codecov Download License Twitter

projectlogo

An Android library to interact with your Thalmic Myo, written in Kotlin and using RxJava2.

This repo contains also a sample app that showcases the usage of the library: Myo EMG Visualizer. With this app you can stream EMG Raw data from your device and save it as a CSV. The app is also available on the play store:

Get it on Google Play

DISCLAIMER: If you don't know what a Myo is, please go here: support.getmyo.com. Please note that you need a Myo in order to use this library/app.

Getting Started πŸ‘£

Myonnaise is distributed through JCenter. To use it you need to add the following Gradle dependency to your android app gradle file (NOT the root file).

dependencies {
   implementation 'com.ncorti:myonnaise:1.0.0'
}

Example 🚸

After setting up the Gradle dependency, you will be able to access two main classes: Myonnaise and Myo.

  • Myonnaise is the entry point where you can trigger a bluetooth scan to search for nearby devices. A scan will return you one or mode BluetoothDevice (from the android framework). If you don't know your Myo's address a priori, you need to show those devices to the user and allow him to pick one.

  • Myo is the class that will allow you to connect to your device, send commands and start the streaming. You need a BluetoothDevice in order to create a Myo.

Searching for a Myo

First, you need to find a Myo with a bluetooth scan.

** ⚠️ Please note that you need to request the user the ACCESS_COARSE_LOCATION permission. If not, the scan will be empty ⚠️ **

To start a bluetooth scan, you can use the startScan() method:

Myonnaise(context).startScan()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                // Do something with the found device
                println(it.address)
            })

This method will return a Flowable that will publish all the BluetoothDevice that are discovered nearby. Please note that the scan will stop only when you cancel the Flowable.

Alternatively, you can also provide a timeout and the scan will stop after the timeout:

Myonnaise(context).startScan(5, TimeUnit.MINUTES)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                // Do something with the found device
                println(it.address)
            })

Once you found a BluetoothDevice that is a Myo, you can get a Myo instance from it.

val myMyo = Myonnaise.getMyo(foundDevice)

Connecting to a Myo

Connecting or disconnecting to a Myo is really easy:

// To Connect
myMyo.connect(getContext())

// To Disconnect
myMyo.disconnect()

Connecting and disconnecting are not syncronous operations. You have to wait that the device is successfully connected before start sending commands. You can get notified of status updates using the RxJava statusObservable.

myMyo.statusObservable()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe {
            when (it) {
                MyoStatus.CONNECTED -> { ... }
                MyoStatus.CONNECTING -> { ... }
                MyoStatus.READY -> { ... }
                else -> { ... } // DISCONNECTED
            }
        }

In order to send command to your Myo, your Myo should be in the READY state. If the Myo is not ready, commands will be ignored.

Sending a Command

To send a command you can use the sendCommand() method. For example, you can let your device vibrate with:

myMyo.sendCommand(CommandList.vibration1())

Commands will be processed by the library and sent to the device (the library has a queue to process all the commands).

List of all available commands is in the CommandList.kt file.

Starting the Streaming

You can start/stop the streaming using again the sendCommand method:

// Start Streaming
myMyo.sendCommand(CommandList.emgUnfilteredOnly())

// Stop Streaming
myMyo.sendCommand(CommandList.stopStreaming())

You will start receiving the streaming of data as a Flowable<FloatArray> through the dataFlowable() method. To collect the data, just subscribe to the flowable:

myMyo.dataFlowable()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe {
            println(it) // it is an array of 8 floats.
        }

Streaming Frequency

You can change the streaming frequency to receive less data. By default data is streamed at 200Hz (the max supported by the device). You can subsample the data if you set the frequency property:

myMyo.frequency = 50 // Streaming at 50Hz

Allowed values are from 0 (reset to default) to 200.

Keep Alive

The Myo will go to sleep if he receives no intereaction within some seconds. For this reason we are sending a CommandList.unSleep() every 10 seconds, in order to keep the connection always on.

If you don't want this behavior, just turn off the keep alive:

myMyo.keepAlive = false

Features 🎨

  • 100% Kotlin (but you don't need Kotlin to use it)!
  • Uses RxJava. You don't need to poll for status update, the library will call you.
  • Unleash the full Myo power, Raw Data Streaming at 200Hz! πŸ’ͺ
  • Small footprint: The AAR is just 36Kb
  • API >= 21 compatible (due to BluetoothLE limitations).
  • Easy to integrate (just a gradle implementation line).

Test App πŸ“²

You can find the test app (Myo Emg Visualizer) inside the app module.

This app allows you to:

  • Scan for a Myo
  • Connect to a Myo, control the frequency and send vibration.
  • See a graph of the EMG data.
  • Export the EMG data as a CSV file

Some technical features are:

  • The app is 100% Kotlin.
  • Architecture pattern used: MVP.
  • Dagger Android to inject the views.
  • Use AndroidX.
  • Use the Material Design Components library.

Videos

Searching for a Myo

Starting the Streaming

Exporting to CSV

Building/Testing βš™οΈ

CircleCI CircleCI

This projects is built with Circle CI 2.0. The CI environment takes care of building the library .AAR, the example app and to run the JUnit tests. Test and lint reports are exposes in the artifacts section at the end of every build.

Codecov codecov

Circle CI is responsible of uploading Jacoco reports to Codecov. When opening a Pull Request, Codecov will post a report of the diff of the test coverage.

Please don't ignore it! PR with new features and without are likely to be discarded πŸ˜•

Building locally

Before building, make sure you have the following updated components from the Android SDK:

  • tools
  • platform-tools
  • build-tools-28.0.1
  • android-28

Then just clone the repo locally and build the .AAR with the following command:

git clone git@github.com:cortinico/myonnaise.git
cd myonnaise/
./gradlew app:assemble

The assembled .AAR (library) will be inside the myonnaise/build/outputs/aar folder. The assembled .APK (application) will be inside the app/build/outputs/apk/debug folder.

Testing

Once you're able to build successfully, you can run JUnit tests locally with the following command.

./gradlew test 

Please note that there are tests inside the myonnaise and the app module. The app module contains test for the presenters. The myonnaise module contains tests for the library.

Make sure your tests are all green βœ… locally before submitting PRs.

Contributing 🀝

Looking for contributors! Don't be shy. 😁 Feel free to open issues/pull requests to help me improve this project.

  • When reporting a new Issue, make sure to attach Screenshots of the problem you are reporting.
  • Debugging
  • When submitting a new PR, make sure tests are all green. Write new tests if necessary (would be great if the code coverage doesn't decrease).

License πŸ“„

This project is licensed under the MIT License - see the License file for details