WebUSBの動向を最近追えてなかったので久々に触ってみます。
そして思ったより簡単に出来ました。
こんなの作ってみました。
WebブラウザのカラーピッカーをグリグリやるとLEDの色が変わります。
WebUSB
"ブラウザの"JavaScriptからUSBデバイスへ"直接"アクセスできる技術です。
Chromeだとnavigator.usb
が使えます。
結構前から注目はしていたけど実際にちゃんとArduinoで動かしてみるのは初めてです。
- 2017.8.13: 次世代Webログインフォーム
- 2016.3.13: IoT時代のブラウザAPI
少し前に調べてた時よりも情報が増えてきてて嬉しい限りです。
参考: GoogleのエンジニアがUSBデバイスとウェブを直接接続できるAPIを作成
参考: Webブラウザからハードウェアにアクセス!WebUSB APIを使ってログイン認証を実装してみよう
環境
- 母艦
- Mac Book Pro 2015
- macOS Mojave 10.14
- Arduino IDE 1.8.7
- Google Chrome 70
- マイコン
- Arduino Leonardo
- Amazonで買うと安いかも
- Arduino Leonardo
とりあえずWebUSBを試してみる
WebUSB/Arduinoを試してみた | Arduino | kosakalabの記事の内容を試してみました。
Arduino向けのWebUSBファームを書き込む
kimio-kosakaさんが公開してくれているファームを使います。感謝です。それにしてもすごい家系(わかる人はわかる)。
Arduino/WebUSBライブラリをインストール
このzipファイルをDLして、Arduino IDEの.ZIP形式のライブラリをインクルード
からインストールします。
サンプルコードを書き込み
-
ファイル>スケッチ例>WebUSB>console
のサンプルコードを開きます
最初だけ写真のように
ファイル>スケッチ例>互換性なし>WebUSB>console
になってました。
ボードをArduino Leonardoにして書き込みます。Arduino LeonardoはUSB MicroBケーブルでPCと接続しましょう。
これでOKです。
WebサイトからLチカ操作してみる
- https://kimio-kosaka.github.io/webUSB-arduino/console/ にアクセスします。
- connectボタンを押すと接続しているUSBデバイスが表示されます。
- Arduino Leonardoが表示されるので選択して接続するとconnectedに表示が変わります。
- キーボードのHを押すとLEDが付いてLを押すとLEDが消えます。
内蔵のLEDでも確認出来ますが落ちてたLEDを13番に付けてみました。
すごい、さすがに反応が早い。
フルカラーLEDのサンプル
- 同様に
ファイル>スケッチ例>WebUSB>rgb
のサンプルを書き込む - フルカラーLEDを以下のように配線
- ちなみにフルカラーLEDはオフィスに落ちてたやつなので型番とか分からないけど多分これでいけます。カソード顧問(左から3番目がマイナスなのでGNDに接続)。
引用: https://make.kosakalab.com/nodejs/webusb-arduino/
- https://kimio-kosaka.github.io/webUSB-arduino/rgb/ にアクセスしてメモリをいじると左からRGBの値が変わるのが分かる
自前で作ってみた
今回はArduinoスケッチをいじらずにRGBカラーを変えるコードを作ってみます。
View this post on Instagramふと思い立ってWebUSBでフルカラーLED制御! 楽しいぞこれ #webusb #iotlt
n0bisukeさん(@n0bisuke)がシェアした投稿 - 2018年12月月13日午後12時03分PST
CodePenにあったRadial Color Picker - Vueのデモを元に改良してみます。イメージしてるカラーピッカーのサンプルを探したらVue.jsであったのでVue.js使っての実装です。
このライブラリの挙動に関してはradial-color-picker/vue-color-pickerを見ると良いです。
- Arduinoのスケッチ
元のスケッチのままなのですが、localhostだったり自分の環境で試したいので3行目のhost指定の部分をこんな感じにしました。
WebUSB WebUSBSerial(1, "localhost");
- html
省略
<div id="app">
<button id="connect" @click="connectButtonAction" v-cloak>{{connectButtonText}}</button>
<span id="status" v-cloak>{{statusText}}</span>
<color-picker v-model="color" @input="onColorInput"></color-picker>
<h1 v-text="msg"></h1>
<pre v-html="color"></pre>
<script src="https://jp.vuejs.org/js/vue.min.js"></script>
<script src="https://unpkg.com/@radial-color-picker/vue-color-picker"></script>
<script src="serial.js"></script>
<script src="app.js"></script>
</div>
省略
CSSも入っているので試す場合は元のコードを見た方が良いです。
- serial.js
こちらをそのまま利用させてもらってます。
- app.js
利用したカラーピッカーがRGBじゃなくてHSL形式で値をとっているため、変換させる必要があってこちらのコードを入れ込んでます。
(変換ロジックが上手くいってないのか、なぜかちょっと色がずれてる感じがするんですが...)
var ColorPicker = VueColorPicker;
var app = new Vue({
el: '#app',
components: {
ColorPicker: ColorPicker
},
data: {
msg: 'WebUSB & Radial Color Picker - Vue',
color: {
hue: 50,
saturation: 100,
luminosity: 50,
alpha: 1
},
statusText: '',
connectButtonText: 'Connect',
port: {},
},
methods: {
// HSV(L)をRGBに変換
hsv2rgb: function(hue, saturation, value) {
// まるっとここの中身なので省略 http://shanabrian.com/web/javascript/color-code-convert-hsv-to-10rgb.php
},
//デバイスとの接続
connect: async function() {
try {
await this.port.connect();
console.log('connecting...');
this.statusText = 'connected';
this.connectButtonText = 'Disconnect';
//デバイス側から値が送られてくるのを待ち受ける
this.port.onReceive = data => {
let textDecoder = new TextDecoder();
console.log(textDecoder.decode(data));
}
this.port.onReceiveError = error => console.error(error);
} catch (error) {
console.log(error);
this.statusText = error;
}
},
//カラーピッカーの値が変わると反応
onColorInput: function() {
if (!this.port) return;
let view = new Uint8Array(3);
//HSV(L)の値をRGBに変換
const color = this.hsv2rgb(this.color.hue,this.color.saturation,this.color.luminosity);
view[0] = parseInt(color.red);
view[1] = parseInt(color.green);
view[2] = parseInt(color.blue);
this.port.send(view); //データをUSBデバイスに送信
console.log(this.color.hue,this.color.saturation,this.color.luminosity);
console.log(this.hsv2rgb(this.color.hue,this.color.saturation,this.color.luminosity));
},
//connectボタンのトグル処理
connectButtonAction: async function(){
if (this.port) {
this.port.disconnect();
this.connectButtonText = 'Connect';
this.statusText = '';
this.port = null;
} else {
try {
const selectedPort = await serial.requestPort();
this.port = selectedPort;
this.connect();
} catch (error) {
this.statusText = error;
}
}
}
},
//ページ立ち上げ時
mounted: async function() {
const ports = await serial.getPorts();
if (ports.length == 0) {
this.statusText = 'No device found.';
} else {
this.statusText = 'Connecting...';
this.port = ports[0];
this.connect();
}
}
});
作ったコードはGitHubにも載せておきます。
https://github.com/n0bisuke/webusb_vue_sample/tree/master/colorpicker
Nuxt.jsとSkyWayで1時間でビデオチャットを作ってみるを書いた時もそうですが、navigatorにアクセスする初期起動はmountedに入れてあげて、その関数はmethodsに入れてあげればだいたい移植できそうですね。
参考にさせてもらってる元のrgb.jsと見比べてみてください。
所感
楽しい。
WebブラウザのJavaScriptが直接Arduinoにつながるってすごいですね。
今回はブラウザ -> Arduino
だったのでArduino -> ブラウザ
もそのうち試してみます。
WebBluetoothと同じような楽しさがあるんですけど@masciiくんの記事にも書いているようにブラウザが直接デバイスに繋がると色々と面白いことできそうですよね。
皆さんもお試しください!
実装してて詰まったところ
- "DOMException: Unable to claim interface"
調べたら、こちらに当たりました。
他のタブで開いてて、すでにUSB接続してる場合に怒られるみたいです。
他のタブやウィンドウなどで開いてないか確認して閉じてから再トライしましょう。
僕はこれが原因でした。
参考
めちゃ参考になりました!現状のWebUSBはとりあえずここ読んでおけばOKって感じがします。