[뇌파 VR게임] #3 뇌파 Headset 연결2 - Android 라이브러리 만들기 by etainclub

View this thread on steempeak.com
· @etainclub · (edited)
$1.54
[뇌파 VR게임] #3 뇌파 Headset 연결2 - Android 라이브러리 만들기
이전글 - [[뇌파 VR게임] #2 뇌파 Headset 연결](https://steemit.com/@etainclub)

https://steemitimages.com/0x0/https://ipfs.busy.org/ipfs/QmXyU7s7nPSmBRj9WqcZXP36abjDpwGhLLkKAQXbWH4xxE
[게임 화면]

---
이전글에서는 뇌파 센서 업체에서 제공하는 Unity 라이브러리를 사용하는 방법을 알아봤습니다. 여기서는 이 방법을 사용안한다고 했습니다. 하시고 싶은 분들은 하셔도 됩니다. 굳이 안드로이드 안하셔도 되니 편할 수 있습니다. 그러나 자유도가 좀 떨어지는 것 같습니다.

이번에 알아볼 방법은 뇌파 센서인 Neurosky를 안드로이드에서 제어하여 Unity 플러그인으로 만드는 것입니다. 이 방법이 제가 이전에 올렸던 VR과 안드로이드 연결에서 사용했던 방법입니다. 한가지 다른 점은 뇌파센서는 블루투스 API를 제공하지 않는다는 점입니다. 이로 인해 고생을 좀 했습니다. 

## 뇌파 센서를 Android 라이브러리로 만들어서 Unity에서 사용하기
기본적인 개발 흐름을 살펴보겠습니다. 여기서는 굳이 VR이 아니어도 됩니다. 오히려 VR이 아니라 Unity 2D 앱으로 만드는 것이 빨리 테스트하고, 검증하기 편합니다. 그래서 2D 앱으로 테스트하길 추천합니다.
- 뇌파 센서용 Androiid 라이브러리 다운로드
- Android Project 생성하여 Android 라이브러리 생성
- 라이브러리 빌드하기
- 라이브러리를 하나의 AAR 파일로 합치기
- AAR파일을 Unity에 Plugin으로 삽입

단계적으로 조금 복잡합니다. 더 나은 방법이 있을 수 있습니다. 그러나 제가  찾은 방법을 바탕으로 써보겠습니다. 

## 뇌파 센서용 Android 라이브러리 다운로드
- https://github.com/pwittchen/neurosky-android-sdk
- 이곳을 방문하여 repository를 다운로드. Clone or Download 버튼을 눌러 압축 파일을 다운로드
- 몇 가지 예제가 있는데, 여기서는 app-java를 사용
- 다운로드 받은 파일을 압축 풀고, Android Studio 실행하여 app-java를 빌드
![image.png](https://ipfs.busy.org/ipfs/QmYndsG94pvmT3FqL5ZG8TEEzMNBzRjN7xbdpePmZUBJqr)
 위 그림처럼 app-java 폴더를 선택한 후에 Make Module "app-java"를 빌드.
- 빌드가 완료되면 Run "app-java"를 실행하여 Android 폰에 설치.
- 앱 설치 후 아래와 "Connect" 버튼을 누르고 약 30초 정도 기다리면 Connected 메시지가 나타남. 
![image.png](https://ipfs.busy.org/ipfs/QmaqtVt4MqbpWVobNj7jYuFeombULo5xkANNqf8ZMSbRRP)
- 이후에 "Start Monitoring" 버튼을 클릭하면 아래와 같이 뇌파값이 표시됨.
![image.png](https://ipfs.busy.org/ipfs/QmeJTJghTfZMSmQBd17hN9SHty3SQZBsjA3Crt8NaYeNkq)

일단 Android 라이브러리가 제대로 동작하는 것이 확인되었습니다. 이제 다음 단계로 이 라이브러리를 이용하여 뇌파 센서에서 값을 읽어오는 것을 구현해보겠습니다.

## Android Project 생성하여 Android 라이브러리 생성
여기서 잠깐! 지금 하려고 하는 것은 Android 라이브러리를 만들어서 Unity 플러그인으로 사용하려고 하는 것입니다. 즉 여기서의 결과물은 앱 파일이 아니라 라이브러리 파일입니다.
위 뇌파 라이브러리 웹페이지를 보면 사용법이 다음과 같이 나와 있습니다.
![image.png](https://ipfs.busy.org/ipfs/QmX3s3E6ANCydfD7vvHHopmK8t23p5oc9PunEjPKc5c1Fo)
해당 라이브러리를 사용하기 위해 Android Studio에서 Gradle 파일에 위와 같이 dependency만 추가하면 된다고 나옵니다. 앱 파일을 만들 때는 이렇게 하면 아무 문제 없이 잘 됩니다. 그러나 라이브러리 파일을 만들어서 Unity에서 사용하려면 문제가 발생합니다. 그 문제는 뒤에서 설명하도록 하겠습니다.

일단, Android 빈 프로젝트를 만듭니다. 그리고 라이브러리를 추가합니다. 이것은 이전에 작성한 글을 참고하세요. 여기서는 생략하겠습니다.
[[Unity VR과 Android BLE] #8 안드로이드 Plugin](https://steemit.com/kr-dev/@etainclub/unity-vr-android-ble-8-plugin)

- Android 라이브러리 neuroskymodule가 만들어지고, NeuroskyControl.java 파일을 만듦.
![image.png](https://ipfs.busy.org/ipfs/QmZaAU5zjFaKdfg1jneWoJ8LsC2SERDfLUu1WMw6e643de)
- NeuroskyControl.java파일에 아래와 같이 코딩
```
package club.etain.neuroskymodule;

import android.app.Activity;
import android.media.AudioManager;
import android.support.annotation.NonNull;
import android.util.Log;
import android.widget.Toast;

import com.github.pwittchen.neurosky.library.NeuroSky;
import com.github.pwittchen.neurosky.library.exception.BluetoothNotEnabledException;
import com.github.pwittchen.neurosky.library.listener.ExtendedDeviceMessageListener;
import com.github.pwittchen.neurosky.library.message.enums.BrainWave;
import com.github.pwittchen.neurosky.library.message.enums.Signal;
import com.github.pwittchen.neurosky.library.message.enums.State;

import java.util.Locale;
import java.util.Set;

public class NeuroskyControl {
    // tag for log message
    public static String TAG= "NeuroskyControl";
    // initialized message
    public static String INIT_MSG= "initialized";
    // current activity
    protected Activity current_activity_;
    // Audio manager
    private AudioManager audio_manager_;
    // neurosky instance
    private NeuroSky neuroSky;
    // meditation value
    private int meditation;
    // attention value
    private int attention;
    // connection flag
    private boolean connected_= false;

    // set current activity by unity
    public void setActivity(Activity _activity) {
        Log.d( TAG, "Call by Unity: setActivity");
        current_activity_= _activity;
    }

    // initialize
    public String init() {
        Log.d( TAG, "Call by Unity");
        Toast.makeText( current_activity_, "Call by Unity", Toast.LENGTH_SHORT).show();
        neuroSky = createNeuroSky();
        return INIT_MSG;
    }

    @NonNull
    private NeuroSky createNeuroSky() {
        return new NeuroSky(new ExtendedDeviceMessageListener() {
            @Override
            public void onStateChange(State state) {
                handleStateChange(state);
            }

            @Override
            public void onSignalChange(Signal signal) {
                handleSignalChange(signal);
            }

            @Override
            public void onBrainWavesChange(Set<BrainWave> brainWaves) {
                handleBrainWavesChange(brainWaves);
            }
        });
    }

    private void handleStateChange(final State state) {
        if (neuroSky != null && state.equals(State.CONNECTED)) {
            connected_= true;
            neuroSky.startMonitoring();
        }
        Log.d(TAG, state.toString());
    }

    private void handleSignalChange(final Signal signal) {
        switch (signal) {
            case ATTENTION:
                attention= signal.getValue();
                break;
            case MEDITATION:
                meditation= signal.getValue();
                break;
            case BLINK:
                break;
        }

        Log.d(TAG, String.format("%s", signal.toString(), signal.getValue() ));
    }

    private String getFormattedMessage(String messageFormat, Signal signal) {
        return String.format(Locale.getDefault(), messageFormat, signal.getValue());
    }

    private void handleBrainWavesChange(final Set<BrainWave> brainWaves) {
        for (BrainWave brainWave : brainWaves) {
            Log.d(TAG, String.format("%s: %d", brainWave.toString(), brainWave.getValue()));
        }
    }

    public void connect() {
        try {
            neuroSky.connect();
        } catch (BluetoothNotEnabledException e) {
            Toast.makeText(current_activity_, e.getMessage(), Toast.LENGTH_SHORT).show();
            Log.d(TAG, e.getMessage());
        }
    }

    public boolean isConnected()
    {
        return connected_;
    }

    void disconnect() {
        neuroSky.disconnect();
        connected_= false;
    }

    public int getMeditationValue() {
        return meditation;
    }

    public int getAttentionValue() {
        return attention;
    }
}
```
- 블루투스를 사용할 것이기 때문에 아래와 같이 Androidmanifest.xml 파일에 권한 설정.
```
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="club.etain.neuroskymodule">

    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

</manifest>
```
- 뇌파 센서 Android 라이브러리를 빌드하기 위해 dependency 추가. 추가할 곳은 build.gradle(Module neuroskymodule) 파일.
```
dependencies {
    <생략>
    api 'com.github.pwittchen:neurosky-android-sdk:0.0.2'
}
```
위에 보면 api하고 의존 라이브러리가 적혀있습니다. implementation이 아니라 api인 점은 의존 라이브러리를 한꺼번에 빌드에 포함시키기 위한 것인데, 바로 이어서 설명드리겠습니다.

## 라이브러리 빌드
우리가 만든 NeuroskyControl.java로 부터 생성되는 neuroskymodule 라이브러리에는 몇 가지 dependency가 있습니다. 특히 뇌파 센서 라이브러리가 사용하는 의존 라이브러리들이 있는데, 이것들은 빌드하면 포함되지 생성되는 파일에 포함되어 있지 않습니다. Unity에서는 이 부분 때문에 에러가 발생합니다. 즉 모든 의존 라이브러리들이 하나의 AAR 파일에 다 들어가 있어야 Unity에서 문제 없이 동작합니다.

그럼, 의존 라이브러리들을 하나의 AAR로 만드는 법을 알아보겠습니다.

먼저, Mobbeel fat AAR Gradle plugin이라는 사이트를 방문합니다.
https://github.com/Mobbeel/fataar-gradle-plugin

#### build.gradle(Project: NeuroskyLibrary) 수정
한가지 주의할 것은 build.gradle 파일들입니다.
- Project명: NeuroskyLibrary
- Library명: neuroskymodule
따라서 build.gradle 파일들을 구별해서 봐야 합니다. 여기서는 먼저 프로젝트의 build.gradle파일을 아래와 같이 수정합니다. mobbeel plugin을 사용하기 위한 설정입니다.
```
buildscript {
    
    repositories {
        google()
        jcenter()
        mavenCentral()
        maven {
            url 'https://plugins.gradle.org/m2/'
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.0'
        classpath 'gradle.plugin.com.mobbeel.plugin:mobbeel-fataar:1.2.0'
        

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        mavenCentral()
        maven {
            url 'https://plugins.gradle.org/m2/'
        }
    }
}
```

#### build.gradle(module: neuroskymodule) 수정
아래와 같이 `apply plugin: 'com.mobbeel.plugin'`와 `    api 'com.github.pwittchen:neurosky-android-sdk:0.0.2'`를 추가합니다. 또 아래와 같이 옵션을 추가해 줍니다.
```
aarPlugin {
    includeAllInnerDependencies true  // It's false for default
}
```

build.gradle(module: neuroskymodule) 파일 코드 전문은 아래와 같습니다.
```
apply plugin: 'com.android.library'
apply plugin: 'com.mobbeel.plugin'

android {
    compileSdkVersion 28



    defaultConfig {
        minSdkVersion 24
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

}

fatAARConfig {
    includeAllInnerDependencies true
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:28.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    api 'com.github.pwittchen:neurosky-android-sdk:0.0.2'
}
```

### neuroskymodule 빌드
이제 neuroskymodule을 빌드합니다. 주의할 것은 app을 빌드하는 것이 아니라 우리가 생성한 neuroskymodule을 빌드해야 하는 것입니다.
먼저, NeuroskyControl.java가 컴파일되어 아래와 같은 위치에 classes.jar 파일이 만들어집니다.
![image.png](https://ipfs.busy.org/ipfs/QmS9S4uhpjD7vdooaN7LwuhPdYifxnFx2YqQHHDxeXhQ79)

이 파일을 알집과 같은 압축프로그램으로 열어서 보면 아래와 같이 보입니다.
![image.png](https://ipfs.busy.org/ipfs/QmXnbxVWcPu5JYTmEBnBPUmnFXTyQGVQ6dqVFMFvz8f7Lm)

안타깝게도 이 파일만 있어서는 Unity에서 NeuroskyControl.java에서 사용하는 라이브러리 내용들이 없다고 에러가 납니다. 

안타까워 하긴 이릅니다. 아래와 같은 위치에 보면 aar 파일이 하나 있습니다.
![image.png](https://ipfs.busy.org/ipfs/QmQYs2vYxHpwnhqHhB8d1o7aydPbgjCVTTxyRhgcMu7Tyu)
이것은 알집 프로그램에서 바로 열리지 않지만, 파일 확장자를 `.jar`로 변경하면 내용을 볼 수 있습니다. aar파일을 복사하여 확장자를 jar로 변경하고 알집 프로그램으로 열어 봅니다.
![image.png](https://ipfs.busy.org/ipfs/Qma1up9bEkFSRYtV43THTRsUK3hHyFh1n9aHp8RTxGxcWR)

 aar 파일에 포함된 내용을 보면 라이브러리 파일들이 보입니다. 가장 중요한 ThinkGear.jar 파일도 보입니다. 

### AAR 파일 합치기
모든게 준비된 거 같은데... aar 파일에 우리가 만든 neuroskymodule에 대한 라이브러리는 빠져 있습니다. 저도 이것도 포함되는 줄 알았는데, 어떤 이유인지 포함이 안되어 있습니다. 그러면 일단 강제로 포함시켜 봅니다.

이것도 알집을 이용해서 포함시키겠습니다.
![image.png](https://ipfs.busy.org/ipfs/QmQQVY6DbkbkHjmRMRixfoUPuqLbxdpxi828pcWeMP84Vg)

classes.jar 파일도 포함되어 아래와 같이 됩니다.
![image.png](https://ipfs.busy.org/ipfs/QmXF8qHAfZX1hzQpzY9FLNUPngRLy1TJ4xbsQ9J9KbGxju)

원래의 aar 파일에 classes.jar 파일을 추가한 것입니다. 이제 원래의 aar 파일을 삭제하고, 새로 만든 jar파일의 확장자를 aar로 변경시킵니다.

---
이것으로 Unity에서 사용할 수 있는 라이브러리 파일이 만들어 졌습니다. 별거 아닌데, 꼼수를 부리다 보니 내용이 많아졌습니다.

그럼 이제 이 라이브러리가 Unity에서 제대로 동작하는지 테스트 해봐야겠죠? 테스트는 심플하게 해야 합니다. 결국에 VR앱을 만들 것이지만, VR앱으로 테스트 한다면 매번 빌드한 후에 VR을 써서 직접 테스트해야 하기때문에 매우 번거롭습니다. 그래서 다음 글에서 간단한 2D Unity 앱을 만들어서 라이브러리 동작 테스트를 해보겠습니다. 

---
`오늘의 실습:  뇌파를 측정하는 센서는 어떻게 동작하는지 알아보세요.`
👍  , , , , , , , , , , , ,
properties (23)
post_id64,787,040
authoretainclub
permlinkvr-3-headset-2-android
categorykr
json_metadata{"app":"steemit\/0.1","format":"markdown","image":["https:\/\/steemitimages.com\/0x0\/https:\/\/ipfs.busy.org\/ipfs\/QmXyU7s7nPSmBRj9WqcZXP36abjDpwGhLLkKAQXbWH4xxE"],"community":"busy","tags":["kr","busy","unity","brainwave","android"],"links":["https:\/\/steemit.com\/@etainclub","https:\/\/github.com\/pwittchen\/neurosky-android-sdk","https:\/\/steemit.com\/kr-dev\/@etainclub\/unity-vr-android-ble-8-plugin","https:\/\/github.com\/Mobbeel\/fataar-gradle-plugin"]}
created2018-10-23 16:02:42
last_update2018-10-23 16:09:09
depth0
children0
net_rshares1,340,273,291,579
last_payout2018-10-30 16:02:42
cashout_time1969-12-31 23:59:59
total_payout_value1.167 SBD
curator_payout_value0.375 SBD
pending_payout_value0.000 SBD
promoted0.000 SBD
body_length11,469
author_reputation790,274,152,641,380
root_title"[뇌파 VR게임] #3 뇌파 Headset 연결2 - Android 라이브러리 만들기"
beneficiaries[]
max_accepted_payout1,000,000.000 SBD
percent_steem_dollars10,000
author_curate_reward""
vote details (13)