
デザイン会社 btrax > Freshtrax > 【IoT開発ブログ】世界初?ス...
【IoT開発ブログ】世界初?スマートウォッチから声で回せるスマートミラーボールの開発
btraxは、イノベーションの最先端を行く本場サンフランシスコ・シリコンバレーで短期集中少数精鋭でイノベーション創出にチャレンジできる「イノベーション プログラム」を提供している。
また、モバイルアプリやWebサービスだけでなく、IoTデバイスの市場調査やUX研究はもちろん、社内では深い知見を得るために、IoTプロトタイプを開発することで、より良いサービスを提供できるように努めている。
今回は社内エンジニアが、IoTの研究の一環として、弊社サンフランシスコのオフィスに取り付けられたミラーボールをスマートウォッチから声で操作するプロトタイプを開発した。開発方法をご紹介しよう。
ミラーボールはマイクロコンピュータのRaspberry Piを通してインターネットにつながっており、スマートウォッチから次の音声で操作することができる。 なお、速度は、スマートウォッチの画面上のスライダーからも操作可能。
- Speed up : 回転が速くなる
- Speed down : 回転が遅くなる
1. 用意するもの
1-1. Android Wearを搭載したスマートウォッチ
btraxでは、すでにAndroidのスマートウォッチ用OS「Android Wear」を用いたスマートウォッチ用アプリ、「OnTask」をリリースしている。今回は、Asusが2014年末に発売した、Asus zenwatchを用いた。
1-2. ミラーボール用のモーター
普通のモーターでは回転数が高すぎるため、市販の電池式ミラーボール用モータを利用する。今回購入したものは、単1電池を1本使い、1分間に2回転と非常にゆっくり回るモータだ。購入した状態ではONとOFFしかできないが、Rasbperry Piで回転速度を調整できるように改造した。
1-3. Raspberry Pi
Raspberry Pi(ラズベリーパイ)はARMプロセッサを搭載したシングルボードコンピュータである。2015年2月に最新の「Raspberry Pi2」が発売された。価格はなんと5,000円未満。
大きさは名刺サイズで、重さはわずか45g。しかし、その実態は高性能コンピュータだ。まず代表的なOSであるLinuxを搭載できる。能力が高いため、単体で簡単な画像認識処理も行うこともできる。
1-4. モータドライバ
モータの制御にはI2C対応のモータドライバを用いた。今回は、手元にあった秋月電子通商のDRV8830使用 DCモータードライブキットを使用している。I2C対応のため、回路は非常に簡単になり、Raspberry Piとモータドライバキットの間を3本の線で繋ぐだけでよい。
Raspberry Pi(手前)とモータドライバキット(奥)。クリップの先にモータを取り付ける。右に切れている赤と黒の線は電源用
2. Raspberry Piの開発
今回は、Raspberry Piにモータドライバを取り付け、その先にミラーボール用のモータを取り付けるというシンプルな構成とする。なお、Raspbery Pi2を利用しており、ファームウェアの情報は次の通りである。
[code]
$ uname -a
Linux raspberrypi 3.18.10-v7+ #775 SMP PREEMPT Thu Apr 2 19:01:29 BST 2015 armv7l GNU/Linux
$ cat /etc/debian_version
7.8
[/code]
2-1. プログラミング言語
今回は、I2C通信を行うことと将来的にカメラなどを使うことを想定している。このため、Raspberry Piとの相性がよいPythonというプログラミング言語を使う。Pythonを使っている読者は少ないかもしれないが、今回は非常に簡単なコードしか書かないので理解できると思う。Pythonは、Raspberry Piで標準で用意されており、インストールすることなく使える。
前回は、【IoT開発ブログ】Raspberry Pi + Node.js + PubNub(mBaaS)で、ブラウザとIoTのリアルタイム通信を実装で、Node.jsを利用した。Node.jsのほうが経験があるWebエンジニアが多いと思われたためだ。ただ、カメラやI2C、GPIOなどのRaspberry Piのハードウェアの機能を使っていく際には、Pythonの方が扱いやすい場合が多くある。なお、今回利用したのはPython2.xである。
[code]
$ python –version
Python 2.7.3
[/code]
2-2. モーター制御にI2Cを使う
モーターの制御にはI2C対応のICを使う。I2C(アイ・スクエア・シー、アイ・ツー・シー)はフィリップス社で開発されたシリアルバスであり、低速な周辺機器をマザーボードへ接続したり組み込みシステム、携帯電話などで使われている。
I2Cを使うとICチップ間でシリアル通信によりデータをやり取りができるため、単純なONとOFFしかない他のI/Oよりも高機能で利便性が高い。I2Cを用いた温度計など、様々なセンサーが存在するため、I2Cの使い方をマスターしておくとできることも大きく広がる。
特に、電子回路の知識が乏しいWebやアプリのディベロッパーにとっては、電子回路の構築が楽になるメリットは大きい。
2-3. Raspbery PiのI2Cの設定
まず、Raspberry Pi本体のI2Cの設定を有効にする。この方法については、いくつも優れた記事があるため、別のサイトを参考にしていただきたい。弊社では次の記事を参考にさせていただいた。
2015年1月のアップデートからI2Cの有効化の方法が変わっているため、検索すると古い方法で解説されているページが見つかることが多いので気をつけてほしい。
I2C設定方法:Raspberry Pi の I2C を有効化する方法 (2015年版)
I2Cを有効に設定した後、モータドライバを接続し、次のコマンドで接続されているかを確認する。次のコマンドで、今回のモータドライバのアドレスを調べる。
[code]
$ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: — — — — — — — — — — — — —
10: — — — — — — — — — — — — — — — —
20: — — — — — — — — — — — — — — — —
30: — — — — — — — — — — — — — — — —
40: — — — — — — — — — — — — — — — —
50: — — — — — — — — — — — — — — — —
60: — — — — 64 — — — — — — — — — — —
70: — — — — — — — —
[/code]
0x64が、モータドライバのアドレスである。このアドレスをプログラムから指定して、モータドライバを操作することができる。
2-4. モータドライバの使い方
ここで、利用するモータドライバDRV8830の使い方を見てみよう。非常に便利にできており、モータドライバに8ビットの情報を書き込むことで、モータに供給される電圧と、状態を変更することができる。電圧が変わればモータの速度が変わる。このため、非常に簡単なプログラムで速度を変化させることができる。また、今回は触れないが、現在の電圧の取得もできる。
モータドライバのデータシートを見ると、右から2桁が、動作用として使われることがわかる。
- 00 : スタンバイ/惰走
- 01 : 逆転
- 10 : 正転
- 11 : ブレーキ
また、同様に右から3桁目以降の6桁が電圧を表す。0x6(000110)から0x3F(111111)までの値を指定する。例えば、1.53Vの電圧で、正転させたいときは、速度0x13(10011)に、動作用の01を加えて、1001101を書き込む。
2-5. Raspberry Piのプログラム
今回もリアルタイム通信が可能なmBaaS(Mobile Backend as a service)である、PubNubを使う。Raspberry PiのセットアップとPubNubについて詳しくは、【IoT開発ブログ】Raspberry Pi + Node.js + PubNub(mBaaS)で、ブラウザとIoTのリアルタイム通信を実装をご覧いただきたい。
pythonのパッケージ管理システムである、pipを使いpubnubをインストールする。
[code]
$ pip install pubnub
[/code]
なお、pipがインストールされていない場合はINSTALLING PYTHON PACKAGES|Raspberry Pi公式サイトを参考にインストールしてほしい。
PubNubへの接続はいたって簡単で、subscribeメソッドを呼ぶと、スマートフォンや他のデバイスが設定したチャンネルにPUSHされたときに、subscribeCallbackが呼ばれる。subscribeCallback内で、モータの電圧を変化させるメソッドを呼べばよい。
モータが乾電池1本用(1.5V)なので、あまり高い電圧を加えると壊れる可能性がある。また、電圧が低すぎると回転しない。
そこで、0.64Vから、3.53Vの間までの電圧を変化させることで、モーターの速度を変更させる。(最大電圧を高めに設定しているが、動作を保証しているわけではないので、あくまで自己責任でお願いしたい。)
速度を100段階で指定すると、先ほど説明した通り6ビットの速度のデータを生成し、それに正転の動作を加えて、モータドライバに書き込む。
RaspberryPiの全体のソースコードは次のようになる。
[Raspberry Pi] main.py
[python]
#coding:utf-8
import sys
import smbus
import time
from Pubnub import Pubnub
PUBLISH_KEY = "<Your Publish Key>"
SUBSCRIBE_KEY = "<Your Subscribe Key>"
CHANNEL_C_CONTROL = "c-control";
SUBSCRIBE_CHANNELS= [CHANNEL_C_CONTROL]
SPEED_KEY = ‘speed’
ADDRESS = 0x64
MIN_SPEED_BYTE = 0x08 # 0.64V
FAST_SPEED_BYTE = 0x2C # 3.53V
pubnub = Pubnub(PUBLISH_KEY, SUBSCRIBE_KEY)
i2c = smbus.SMBus(1)
# speed 0 – 100
def setSpeed(speed):
if speed > 100:
speed = 100
elif speed < 0:
speed = 0
speedByte = MIN_SPEED_BYTE + (FAST_SPEED_BYTE – MIN_SPEED_BYTE) * speed / 100
# Forward
output = speedByte << 2;
output += 0x01
# write byte data to motor driver
i2c.write_byte_data(ADDRESS, 0, output)
def subscribeCallback(message, channel):
print(‘received, ‘ + channel + ‘:’ + str(message))
if channel == CHANNEL_C_CONTROL:
speed = message[‘speed’]
setSpeed(speed)
def subscribeError(message):
print(str(message))
pubnub.subscribe(SUBSCRIBE_CHANNELS, subscribeCallback, subscribeError)
[/python]
以下のコマンドをRaspberry Pi内で実行すると、Raspberry Piがスマートウォッチからの信号を待ち受ける状態になる。subscribeしたチャンネルに、pushされれば、ログが出力され、ミラーボールの速度が変化する。
今回は、Androidのスマートウォッチから信号を送るが、WebブラウザやiPhone、他のRaspberry Piなど、信号の信号元はなんでもよい。
[code]
$ sudo python main.py
received, c-control:{u’speed’: 52}
received, c-control:{u’speed’: 32}
received, c-control:{u’speed’: 75}
received, c-control:{u’speed’: 33}
[/code]
3. スマートウォッチの開発
ここからはAndroidの開発について解説する。開発にはAndroid Studioを使う。Androidのスマートウォッチ開発については、過去のブログ「美女時計を作ろう!Android Wear開発入門 30分で作る盤面アプリ」も参考にして欲しい。
スマートウォッチから直接インターネットに接続できないため、スマートウォッチ –(Bluetooth)–> スマートフォン –(インターネット)–> PubNub –(インターネット)–> Raspberry Pi という、少々長い経路を通してミラーボールを操作する。
なお、Googleは、2015年4月にApple Watchの発売とほぼ同じタイミングで、次回のアップデートでスマートウォッチから直接WiFiに接続することを許可することをアナウンスしている。今後はよりシンプルにスマートウォッチから通信が行えるようになるはずだ。
3-1. スマートウォッチからスマートフォンへのデータ送信
まず、スマートウォッチからスマートフォンへの通信を行う。
mobileとwearの両方のAndroidManifest.xmlに、以下のmeta-dataを追加する。
[Wear][Mobile] AndroidManifest.xml
[xml]
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
[/xml]
WearのActivityから、Mobileに信号を送る。まず、GoogleApiClientのインスタンスを作り、接続しておく。sendSpeedを呼ぶと、スマートフォンに情報の送信を行う。
[Wear] MainActivity.java
[java]
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
…………(省略)…………
// setup comunicate with smartphone
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(Bundle bundle) {
Log.d(TAG, "onConnected");
}
@Override
public void onConnectionSuspended(int i) {
Log.d(TAG, "onConnectionSuspended");
}
})
.addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d(TAG, "onConnectionFailed");
}
})
.addApi(Wearable.API)
.build();
mGoogleApiClient.connect();
}
/**
* send speed to smartphone
* @param speed
*/
private void sendSpeed(final int speed) {
new Thread(new Runnable() {
@Override
public void run() {
final String message = String.valueOf(speed);
NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
for (Node node : nodes.getNodes()) {
MessageApi.SendMessageResult result = Wearable.MessageApi.sendMessage(
mGoogleApiClient,
node.getId(),
MESSAGE_PATH,
message.getBytes())
.await();
if (result.getStatus().isSuccess()) {
Log.d(TAG, "success:send to mobile");
} else {
Log.e(TAG, "failure:send to mobile");
}
}
}
}).start();
}
[/java]
3-2. 音声入力
次に、Android Wearのスマートウォッチから、音声で入力する。StartVoiceRecognitionActivityで音声入力用のintentを生成し、Activityを起動させる。音声入力を終了して戻ってきたときに、onActivityResult内で、その音声データを取得し、スマートフォンへ送信する。
[Wear] MainActivity.java
[java]
…………(省略)…………
/**
* launch voice input activity
*/
private void startVoiceRecognitionActivity() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, R.string.voice_input_msg);
startActivityForResult(intent, REQUEST_CODE);
}
/**
* receieve data from voice input activity
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// return from voice input activity
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
ArrayList<String> matches = data.getStringArrayListExtra(
RecognizerIntent.EXTRA_RESULTS);
String command = matches.get(0);
// add 20 if voice is "speed up"
if (command.equals(SPEED_UP_VOICE) || command.equals(SPEED_DOWN_VOICE)) {
int speed = mSeekBar.getProgress();
if (command.equals(SPEED_UP_VOICE)) {
speed += 20;
if (speed > 100) {
speed = 100;
}
} else {
speed -= 20;
if (speed < 0) {
speed = 0;
}
}
mSeekBar.setProgress(speed);
sendSpeed(speed);
mSpeedText.setText(String.valueOf(speed));
}
}
super.onActivityResult(requestCode, resultCode, data);
}
[/java]
3-3. スマートフォンでのデータの受信
次に、Mobileでのメッセージ受信の設定を行う。MobileのManifest.xmlのapplicationタグ内に、wearableからメッセージを受信するためのServiceを登録する。
[Mobile]AndroidManifest.xml
[xml]
…………(省略)…………
<service
android:name=".WearableService" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
…………(省略)…………
[/xml]
Android Wearからメッセージが送られると、onMessageReceivedが呼ばれる。onMessageReceived内で、PubNubへの通信を行う。なお、WearableServiceは、Androidスマホ側でアプリが起動していなくても呼び出されるため、Activityの実装は不要である(プロジェクト作成時に自動で作られたままでいい)
[Mobile] WearableService.java
[java]
public class WearableService extends WearableListenerService {
…………(省略)…………
/**
* called when receieve message from Android Wear
* @param messageEvent
*/
@Override
public void onMessageReceived(MessageEvent messageEvent) {
Log.d(TAG, "onMessageReceived");
String speedStr = new String(messageEvent.getData());
int speed = Integer.valueOf(speedStr);
// send speed to discoball
updateDiscoBallSpeed(speed);
}
…………(省略)…………
}
[/java]
3-4. スマートフォンとRaspberry Piの通信
それでは、Rasbperry Piに速度の情報を送信するメソッド、updateDiscoBallSpeedの実装を行おう。PubNubのSDKを使うことで、非常に簡単に実装ができる。mobileのbuild.gradleにdependenciesにpubnubを追加する。
[Mobile] build.gradle
[code]
…………(省略)…………
dependencies {
compile fileTree(dir: ‘libs’, include: [‘*.jar’])
wearApp project(‘:wear’)
compile ‘com.android.support:appcompat-v7:21.0.3’
compile ‘com.google.android.gms:play-services:6.5.87’
compile ‘com.pubnub:pubnub:3.7.2+’
}
[/code]
OnCreateなどでPubnubのインスタンスを初期化しておけば、Pubnubのpublishメソッドを使って、Raspberry Piに速度を送信できる。
[Mobile] WearableService.java
[java]
…………(省略)…………
@Override
public void onCreate() {
super.onCreate();
mPubnub = new Pubnub(
PUBNUB_PUBLISH_KEY,
PUBNUB_SUBSCRIBE_KEY,
"", // SECRET_KEY
"", // CIPHER_KEY
false // SSL_ON?
);
}
/**
* send to pubnub, change disco ball speed
* @param speed rotation speed (0 – 100)
*/
private void updateDiscoBallSpeed(int speed) {
try {
Log.d(TAG, "speed:" + speed);
JSONObject object = new JSONObject();
object.put(PUBNUB_SPEED_KEY, speed);
mPubnub.publish(CHANNEL_C_CONTROL, object, new Callback() {
@Override
public void successCallback(String channel, Object message) {
super.successCallback(channel, message);
Log.i(TAG, message.toString());
}
@Override
public void errorCallback(String channel, PubnubError error) {
super.errorCallback(channel, error);
Log.e(TAG, error.toString());
}
});
} catch (JSONException e) {
e.printStackTrace();
}
}
…………(省略)…………
[/java]
これにより、スマートウォッチに音声入力したコマンドが、Raspberry Piに送られ、ミラーボールの速度を変化させるプログラムが実装できた。ソースコードは、Githubで公開している。こちら。
btraxのSF本社オフィスで動くスマートミラーボール
btraxではプロトタイプ開発中!エンジニアも募集
btraxでは、IoTデバイスの市場調査、UX研究を目的に今回紹介したミラーボールのようなIoTプロトタイプを開発している。
将来的には、米国でクラウドファンディングを実施し、自社IoTデバイス開発を行うことも予定している。シリコンバレー、サンフランシスコで最先端のIoT事情を学びたいエンジニア、米国向けにIoTを開発し、プロダクト化を目指したいエンジニアを募集している。
私たちと一緒に働きませんか?
ビートラックスは、デザインを中心に、最適なユーザー体験を創造し、新しい価値の創出に貢献している会社です。協調性が高く、ポジティブで、主体性を持って仕事を進めるメンバーが集まっています。
私たちと一緒に働いて革新的なプロジェクトを共に実施しませんか?