1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Myo Gesture Control ArmbandのデータをPCを経由してUDPでAndroidスマートフォン/スマートウォッチに送信する

Last updated at Posted at 2018-10-26

やること

Myo Gesture Control ArmbandのデータをPCで受け取り、処理を行なってAndroidスマートフォン/スマートウォッチへと送る。
Myo armbandとPCの間はBLEでの通信、PCとAndroid側の間はUDPで通信することにしました。
Myo armbandとAndroid側を直接BLEで通信する方法にするとシステムが一番シンプルになりますが、BLE接続に関しての資料が非常にわかりにくいことと、プロトタイプであればUDPを使った方法が簡単なので今回はUDPを使った方法を紹介します。

スクリーンショット 2018-10-26 16.56.11.png

※2018.10.25現在 Myo armbandの販売が終了しています。

PC側はProcessing、Android側のアプリはAndroid StudioにてJavaで記述しました。

Processing側

「スケッチ」→「ライブラリをインポート...」→「ライブラリを追加...」から以下のライブラリを追加します。

・Myo for Processing 0.9.0.3
・ControlP5 2.2.6
・UDP 0.1

また、PCはMyoと接続しておく必要があります。

以下にコードを示します。
Processing側のコードでは、BLE接続されたMyoから信号を受け取り、Android側へUDPを用いて送信します。

MyoWebsocket.pde

import de.voidplus.myo.*;
import controlP5.*;

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.ArrayList;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import hypermedia.net.*;

Myo myo;
ArrayList<ArrayList<Integer>> sensors;
ControlP5 cp5;
UDP udp;
int portNo = 9002;
String ipAddress = "192.168.63.169"; //Android側のIPアドレス(192.168.63.169は例です)
int SEND_BUFFER_SIZE = 4;
byte[] sendBuffer = new byte[SEND_BUFFER_SIZE];

boolean useGUI = false;
boolean useSpectram = false;
boolean useWebsocket = false;
boolean sendStart = false;
boolean paintMode = true;

String receivedText = "";
String sendingText  = "";

float start = 0;
float ptime = 0;
float stime = 0;

int red   = 0;
int green = 0;
int blue  = 0;

 class NetworkData{
  int code; // 4byte integer
  int id;   // 4byte integer
 }

 boolean flagReceived = false;
 NetworkData receiveBuffer;

void setup(){
  // Screen setup
  frameRate(60);
  size(800, 600);
  background(200);
  noFill();
  stroke(0);

  // Myo setup
  println("attempting connect to Myo...");
  myo = new Myo(this, true); // true, with EMG data
  sensors = new ArrayList<ArrayList<Integer>>();
  for(int i=0; i<8; i++){
    sensors.add(new ArrayList<Integer>());
  }

  // UDP setup
  udp = new UDP(this, portNo);
  udp.setBuffer(SEND_BUFFER_SIZE);
  udp.setReceiveHandler("received");

  receiveBuffer = new NetworkData();
  receiveBuffer.code = receiveBuffer.id = 0;

  // GUI setup
  cp5 = new ControlP5(this);
  cp5.addButton("RawDataButton")
  .setPosition(695, 40)
  .setSize(100, 40);
  cp5.addButton("SpectramButton")
  .setPosition(695, 100)
  .setSize(100, 40);
  cp5.addButton("SendButton")
  .setPosition(695, 160)
  .setSize(100, 40);

  background(255);

  println("setup was done!");
}

int calcSumValue(){
 int sum = 0;
 //String s = "";
 for(int ch=0; ch<8; ch++){
   if(sensors.get(ch).size() > 0){
    sum = sum + Math.abs(sensors.get(ch).get(sensors.get(ch).size()-2)) * 5;
   }
 }
 return sum;
}

void draw(){

  stroke(red, green, blue);

  if(paintMode){
    if(mousePressed == true){
      line(mouseX, mouseY, pmouseX, pmouseY);
    }
  }

  if(useGUI){
    background(255);
    synchronized(this){
      for(int i=0; i<8; i++){
        if(!sensors.get(i).isEmpty()){
          beginShape();
          for(int j=0; j<sensors.get(i).size(); j++){
           vertex(j, sensors.get(i).get(j)+(i*50)+175); 
          }
          endShape();
        }
      }
    }
  }

  else if(useSpectram){
    background(255);
   synchronized(this){
    for(int ch=0; ch<8; ch++){
     pushStyle();
     fill(171, 1, 88, 100);
     rect(50+(ch*75), 400, 75, -calcRMS(sensors.get(ch), sensors.get(ch).size()-1*2));
     popStyle();
    }
   }
  }

}

// Calcuration of Route Mean Square(RMS) value.
float calcRMS(ArrayList<Integer> emgValue, int headOfData){
  float rmsValue=0;

  try {
    for(int i=0; i<300; i++){
      rmsValue += pow(emgValue.get(headOfData-i), 2);
    }
  } catch(ArrayIndexOutOfBoundsException e){
    // println("ERROR:" + e + ", return value 0.");
    return 0.0;
  }
  return sqrt(rmsValue/5.0);
}


void RawDataButton(){
  useGUI = !useGUI;
  useSpectram = false;
  background(255);
}

void SpectramButton(){
  useGUI = false;
  useSpectram = !useSpectram;
  background(255);
}

void SendButton(){
  sendStart = !sendStart;
  //wss.sendMessage("");
  if(sendStart){
    println("send start!"); 
    (new Thread(new UDPThread())).start();
  }
  else{
    println("send suspended"); 
  }
}

void myoOnEmgData(Device myo, long timestamp, int[] data) {

  // Data:
  synchronized (this) {
    for (int i = 0; i<data.length; i++) {
      sensors.get(i).add((int) map(data[i], -128, 127, -25, 25)); // [-128 - 127]
    }
    while (sensors.get(0).size() > width) {
      for(ArrayList<Integer> sensor : sensors) {
        sensor.remove(0);
      }
    }
  }
}

int byteArrayToInt(byte[] b) {
  return b[3] & 0xFF | (b[2] & 0xFF) << 8 | (b[1] & 0xFF) << 16 | (b[0] & 0xFF) << 24;
}

byte[] intToByteArray(int a){
  byte[] bytes = ByteBuffer.allocate(4).putInt(a).array();
  return bytes;
}

class UDPThread implements Runnable{
  @Override
  public void run(){
    while(true){

      int emgData = calcSumValue();

      byte[] tmpArray = intToByteArray(emgData);
      sendBuffer[0] = tmpArray[0];
      sendBuffer[1] = tmpArray[1];
      sendBuffer[2] = tmpArray[2];
      sendBuffer[3] = tmpArray[3];
      println(sendBuffer[0]+" "+sendBuffer[1]+" "+sendBuffer[2]+" "+sendBuffer[3]);

      udp.send(sendBuffer, ipAddress, portNo);

      try{
        Thread.sleep(20);
      }
      catch(Exception e){

      }
    }
  }
}

RawDataButtonを押すと、Myoからの源信号を表示します。力を入れてみたりしてうまく信号が上下していたらMyoからPCへは正しく信号が送られています。
SpectrumButtonを押すと、Myoの信号をRMS(Root Mean Square)値を表示します。
SendButtonを押すと、Android側とのUDP通信を開始し、Myoの筋電値を送信します。本コードでは筋電値8チャンネル分の合計値を送信しています。

Android側

Android側では、PC側から送られたデータを受け取ります。
以下にコードを示します(一部抜粋)

MainActivity.java
package com.example.midwinter.experience1;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.io.IOException;

import java.nio.ByteBuffer;

public class MainActivity extends Activity{

    // UDP関連
    public static final int SERVER_PORT = 9002; //サーバポート(9002は例です)
    public static final int PACKET_SIZE = 1024;
    DatagramSocket socket = null;
    byte[] buf = new byte[PACKET_SIZE];
    DatagramPacket packet = new DatagramPacket(buf, buf.length);
    
    // *************************** onCreate *************************** //
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 縦画面固定
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }
    // **************************************************************** //

    // *************************** onStart **************************** //
    @Override
    public void onStart(){
        super.onStart();

        Thread ut = new Thread(new UDPThread());
        ut.setPriority(10);
        ut.start();
    }
    // **************************************************************** //

    class UDPThread implements Runnable{
        @Override
        public void run(){
            try{
                socket = new DatagramSocket(SERVER_PORT);
                Log.d("UDPconnection", "データグラムレシーバが起動しました port="+socket.getLocalPort());

                while(true) {
                    socket.receive(packet);

                    int num = ByteBuffer.wrap(packet.getData()).getInt();
                    String s = String.valueOf(num);
                    Log.d("received", s);
                    try{
                        Thread.sleep(5);
                    }
                    catch(Exception e){
                        e.printStackTrace();
                    }
                }
            }
            catch(IOException e){
                e.printStackTrace();
            }
            finally {
                if(socket != null){
                    socket.close();
                }
            }
        }
    }
}


UDPThreadクラス内のwhile文の中でUDP通信のデータを受信し続けます。
Stringとして受け取っていますがこれを事前に用意しておいたフィールドにsetすることで、アプリ内でのアクションに利用することができます。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?