これはソラコムのSoftware Design 誌 巻頭特集記念リレーブログ 4/28分です
Wifiがつながりにくい室内環境やそもそもWifiのない屋外環境で、ゲーム的な操作を記録できないかなということで、GROVEジョイスティック+littleBits KoshianBitをSORACOM経由でMilkcocoaに記録してみたメモです。
やりたいこと
すしルート#3で「すし×うごく」ネタで登壇してきましたで行った曲げセンサー情報をBluetoothに送るのがかなりゲーム的だったので、ジョイスティックだとどうだろうかという想起です。
以下にように実装をします。
- GROVEジョイスティック+littleBits KoshianBitでジョイスティックの傾きをBluetooth無線化する
- RaspberryPi 3の内蔵BluetoothでKoshianBitで無線化された数値をNodeJS nobleで検出する
- RaspberryPi 3で内蔵Bluetoothで検出した値をSORACOM経由でMilkcocoaに送信する
GROVE ジョイスティック+littleBits KoshianBitでジョイスティックの傾きをBluetooth無線化する
最近、制作したGROVE のデジタル・アナログ入力と連携できる個人開発したGROVE INPUT Bitを使ってGROVEとlittleBitsが連携することが出来ます。
こちらをKoshianBitと連携して接続し、ジョイスティックの一方向の傾きのみ検出しました。
GROVE ジョイスティックの仕様で、通常時50%、最小25%~最大75%を検出できます。
RaspberryPi 3の内蔵BluetoothでKoshianBitで無線化された数値をNodeJS nobleで検出
RaspberryPi 3では内蔵Bluetoothがあるので、追加アダプタ無しですぐにつながるのが素敵でした。NodeJSでBLE通信ができるnobleライブラリでkonashiとつなげたメモをベースに、検出するようにします。
今回はインストール時に、Raspberry Pi デフォルトのNodeJSはv0.10でnobleがうまく入らなかったため、本家の v4 のインストール記事をベースにバージョンアップしてから入れました。
ソースコードは以下のとおりです。
var express = require('express'); var app = express(); var MILKCOCOA_APP_ID = " "; var MILKCOCOA_DATASTORE_ID = "grove_joystick"; console.log("MILKCOCOA_APP_ID:" + MILKCOCOA_APP_ID); console.log("MILKCOCOA_DATASTORE_ID:" + MILKCOCOA_DATASTORE_ID); // milkcocoa ///////////////////////////////// var MilkCocoa = require("./node_modules/milkcocoa/index.js"); var milkcocoa = new MilkCocoa(MILKCOCOA_APP_ID + ".mlkcca.com"); // dataStore作成 デフォルトのデータストアID var sampleDataStore = milkcocoa.dataStore(MILKCOCOA_DATASTORE_ID); // データがpushされたときのイベント通知 sampleDataStore.on("push", function(datum) { // 内部のログ console.log('[push complete]'); console.log(datum); }); ////////////////////////////////////////////// var express = require('express'); var bodyParser = require('body-parser'); var app = express(); app.set('port', (process.env.PORT || 5000)); app.use(express.static(__dirname + '/public')); app.use(bodyParser.urlencoded({extended: true})); app.use(bodyParser.json()); app.get('/', function(request, response) { response.send('Hello World!'); }); app.listen(app.get('port'), function() { console.log('Node app is running on port', app.get('port')); }); var noble = require('noble'); // KONASHI自体のサービスID var KONASHI_SERVICE_UUID = '229bff0003fb40da98a7b0def65c2d4b'; // 操作したいpinのID var KONASHI_ANALOG_INPUT_PINID = '229b300803fb40da98a7b0def65c2d4b'; var KONASHI_PIO_SETTING_PINID = '229b300003fb40da98a7b0def65c2d4b'; var KONASHI_PIO_OUTPUT_PINID = '229b300203fb40da98a7b0def65c2d4b'; // 各CHARACTERISTICSをUUIDから判断してcharacteristic格納する変数 var KONASHI_CHARACTERISTICS_ANALOG_READ; var KONASHI_CHARACTERISTICS_PIO_SETTINGS; var KONASHI_CHARACTERISTICS_PIO_OUTPUT; // LED ON/OFF var led_toggle = false; // 状態がパワーONだったらスキャンに移行 noble.on('stateChange', function(state) { console.log('on -> stateChange: ' + state); if (state === 'poweredOn') { noble.startScanning(); } else { noble.stopScanning(); } }); noble.on('scanStart', function() { console.log('on -> scanStart'); }); noble.on('scanStop', function() { console.log('on -> scanStop'); }); // discover 機器が発見されたら noble.on('discover', function(peripheral) { console.log('on -> discover: ' + peripheral); // まずスキャンをとめる noble.stopScanning(); // KONASHI接続時のイベント peripheral.on('connect', function() { console.log('on -> connect'); this.discoverServices(); }); // KONASHI切断時のイベント peripheral.on('disconnect', function() { console.log('on -> disconnect'); }); // 見つけたサービス(機器)へのアクセス peripheral.on('servicesDiscover', function(services) { for(i = 0; i < services.length; i++) { // サービスがKONASHI_SERVICE_UUIDと一致した時だけ処理 if(services[i]['uuid'] == KONASHI_SERVICE_UUID){ // サービスのcharacteristic捜索 services[i].on('includedServicesDiscover', function(includedServiceUuids) { console.log('on -> service included services discovered [' + includedServiceUuids + ']'); this.discoverCharacteristics(); }); // characteristic取得イベント services[i].on('characteristicsDiscover', function(characteristics) { // characteristics配列から必要なCHARACTERISTICSをUUIDから判断してcharacteristic格納 for(j = 0; j < characteristics.length; j++) { // アナログ入力 characteristic if( KONASHI_ANALOG_INPUT_PINID == characteristics[j].uuid ){ console.log("KONASHI_CHARACTERISTICS_ANALOG_READ exist!!"); KONASHI_CHARACTERISTICS_ANALOG_READ = characteristics[j]; } } setInterval(function(){ // ANALOG_READ KONASHI_CHARACTERISTICS_ANALOG_READ.read(function(error, data) { if (data) { // 実際のジョイスティックの値検知 var value = (data[1] + data[0]*256)/1000; console.log( 'value:' + value ); // Milkcocoaに送る sampleDataStore.push({ player : 'A' , value : value }); } }); }, 1000); }); services[i].discoverIncludedServices(); } } }); // 機器との接続開始 peripheral.connect(); });
RaspberryPi 3で内蔵Bluetoothで検出したジョイスティック値をSORACOM経由でMilkcocoaに送信する
Milkcocoaの記録と内蔵Bluetoothで検出したジョイスティック値の連動が出来ました。いよいよ、SORACOMを装着してWifiを関係のない場所でジョイスティックを動かしてみます。
無事、送られています!
Milkcocoa側でも無事記録されています。グイグイ傾いていますね。
FreeBoardで値をグラフ化してみるとより分かりやすくなりました。
おわりに
なかなかレスポンスが良く、BluetoothとSORACOMでのダブルの無線規格によってやや屋外で配線や場所に縛られにくく、複数台のジョイスティックをみんなで動かすようなネタに妄想が膨んできました。
また、GROVEの入力は水センサー・傾きセンサー・タッチセンサーなど面白いセンサーがあるので、そこから取得できるいろいろなデータをSORACOM経由でMilkcocoaに貯めるのも面白そうです。
それではよき SORACOM + KoshianBit + GROVE Joystick + Raspberry Pi3 Lifeを!