JoyConの加速度センサーを取るための設定変更についての話


はじめに


JoyConの加速度センサーやジャイロセンサーの値を取得したい!


ということで方法を調べたところ、

「JoyConにデータ列を送信して、設定を変更しないといけない」

ということがわかりました。


色々と調べながら、なんとか実装をしたのですが、

「知らなかったせいで詰まっていた」ところがいくつかあったので、

今回は、設定変更のためにJoyConに送信する内容について解説し、知見を共有しようと思います。


注意


この記事は、Bluetooth初心者かつハードウェア初心者によるものです。

そのため、不正確な情報が含まれている可能性があります。

(現状、私の環境では正常に動作はしています。)


本ページの情報の使用/参照によって発生したいかなる不利益/損害について責任を負いません。


実行環境


- MacBook Pro (Retina, 13-inch, Early 2015)


前知識


その1 HIDプロファイルとは


JoyConはPCとBluetooth接続すると、自動的にHIDプロファイルのデバイスとして認識されます。


HIDプロファイルとは、Bluetoothプロファイル(通信手順を標準化したもの)の一つであり、

以下のような3タイプのレポート(データのまとまり)を利用して通信します。

  • Inputレポート ... デバイス(JoyCon)からホスト(PC)へ入力するレポート。

  • Outputレポート ... ホストからデバイスへ出力するレポート。

  • Featureレポート ... よくわからない、今回は利用しない。


詳しく知りたい方は、こちらの記事を参考にすると良いと思います。

HID/レポート - おなかすいたWiki!


その2 JoyConへのOutputレポートの形式


センサー値取得のための設定の変更には、HIDプロファイルのOutputレポートを使用します。

細かいことは省略するとして、基本的にはJoyConに対して、

// PureJavaHid-Apiの例

byte[] buf = {
  command, 
  (++(packetCount)&0xF),  //packetCountは送信ごとに1増加 0x0~0xFをループ
  0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40, 
  subcommand, 
  argument[0], ... , argument[argument.length-1]
  };

といったようなデータ列bufをOutputレポートにセットして送信します。

Pure Java HID-APIでは、setOutputReport(0, buf, buf.length)のようにします。

言語やライブラリによって多少書き方などに違いはあれど、ほぼ同様な送信方法だと思います。


buf内の、command, subcommand, argumentについてはひとつずつ説明していきます。


command

簡単に言うなら、Outputレポートの種類です。

Nintendo_Switch_Reverse_Engineering/bluetooth_hid_notes.md

こちらの情報を見ると、

Output 0x01, Output 0x03, ...

というようにたくさん並んでいます。このOutputの後の16進数がcommandの値にあたります。

この中から必要なものを選んで使っていきましょう。

ちなみに今回は使うのはすべて、0x01というsubcommandを送るためのcommandです。


subcommand

簡単に言うなら、具体的な要求内容です。

Nintendo_Switch_Reverse_Engineering/bluetooth_hid_subcommands_notes.md

今度はこちらの情報を見ると、

Subcommand 0x00, Subcommand 0x01, ...

などというようにたくさん並んでいます。お察しの通り、Subcommandの後の16進数がsubcommandの値にあたります。

commandと同様に、この中から必要なものを選んで使っていきましょう。


argument

簡単に言うなら、要求のためのパラメータです。

subcommandの方のページで、Subcommand 0x03や0x38のところに表が書いてありますよね。

これが、argumentに入れる値の情報になります。argumentはbyteの配列です。

subcommandによっては複数の値を設定することもあります。


本題


設定変更のための送信については、

output(command, subcommand, argument);

という関数を作ったとして説明します。

この関数を実行すると、引数からデータ列を作成して送信してくれるものとします。


加速度・ジャイロセンサーの値の取得


// Subcommand 0x40: Enable IMU (6-Axis sensor)
// One argument of x00 Disable or x01 Enable.
output(0x01, 0x40, new byte[] {0x01});

// output()の間に適当に一定時間停止を入れる

// Subcommand 0x03: Set input report mode
// Argument 30: Standard full mode. Pushes current state @60Hz
output(0x01, 0x03, new byte[] {0x30});

output()の間には、JavaのスレッドのThread.sleep(100);などのように一定時間停止を入れないとうまく動作しない可能性が高いです。


実行すると、(私のプログラムでは)センサー値を含んだInputレポートを連続的に送ってくるようになります。

これだけでもう設定変更完了です。プログラミングってやっぱり調べる時間の方が圧倒的に長いですね。


ただ、送られてくるデータはそのままでは使えません。

Nintendo_Switch_Reverse_Engineering/imu_sensor_notes.md

こちらの情報をもとにデータを加工する必要があります。(まだ実装中)


↓ある程度実装できて、

ジャイロセンサーの値のみを利用してポインターっぽく表示したもの

youtu.be


おわりに

(おわりに の後におまけもあります)


JoyConを触り始めたときは、JoyConに対してデータを送信する必要があるとは思いませんでした。

というか、データを送信できるとも思っておらず、そのせいで延々と詰まっていました。

この記事が、私と同じように知らないせいで詰まっている人にとって役に立つと良いです。


Joy-Conには加速度・ジャイロセンサーだけでなく、もっと多くの機能があります。

IRセンサーもあれば、HD振動もある。ボタンは左右合わせて20個以上もある。

なぜか、HOMEボタンの光り方もめっちゃ制御できたりもします!笑


JoyConは、本当に多機能でおもしろいコントローラーです。

いっそ、Switchを持っていない人も、JoyConを買ってもいいんじゃないでしょうか。


本記事に関する開発はこちらで公開しています。

github.com


おまけ


今回解説した、設定変更と同様にして、

JoyConのライトなども制御することができます。

youtu.be


その1 HOMEボタンを光らせる


// Light up Home button
byte[] b = {
  byte(0xaf), byte(0x00), 
  byte(0xf0), byte(0x40), byte(0x40), 
  byte(0xf0), byte(0x00), byte(0x00), 
  byte(0xf0), byte(0x00), byte(0x00), 
  byte(0xf0), byte(0x0f), byte(0x00), 
  byte(0xf0), byte(0x00), byte(0x00)
  };
output(0x01, 0x38, b);

HOMEボタンが光ります。


JoyConのホームボタンのライトは、

  • 最大15パターンの明るさ

  • 各パターンの持続時間

  • 次パターンへのフェード時間

を設定してループさせることができるみたいです。


詳しい情報は、Subcommand 0x38に書いてあります。


その2 Playerのライトを制御する


// Light up Player button
output(0x01, 0x30, new byte[] {byte(0xa5)});

プレイヤー番号を表示するあれも制御できます。

こちらは、点滅と点灯を制御できます。

詳しい情報は、Subcommand 0x31に書いてあります。

Realtime FrameworkでWebPush通知をする


はじめに

Webプッシュ通知ってご存知ですか?

プッシュ通知はスマホアプリではよくありますよね。

簡単に言えば、Webプッシュ通知はそのWeb版です。


今回はこのRealtime FrameworkのWeb Push Notificationsを利用して、

自分のサイトへリンクを飛ばすWebプッシュ通知を実装できたので紹介します。


動作環境


公式サンプル

Web Push Notificationsのサンプルをこちらで試すことができます。

「SEND ME A PUSH NOTIFICATION」という青いボタンを押すとプッシュ通知が来るはずです。

f:id:turtley_fms:20171218151445p:plain


実装


通知をするための設定

Realtime FrameworkのAPIやFirebaseの設定などは、

公式のチュートリアルやコード内のコメントがとてもとてもとても親切なので、

こちらを参考にしてください。

github.com

(それでもわからなければこの記事にコメントしてくだされば対応するかもしれません)

GithubにあがっているコードをしてDLして編集していくことになります。


チュートリアルが完了したらサンプルと同じように実行できている状態になります。


通知内容の変更

ここではチュートリアルの先、

プッシュ通知の内容を変更する方法について説明します。


プッシュ通知の内容は

service-worker.js内のnotificationOptionsの値をいじることで変更することができます。

  • body:本文
  • icon:通知につくアイコン
  • click_url:プッシュ通知から飛ばされるページのurl

などいろいろパラメータがあります。


適当に変更してみました。

f:id:turtley_fms:20171218155244p:plain

f:id:turtley_fms:20171218155115p:plain

プッシュ通知をクリックすると、このブログのトップページに飛びます。


注意

ここで1つ注意があります。

cookieにService Workerの情報が残っていると、

変更が反映されない可能性があります。

コードを書き換えたのに変更されない時は、cookieを削除してみましょう。


おわりに

これでWebプッシュ通知ができるようになりました。

まだどこまで応用が利くのかははっきりしていませんが、

通知を送ってくれるWebサービスを作れるようになったわけです!

ユーザによっていろいろパラメータを変更できるようにできたら楽しそうですね。

Processing 文字の輪郭追跡 作品集

はじめに

総コン Advent Calendar 2017の14日目の記事です。


前回( Processingで簡単な輪郭追跡をする - 忘れないうちに(旧) )、

文字の輪郭追跡について、紹介させていただきました。

今回は、文字の輪郭追跡を利用してこれまでに作成したものを紹介します。


本当は、OpenProcessingで公開しようと考えていたのですが、

processing.jsのpixels周りに問題があるのか、エラーが発生したので動画で紹介します。


作品集


一つ目は、「総」を輪郭追跡する様子を可視化してみたものです。

だんだんと探索が完了していく様子はかわいいですね。

youtu.be


二つ目は、「亀」を輪郭追跡する様子を可視化してみたものです。

一つめと同じようですが、今度は内側の輪郭を追跡できている様子を確認することができます。

輪郭が内側であるかを判定するのになかなか苦労したので感慨深いです。

youtu.be


三つ目は、よくわからないです。

完成した文字図形を3D空間上に表示し、音の大きさによって形を崩すことで、

VJ素材のようにしてみたものです。よくわからないです。

でもちょっとおもしろいですよね。

表示文字はテキストボックスから変更することができます。

youtu.be


四つ目は、「総」の文字図形をProcessingのFisicaで表示したものです。

今度は、文字のまとまりごとにバラバラになります。

これもvertexで文字を表現できるようになったからできたものです。

こういう表現は、他の方が外部ライブラリを公開しているそうですが、

自分で作ってしまいました。

youtu.be


まとめ

ちょっと考えただけでもこれだけの作品が作ることができました。


また他にも、同じ輪郭数ならvertexの数を揃えることで、

ある文字が他の文字にだんだんと変化していくというものも作れそうです。


プログラムはここ( GitHub - Turtar/CreateCharacterModel )で公開しています。

多分読むのも大変ですが、よろしければ使ってみてください。


少しでも、文字の輪郭追跡はおもしろいと感じていただければ私は満足です。

Processingで簡単な輪郭追跡をする

はじめに

この記事は NCC Advent Calendar 2017 の4日目

かつ、総コン Advent Calendar 2017の7日目の記事です。

(使いまわしでごめんなさい)


BrokenCharacter - YouTube

こんな感じに、Processingでテキストの形を自由に崩したい!と思ったことはありませんか?

テキストの形を自由にいじれれば、何か面白いことができそうだと思いませんか?

テキストの形をいじるには、テキストをvertexで図形にする必要があります。

テキストをvertexで作るには輪郭を辿って行かなくちゃいけません。


ということで今回は、画像処理のアルゴリズムである輪郭追跡について取り上げたいと思います。


ちなみに「簡単な」輪郭追跡というのは、白背景のテキストの輪郭追跡をするということで、

普通の写真などよりもずっと処理が単純ということです。


輪郭追跡のアルゴリズムはこちらを参考にしています。

輪郭追跡処理アルゴリズム 画像処理ソリューション


実行環境

Processing3.3


ソースコード

ソースコードはGithubGistで公開しています。(「Download ZIP」のボタンでDLできます)

ShapeCharacterModel.pde · GitHub


本題

コードを一行ずつ解説をすると大変なことになるので、処理の流れをつかんでもらおうと思います。

最終的なソースコードはこの記事の最後に公開しておきます。


1. 元となるテキストを描画

まずは、普通にテキストを描画します。

ここは単純な描画なのでサクサク行きましょう。

(フォントによっては最終的な形がバグってしまうことがありますがあまり気にせずに...)


2. テキストの縁を黒く塗る

次は、テキストの縁取りをします。

これはまだ輪郭追跡ではなく、白色のピクセルから白色じゃないピクセルに移動したら、

そのピクセルを黒く塗るという処理をしています。


3. 黒い輪郭を追跡

輪郭の探索の動き

黒い輪郭のピクセルを発見したら輪郭追跡を開始します。


一つの輪郭の追跡が完了したら、今度は、前回の開始点からまた探索を開始します。

そして、また黒い輪郭のピクセルを発見したら、次の輪郭追跡を開始します。


輪郭追跡の処理が完了した状態です。

縁を黒く塗ったときのものとほぼ同じに見えますが、

よく見ると少しだけ輪郭が滑らかになっています。


輪郭追跡アルゴリズムの動き

前述した通り、輪郭処理アルゴリズムは、

輪郭追跡処理アルゴリズム 画像処理ソリューション

こちらの記事を参考にしています。

こちらはとてもわかりやすいので、基本的な部分の解説はこちらにお願いしてしまいます。


ここでは、私がこの記事で疑問に思ったところを解説したいと思います。

私が疑問に思ったところというのは、


ここにちょっとした規則があります。

例えば、0の次に5、1の次に6、・・・などは絶対に来ません。(なぜかは、ちょっと考えると分かります。)

一般的に表現すると、

  一つ前の輪郭の向きから時計回りに3つ分の向きには輪郭が存在しない。

という事になります。


というところです。


下の図を使って解説します。

図の処理の順番は以下の通りです。

  1. 薄い赤紫色の矢印... 前項の輪郭の探索

  2. 赤色破線の矢印... 輪郭追跡のための探索①

  3. 赤色実線の矢印... 発見した輪郭への移動①

  4. 青色破線の矢印... 輪郭追跡のための探索②

  5. 青色実線の矢印... 発見した輪郭への移動②


さて、この図で「一つ前の輪郭の向きから時計回りに3つ分の向き」とは

どこのことを言うのでしょうか。


青色の探索からみて一つ前の「輪郭の向き」とは赤色実線の向きです。

すなわち、赤色実践の向きから「時計回りに3つ分の向き」とは、

下の図の斜線のピクセルになります。

このピクセルは、赤色のときにすでに探索して輪郭じゃないとわかってますよね。

だから、この方向のピクセルは探索しなくて構わないんです。


コードを書いている時にはまだまだたくさん詰まったところはありましたが、

取り上げるとキリがないのでここまでにしておきます。

ソースコードにもコメントをたくさん書いたので参考にしていただければ良いと思います。


Processing テキストボックスで日本語入力をする


はじめに

Processingでアプリ内で日本語入力をしたいのに、

検索すると「日本語対応フォントの作成方法」ばかりが出てくるので書いておきます。


テキストボックスの作成するために、JavaGUIライブラリであるSwingを使用します。

とりあえず結論

下記のコードを実行すればテキストボックスを追加できます。

import javax.swing.*;
import java.awt.*;

JLayeredPane pane;
JTextField field;
JTextArea area;

void setup() {
  size(200, 200); 

  // SmoothCanvasの親の親にあたるJLayeredPaneを取得
  Canvas canvas = (Canvas) surface.getNative();
  pane = (JLayeredPane) canvas.getParent().getParent();

  // 1行のみのテキストボックスを作成
  field = new JTextField();
  field.setBounds(10, 10, 150, 30);
  pane.add(field);

  // 複数行のテキストボックスを作成
  area = new JTextArea();
  area.setLineWrap(true);
  area.setWrapStyleWord(true);
  JScrollPane scrollPane = new JScrollPane(area);
  scrollPane.setBounds(10, 50, 150, 100);
  pane.add(scrollPane);
}

void draw() {
  println( field.getText() +","+ area.getText() );
}

void keyPressed() {
  if (keyCode==ENTER) {
    field.setText("");
    area.setText("");
  }
}

このコードを少しいじれば、十分に他のプログラムに組み込めるかと思います。


Swingには様々なGUIがあり、JTextFiledと同じような要領で使用することができます。

色々試してみるといいと思います。

JTextArea (Java Platform SE 6)

解説

GUIを追加するための準備

Canvas canvas = (Canvas) surface.getNative();
pane = (JLayeredPane) canvas.getParent().getParent();

Processingのウィンドウは下図のような入れ子構造になっています。

f:id:turtley_fms:20171201035831p:plain
 
SwingのGUIはこの中のJLayeredPaneに追加していかなければいけません。

そのため、ここではJLayeredPaneを取得しています。

まず、surface.getNative()によって、SmoothCanvasクラスのインスタンス変数を取得します。

次に、これをCanvasクラスにキャストします。

最後に、getParent()によって親が取得できるので、SmoothCanvasの親の親であるJLayeredPaneを取得します。


1行のみのテキストボックスの作成

// 1行のみのテキストボックスを作成
field = new JTextField();
field.setBounds(10, 10, 150, 30);
pane.add(field);

1行のみのテキストボックスは、JTextFieldを利用します。

setBounds(x, y, w, h); はJTextFieldの表示位置を設定できます。

pane.add(hoge); はSwingの部品をpaneに追加することができます。


複数行のテキストボックスの作成

// 複数行のテキストボックスを作成
area = new JTextArea();
area.setLineWrap(true);
area.setWrapStyleWord(true);
JScrollPane scrollPane = new JScrollPane(area);
scrollPane.setBounds(10, 50, 150, 100);
pane.add(scrollPane);

複数行のテキストボックスは、JTextAreaとJScrollPaneを利用します。

JTextFieldとは異なり、単にJTextAreaをJLayeredPaneに追加するとスクロールできません。

そこで、スクロールを可能にするJScrollPaneにJTextAreaを追加し、

それをJLayeredPaneに追加します。

JLayredPane > JScrollPane > JTextArea という構造になります。


area.setLineWrap(true); は入力がテキストボックスの右端に達したときに、

その先を突っ切るか、折り返すかの設定をしています。trueで折り返しです。


area.setWrapStyleWord(true); は単語を丸ごと折り返すかどうかの設定です。

// trueだと、
hoge fuga    
piyo

// falseだと、
hoge fuga pi
yo

みたいな感じになるイメージです。

どちらでも構いませんが、ここではtrueで単語が崩れないように設定しています。

あとのコードは、JTextFieldと同じようなコードですので説明省略です。


テキストの取得とセット

field.getText(); field.setText(); area.getText(); area.setText();

これらで、テキストを取得したり、セットできたりできます。

参考

GUIを扱う準備をする(Swing) | 自己啓発。人生について考える

processing3でAWT/swingを使う。 - Qiita