-->

2017年10月28日土曜日

Maker Faire Tokyo 2017に出展しました

遅くなりましたが、Maker Faire Tokyo 2017(8/5&6)に出展してきました。出展物は多数決マリオということで複数人でプレイできるスーパーマリオです。展示前は果たして面白いのだろうか?と自問自答していましたが、思っていたより好評だと感じました。

以下のように展示していました。

お隣さんは全自動ぷよぷよプレイヤーのmayahさんとMRファミコンのKOALABさんでした。
全自動ぷよぷよプレイヤーは安定感抜群でした。実機を使用しているということで実動作でのいわゆるラグに苦労しているとのこと。「全自動シレンで同じ現象あるある」と共感。実は行動を決定するAIを作ること自体はそんなに難しくなかったりするけど、それを実機に適用するところは泥臭い作業が必要であることが多かったりします。そして、人間はそんなことを認識するまでもなくやってのけているので、このあたりの苦労が伝わりにくかったりもします。
MRファミコンは終わり際に体験させていただいたのですが、ただファミコンをプレイさせているのではなくうまくパッケージ化していてうまいと思いました。どうパッケージ化していたかは体験して確認してみてね(適当)。確かに、ただプレイさせるだけだと終わるタイミングがないですからね。壁が必要だということで段ボールのパネルを使用していました。大変そうでした。

今回の展示についてまとめてみます。

  • よかった点
    誰もが知っている/プレイできるコンテンツである
     →親子連れで来てもみんなでプレイできた
    コンセプトがわかりやすかった
     →説明も最小限で理解してもらえる
  • 悪かった点
    一人ではプレイできない
     →子供が無理やり2つのコントローラでプレイすること多し
     →これは仕方がないかも
  • 改良点
    ボタン入力の可視化ができると良いかも
     →現在誰がどのボタンを押しているかわかる

今回から東7/8ホールになって、入口からの距離が遠くなりましたが、展示スペースが広くなりました。1日目は空調がかなり効いていて、2日目は空調控えめでした。飛行物体の展示があったので影響軽減のためかもしれません。もしくは寒いとのクレームがあったのかもしれません。はてさて来年はどうなるのでしょうね。このまま規模を拡大していくのかどうするのか。そして当研究所は来年も出展できるのかどうか…。

個人的に素晴らしいと思ったのが、超小型飛行体研究所さんの展示タイトルである「空をみろ! ねこだ、やわらかい戦闘機だ、飛んでるぞ、ゆっくりだ」です。特に"ゆっくりだ"のセンスが素晴らしいと思いました。所長がこの展示のタイトルを考えたとして"ゆっくりだ"は絶対出てきません。ちなみに超小型飛行体研究所さんの展示物は"ゆっくりだ"と言えるぐらい優雅に飛行していました。

あ、アンケート回答するの忘れた…。

2017年10月23日月曜日

2017年6月11日日曜日

Nintendo SwitchのJoy-Con解析

海外の方でNintendo Switchのコントローラの通信方法について解析が進んでいるようです。
Joy-ConはSwitchの画面の両端に接続して使用することもできますし、取り外して使用することもできます。所長はてっきりJoy-Conと本体の通信にはBluetoothのみを使用していると思っていたのですが、画面と接続しているときには接続部の端子接続により有線による通信を行っているようです。

しかも、解析の結果、有線接続ではどうやらシリアル通信を行っているらしいと。注目すべきはその速度で、1000000bpsと3125000pbsを使用しているようだとのこと。

ほう…。

なかなか面白い情報です。シリアル通信とはいえ、速度が1Mbps越えでは通常のPCやマイコンでは対応できないでしょうし。

まあ、FPGA使えば良いのですがね、うしし。

2017年4月4日火曜日

多数決マリオ

新作です。今年はこれでいこう。


ラズベリーパイゼロとPICを使用しています。

NES Classic Editionのコントローラの通信内容解析⑥

では、ダンプしてわかった「NES Classic Edition」のコントローラの通信内容をまとめます。以下は起動してから本体側から見た動作になります。
  • レジスタ"0xF0"にデータ"0x55"をライト
  • レジスタ"0xFB"にデータ"0x00"をライト
    この2つの手順が暗号化OFF設定らしい。
  • レジスタ"0xFE"にデータ"0x03"をライト
    この設定はよくわからない。デバイス識別子を書き換えている?
  • レジスタ"0xFA"から6Byteのデータ(デバイス識別子)をリード
    デフォルトコントローラなら"0x01 0x00 0xA4 0x20 0x03 0x01"が読み出される。これ以外の識別子が読み出されたときの動作は未調査。たぶん、動かないのではないかと思われる。そう考えると、アドレス"0xFE"に"0x03"をライトしたのはクラシックコントローラなどを使用してもデバイス識別子を似通ったものにするため?
  • レジスタ"0x00"から21Byteをリード
    この中にボタン情報が格納されている。該当箇所はアドレス"0x06-0x07"の2Byteっぽい。何故に21Byte?他のデータは何?など疑問は残るが深く考えずスルーすることも大人の知恵だったりする。
    ちなみに後日解析した結果、この21Byteのボタン情報以外のデータ部分にオール"0x00"を設定するとうまく動かないという現象が発生したので以下の値を使うのがよろしい。
    0x83 0x85 0x85 0x86 0x00 0x00 0xXX 0xXX 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
    ※XXはボタン情報の格納場所
    ※オール"0x00"にすると"NES Classic Edition"の起動時のゲーム選択画面では操作できるが、ゲーム選択後に操作不可能になるという謎の現象に陥る
  • 以降、レジスタ"0x00"からの21Byteリードが繰り返される
    リード間隔は約6.6ms。ということは秒間150回リード?120回でも180回でもなく、なんか中途半端な気が…。
ボタンの状態は2Byteの負論理で表される。負論理とはつまり、押していないときは"1"になる。
  • 何も押していないときは"0xFFFF"
    上でも書いているが、該当データはレジスタ"0x06-0x07"の場所のデータ。
  • 各ボタンの該当位置は以下の通り
    Right Down X Select X Start X X X B X A X X Left Up
    例えば、右キーを押している場合は"0x7FFF"、Bボタンを押している場合は"0xFFBF"、右キーとBボタンを同時に押している場合は"0x7FBF"となる。
以上で、「NES Classic Edition」のコントローラの通信内容解析を終わります。
てっきり暗号化していると思いこんでいたのでちょっとびっくりしました。この動作を真似すれば「NES Classic Edition」のコントローラを作成することができます。

NES Classic Editionのコントローラの通信内容解析⑤

ここで、解析されているクラシックコントローラの通信内容についてまとめておきます。
  • コントローラは内部に256Byte(アドレス8bit)のレジスタを持っている
  • コントローラにはI2Cからレジスタにアクセスすることで通信を行う
I2Cのアクセスに関しては以下のようなフォーマットです。
  • ライト動作
    0xA4 "レジスタアドレス" "データ0" "データ1" …
    "0xA4"はデバイスアドレス"0x52"+ ライトビット"0"です。この次にライトするレジスタアドレスを指定する。次にライトするデータを指定、データが連続したときにはレジスタアドレスをインクリメントしながらライトしていきます。
  • リード動作
    0xA4 "レジスタアドレス"
    0xA5 "データ0" "データ1" …

    最初にライト動作でリードしたいレジスタアドレスを指定して一旦通信を終わります。次にリード動作をするのですが、最初の"0xA5"はデバイスアドレス"0x52"+ リードビット"1"です。次にコントローラ側からリード対象のレジスタアドレスのデータを読み出すのですが、ライト動作と同じく、データが連続したときはレジスタアドレスをインクリメントしながらリードされます。連続リードはリードしている側のNACKによって止められます。
主要なレジスタの機能について以下にまとめます。
  • 0xFA-0xFF(6Byte)
    デバイス識別子。自分がどのデバイスかを格納している。例えば、"0100A4200101"ならクラシックコントローラPROというような感じ。
  • 0x20-0x2F(16Byte)
    キャリブレーションデータらしい、何のかは知らない。
  • 0x30-0x3F(16Byte)
    0x20-0x2Fのデータのコピーらしい、なぜコピーしているかは知らない。
  • 0xF0(1Byte)
    暗号化のON/OFFに関係しているレジスタ。0xAAを書き込むと暗号化ONで、0x55を書き込むと暗号化OFFらしい。
  • 0x00-0x05(6Byte)
    コントローラのボタン状態。何のボタンを押しているか、スティックの状態がどうかを格納している。
  • 0x06-0x07(2Byte)
    「NES Classic Edition」のコントローラのボタン状態が格納されているっぽい。暗号化OFFのときに有効?
以下のサイトを参考にしています。
Wiimote/Extension Controllers - WiiBrew
Wiimote/Extension Controllers/Classic Controller - WiiBrew
-> WiiBrewというサイトに詳しいことがまとまっています
WiiLi.org Wii Linux - Wiimote/Extension Controllers/Classic Controller
-> WiiLiというサイトもあったようですが、今はなくなっているようなのでアーカイブサイトのリンクです

次 → NES Classic Editionのコントローラの通信内容解析⑥

NES Classic Editionのコントローラの通信内容解析④

では、実際の通信内容を見ていきます。まずはI2Cのパラメータについて。
  • SDA&SCLは3.3Vでプルアップ
  • 本体側がマスタ、コントローラ側がスレーブ
  • コントローラのスレーブアドレスは"0x52"
  • 通信速度は400kHz
また、DeviceDetectはHighで接続されているとみなされるようです。
起動時の通信内容をロジアナでプローブしたものが以下です。※"A2"がDeviceDetect、以下の画像はリセット時のものなのでHighのままです

通信内容を文字に起こしたものが以下です。
  1. DeviceDetectが"Low"から"High"に変化
  2. ライト(マスター → スレーブ)
    0xA4 0xF0 0x55
  3. ライト(マスター → スレーブ)
    0xA4 0xFB 0x00
  4. ライト(マスター → スレーブ)
    0xA4 0xFE 0x03
  5. ライト(マスター → スレーブ)
    0xA4 0xFA
  6. リード(スレーブ → マスター)
    0xA5 0x01 0x00 0xA4 0x20 0x03 0x01
  7. ライト(マスター → スレーブ)
    0xA4 0x00
  8. リード(スレーブ → マスター)
    0xA5 0x83 0x85 0x85 0x86 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  9. 4.6ms程度待機
  10. ライト(マスター → スレーブ)
    0xA4 0x00
  11. リード(スレーブ → マスター)
    0xA5 0x83 0x85 0x85 0x86 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  12. 4.8ms程度待機
  13. ライト(マスター → スレーブ)
    0xA4 0x00
  14. リード(スレーブ → マスター)
    0xA5 0x83 0x85 0x85 0x86 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  15. 4.8ms程度待機
  16. …(以下ループ)
あれ?暗号化されてなくね?どういうこと?

次 → NES Classic Editionのコントローラの通信内容解析⑤

NES Classic Editionのコントローラの通信内容解析③

ようし、I2Cを使用していることはわかった。信号線の配置もわかった。これでコントローラを自由自在に使いこなせるぞ!え?暗号化?なんですかそれ?

ということで、コントローラと本体ではI2Cで通信を行っているのですが、その通信内容は暗号化されているようです。
例えば、以下のページではI2Cの暗号化された通信内容をダンプしています。
コメを噛め» Blog Archive » Wii クラシックコントローラとの通信

とは言え、所詮はハードウェアで実装されている暗号化なのですでに解析されているようです。

Reverse engineering the Wiimote's encryption
上記が暗号解析成功報告みたいです(2008年)。"weak"という言葉があるので、暗号強度的には弱いのでしょうね、ハードウェア固定ですし。

暗号化のプロトコルについて以下に箇条書します。
  1. 暗号キーは本体側からコントローラ側に送付する
    起動時に本体側から16Byteの暗号キーをコントローラ側に送信する。暗号キーはランダムで生成されるようです。
  2. コントローラ側では暗号キーを基に暗号テーブルを作成する
    "S-Box"という共通鍵暗号化技術を使っているようです(よく知らない)。
  3. コントローラ側から本体側に情報を送るときには暗号テーブルで暗号化してデータを送付する
ここからは電子工作などでクラシックコントローラやヌンチャクコントローラを使用する場合の話です。
  • 本体側を作る場合
    クラシックコントローラなどを機器のコントローラとして使用する場合は、暗号キーを固定値にすることで容易に復号ができます。前述の通り、暗号化に使用する暗号テーブルは起動時に送付する暗号キーによって生成されるので、逆説的に暗号キーを固定すると暗号テーブルも固定されます。すると実質的に復号機能はいらなくなります。
    実際には暗号キーをすべて"0x00"にして使用するということをよくやるみたいです。
  • コントローラ側を作る場合
    コントローラ側の機器を作成する場合は、暗号キーから暗号テーブルを作成する機能と暗号化機能は必須です。さらに本体側がI2Cのマスターになるので作成する機器はスレーブとして機能する必要があります。
    もっとも、所長以外にコントローラ側を作りたい人なんているんですかね?
暗号化するためのの実コードは探せば色々なところにありますが、出所は一緒なようでほとんど同じ実装のようです。

実コード掲載のされているページの例
wii-retropad-adapter/src/wii-retropad-adapter at master · bootsector/wii-retropad-adapter · GitHub

次 → NES Classic Editionのコントローラの通信内容解析④

NES Classic Editionのコントローラの通信内容解析②

では、「NES Classic Edition」のコントローラの通信内容について見ていきます。

まず、通信方法なのですがI2Cが使用されています。ですので、スーパーファミコンなどと比べてだいぶ扱い易くなっています。

参考までに以前調査したものです。
スーパーファミコンコントローラの解析 - ハードウェアとか研究所

スーパーファミコンはほぼ独自規格のようなものだったのですが、I2Cであれば既存のマイコンなどでも簡単に扱えます。

コネクタについては以下のページが参考になります。
Wiimote/Extension Controllers - WiiBrew
コネクタには6端子ありますが、1端子が未使用なので使用しているのは実際には5端子です

信号線についてはこちら。
「ミニファミコン」で大きなコントローラーも使えるように改造してみました(ウェブ情報実験室) - Engadget 日本版
上記のページではファミコンクラッシクミニを改造して、クラシックコントローラを接続しています(デフォルトのコントローラが小さいので)。つまり、Wiiなどで使用できるクラシックコントローラで使用されている通信方法とファミコンクラシックミニのコントローラの通信方法は同じだということみたいです(よくよく考えれば当然か)。さらに「NES Classic Edition」のコントローラも同じだということは想像するに易いです。


今回使用した延長ケーブルの中の線には色がついているのですが、参考にしたページと同じでした。以下に載せておきます。
  • 黄緑色
    → GND
  • ピンク色
    → VCC(電源)3.3V
  • 水色
    → SDA(I2Cデータライン)
  • 白色
    → SCL(I2Cクロックライン)
  • 黒色
    → デバイス検出(コントローラが接続されているかどうかの検出)
次 → NES Classic Editionのコントローラの通信内容解析③

NES Classic Editionのコントローラの通信内容解析①

今回はNES Classic Editionのコントローラと本体で行われている通信内容を解析してみます。

使用するのは海外版ファミコンミニである「NES Classic Edition」です。


「NES Classic Edition」はコントローラをコネクタで接続する仕様になっています。コネクタはWiiリモコンについている拡張コネクタと同様のものが採用されています。今回は改造しない方法を取りますのでWiiリモコンに接続するヌンチャクコントローラのための延長ケーブルを使用します。

簡単に言うと、この延長ケーブルをぶった切って通信内容をプローブします。本体を改造しても同様のことはできるのですが、それをやらないのは所長の任天堂へのリスペクトだと思ってください。

次 → NES Classic Editionのコントローラの通信内容解析②

2017年2月18日土曜日

OpenCV(3.x系)でマウスホイールを使う(2.4.x系は不可)

いつの間にかOpenCVでマウスホイールが使えるようになっていました。
どうやらOpenCV3.xから追加されたみたいです。
ウィンドウ上でのマウスホイールの変化がイベントとして検出できます。
※コールバック関数内で"EVENT_MOUSEWHEEL"で引っ掛ける
ホイールがどの方向に回ったのかはgetMouseWheelDelta関数の返り値で知ることができます。
まず、その値が正の値か負の値かで回転した方向がわかります。
例えば、Windows7で値を観測した場合、以下のような値が得られます。
 ホイールを奥に回す → 120(正の値)
 ホイールを手前に回す → -120(負の値)
回転量は120を単位(1ノッチ)とした値で取得できます。
マウスの設定にもよるとは思いますが、通常は"120"もしくは"-120"の値しか見えないのですが、
勢いよくホイールを回すとたまに"240"とか"-240"という値を見ることができます。

この関数はWindowsだけしかサポートしてないって書いてあったのですが、同様のことをLinux(ラズパイ3)で実行すると以下のようになります。

 ホイールを奥に回す → -1(負の値)
 ホイールを手前に回す → 1(正の値)

値の符号は反転していますが、使えないことはなさそうです。
その他、Linuxでは"EVENT_MOUSEWHEEL"イベント発生時のマウスポインタ座標がうまく取得できませんでした。
※コールバック関数内では常時(x, y) -> (-3, -3)が取得される
※Windows7では、イベント発生時のマウスポインタの位置が正常にx&yに格納された
やはり完全にサポートしているわけではなさそうです。

ホイール動作が使えるようになると、表示している画像の拡大や縮小処理を直感的に行えるので便利です。

以下はラズベリーパイ3で使用しているコードで、ホイールイベントの使用サンプルコードです。
前述の通り、ラズベリーパイ3ではx&y座標がうまく取得できないので、"CV_EVENT_MOUSEMOVE"イベント時のx&y座標を保持しておくことで代替しています。
使用しているOpenCVのバージョンは"3.2"です。

#include <stdio.h>

// for OpenCV
#include "opencv2/opencv.hpp"

// マウスポインタの座標保持
cv::Point MousePointer(0,0);

void mouse_callback(int event, int x, int y, int flags, void* data);

int main(int argc, char *argv[]) {

    // 画像ロード
    cv::Mat image = cv::imread("test.png");
    if(image.empty()) {
        printf("[error] : can't load image...\n");
        return 0;
    }

    // ウィンドウの作成
    char window_name[256] = "test";
    cv::namedWindow(window_name, CV_WINDOW_NORMAL);
    cv::moveWindow(window_name, 0, 0);
    cv::resizeWindow(window_name, 640, 480);

    // マウスコールバック関数設定
    cv::setMouseCallback(window_name, mouse_callback, (void *)NULL);

    // 画像表示
    cv::imshow(window_name, image);

    // キー入力待ち
    int key = cv::waitKey(0);

    cv::destroyWindow(window_name);

    return 0;

}

// マウスコールバック関数
void mouse_callback(int event, int x, int y, int flags, void* data) {
    switch (event){
        case cv::EVENT_MOUSEMOVE:
            // アドレス確保
            MousePointer = cv::Point(x,y);
            break;
        case cv::EVENT_MOUSEWHEEL:
            // getMouseWheelDelta(flags)の返り値
            // 正か負の値が得られる
            // どちらがどの回転方向に対応するかは環境依存っぽい
            printf("[debug] : mouse wheel. x->%d, y->%d, getMouseWheelDelta -> %d\n", MousePointer.x, MousePointer.y, cv::getMouseWheelDelta(flags));
            break;
    }
}

2017年1月1日日曜日

OgakiMiniMakerFaire2016に出展しました

OMMF2016(12/3-4)に"全(技術)力スプラトゥーン"を展示してきました。以下、雑感です。
  • やっぱりスプラトゥーンを知っている人は少ない
  • システムが難しすぎる
  • 子供がシステムそっちのけで遊ぼうとする
試遊できるシステムは体験できるのでとっつきやすいのですが、いかんせんWiiUとスプラトゥーンの認知度が低いので、WiiUのシステムからスプラトゥーンの遊び方まで説明しないといけない。

今年はもうちょっとわかりやすいものを作ろう…。