デザイン会社 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を利用しており、ファームウェアの情報は次の通りである。
$ 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
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である。
$ python --version Python 2.7.3
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を有効に設定した後、モータドライバを接続し、次のコマンドで接続されているかを確認する。次のコマンドで、今回のモータドライバのアドレスを調べる。
$ 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: -- -- -- -- -- -- -- --
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をインストールする。
$ pip install pubnub
なお、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
#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)
以下のコマンドをRaspberry Pi内で実行すると、Raspberry Piがスマートウォッチからの信号を待ち受ける状態になる。subscribeしたチャンネルに、pushされれば、ログが出力され、ミラーボールの速度が変化する。
今回は、Androidのスマートウォッチから信号を送るが、WebブラウザやiPhone、他のRaspberry Piなど、信号の信号元はなんでもよい。
$ 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}
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
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
WearのActivityから、Mobileに信号を送る。まず、GoogleApiClientのインスタンスを作り、接続しておく。sendSpeedを呼ぶと、スマートフォンに情報の送信を行う。
[Wear] MainActivity.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(); }
3-2. 音声入力
次に、Android Wearのスマートウォッチから、音声で入力する。StartVoiceRecognitionActivityで音声入力用のintentを生成し、Activityを起動させる。音声入力を終了して戻ってきたときに、onActivityResult内で、その音声データを取得し、スマートフォンへ送信する。
[Wear] MainActivity.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); }
3-3. スマートフォンでのデータの受信
次に、Mobileでのメッセージ受信の設定を行う。MobileのManifest.xmlのapplicationタグ内に、wearableからメッセージを受信するためのServiceを登録する。
[Mobile]AndroidManifest.xml
............(省略)............ <service android:name=".WearableService" > <intent-filter> <action android:name="com.google.android.gms.wearable.BIND_LISTENER" /> </intent-filter> </service> ............(省略)............
Android Wearからメッセージが送られると、onMessageReceivedが呼ばれる。onMessageReceived内で、PubNubへの通信を行う。なお、WearableServiceは、Androidスマホ側でアプリが起動していなくても呼び出されるため、Activityの実装は不要である(プロジェクト作成時に自動で作られたままでいい)
[Mobile] WearableService.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); } ............(省略)............ }
3-4. スマートフォンとRaspberry Piの通信
それでは、Rasbperry Piに速度の情報を送信するメソッド、updateDiscoBallSpeedの実装を行おう。PubNubのSDKを使うことで、非常に簡単に実装ができる。mobileのbuild.gradleにdependenciesにpubnubを追加する。
[Mobile] build.gradle
............(省略)............ 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+' }
OnCreateなどでPubnubのインスタンスを初期化しておけば、Pubnubのpublishメソッドを使って、Raspberry Piに速度を送信できる。
[Mobile] WearableService.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(); } } ............(省略)............
これにより、スマートウォッチに音声入力したコマンドが、Raspberry Piに送られ、ミラーボールの速度を変化させるプログラムが実装できた。ソースコードは、Githubで公開している。こちら。
btraxのSF本社オフィスで動くスマートミラーボール
btraxではプロトタイプ開発中!エンジニアも募集
btraxでは、IoTデバイスの市場調査、UX研究を目的に今回紹介したミラーボールのようなIoTプロトタイプを開発している。
将来的には、米国でクラウドファンディングを実施し、自社IoTデバイス開発を行うことも予定している。シリコンバレー、サンフランシスコで最先端のIoT事情を学びたいエンジニア、米国向けにIoTを開発し、プロダクト化を目指したいエンジニアを募集している。
CES 2025の革新を振り返りませんか?
1月11日(土)、btrax SFオフィスで「CES 2025 報告会: After CES Party」を開催します!当日は、CEOのBrandonとゲストスピーカーが CES 2025 で見つけた注目トピックスや最新トレンドを共有します。ネットワーキングや意見交換の場としても最適です!