電子辞書は組み込みLinuxの夢を見るか?

f:id:puhitaku:20200926011101j:plain

はじめに

去る2010年、津山高専の入学を間近に控えた puhitaku 少年は、とある SHARP の電子辞書を手にして震えていた。

SHARP Brain PW-GC610。Windows CE をプリインストールした、中学生から見ても一風変わった電子辞書。そんなものが津山高専の教科書販売で手に入るなんて…。

これは、そんな変わった電子辞書 Brain シリーズで Linux を動かすまでの記録だ。

(本記事は、第53回 情報科学若手の回で発表した以下のスライドを記事にしたものです)

speakerdeck.com

Brain ハックの隆盛と衰退

SHARP の電子辞書 Brain シリーズは、Windows が動くことによって外部アプリ ― 言い換えると、PE 実行可能ファイルそのもの ― が追加可能であることを売りにしている。自作ソフトの防止や対策は特にないので*1、Visual Studio で自作ソフトをビルドしたり、一部のCE向けソフトを追加したりすることで、電子辞書の枠を超えた使いみちを開拓できる。厨房からするとたまらない仕様だ。

2010年には既に、Brain で自作ソフトを動かす界隈が 2ch を中心に大いに盛り上がっていた。Brain で動くアプリ*2は両手では到底足りないくらいの数が出ていて、Newbie な僕でも授業中にいらんことして遊ぶ*3には十分すぎるガジェットになっていた。

f:id:puhitaku:20200925023631j:plain
かつての盟友 PW-GC610 は今も引き出しで眠っている

しかしながら、Windows CE といってもコア以外が削り取られていたり、USB ホストになれなかったり、インターネットに出られなかったりしたことが災いして、2012年を迎える頃にはネタ切れ感が漂い出していた。「自分も一発 Brain アプリを開発してブイブイいわすぞ〜w」と思っているうちに活発な開発者も離れ、Brain スレは急速にゴーストタウンと化していった。

さして技術力もなかった当時の僕には、ただ黙ってそれを見届けるしか選択肢がなかった。

Windows という「障壁」を越える

時は流れて2019年。僕は高専を卒業し、社会人となり、消防からの夢であったコンピューターで飯を食っていた。そんなある日のこと。

「…そういえば、2012年に発売された新機種の Brain は『性能が2倍に上がった』と公式が謳ってたな… あれなんだったんだろ?」

PW-GC610 は、界隈では「第一世代」と呼ばれる最初期のハード。そこから2倍にも性能が上がったのであれば、SoC は間違いなく変わっているはずだ。

調べてみるとやはり SoC のベンダーは変わっていて、その機種を見て思わず笑みがこぼれた。Freescale(現: NXP)i.MX シリーズ。組み込み Linux の生き証人*4とも言える有名 SoC だ。

f:id:puhitaku:20200925024908j:plain:right:w300

組み込み Linux とともに歩んだ歴史から、Freescale (NXP) は i.MX シリーズ向けの Linux ソースツリーを公開しているし、SoC のデータシート、評価ボードの回路図、ビルド方法といった情報がすべて無料で手に入る。

しかも幸いなことに、僕自身も i.MX シリーズが載ったボードに仕事でガッツリ関わっていたので、SoC の動作についてはかなり心得ていた。

10年前には到底持ち得なかった知識が、今ならたくさんある。

もしかすると Linux を移植できるのではないか?Windows という障壁を超え何でも出来るようになるのでは?といてもたってもいられなくなり、思わずメルカリで中古の Brain PW-SH1(第3世代)を購入。届くやいなや分解に取り掛かった。

第一の壁: 回路

Linux ポーティングで最初に立ちはだかる壁は、Linux ではない。回路そのものだ。

基板上のチップの型番を記録して、ヒートガンで部品を剥がして、ピンひとつひとつにテスターを当てて、基板上のどこに繋がっているかを探し回る。気の遠くなるような作業を黙々と進めた*5

f:id:puhitaku:20190713143907j:plain
中央左に並ぶピンが SoC。ほぼすべてのピンに何らかの機能がある。

i.MX シリーズには PSWITCH というピンがあり、ここに電圧を印加しながら電源を入れると USB からブートローダを流し込めるようになる。

程なくして、PSWITCH が eMMC のすぐそこにジャンパとして露出していることを発見し、USB Recovery が発動することも確認した。

まだ壁は乗り越えきっていない。i.MX にはピンの機能を変えられるマルチプレクサ*6があり、ピンの位置だけでは真の機能がわからない。ここは先人の力を借り、Windows CE 上で実メモリをダンプできるソフト*7でレジスタ値を直接読むことにより把握した。

第二の壁: ブートローダ

f:id:puhitaku:20190722222112j:plain:right:w300

次の壁は、まだ Linux ではない。ブートローダだ*8

組み込み Linux でよく使われるブートローダ "U-Boot" は、やはり NXP の fork が GitHub に公開されている。Brain に搭載されているものと同じ i.MX 28 の評価ボードがあるので、それ向けにコンパイルするのが足がかりとなる。

コンパイルはすぐできた。そしてドキドキの実機転送。これも難なく成功!さあシリアルポート*9からの出力は!!?!?

f:id:puhitaku:20200924221526p:plain

「は?」

思わず声が漏れた。高鳴る心臓も急速に静まっていった。

調べたところでは、 "HTLLCLLC" は Boot ROM*10 の出力らしい。 "Undefined Ins" は "Undefined instruction" のことで、原義は未定義命令だが、DRAM が初期化できていない時もこう表示されることがわかった。

さて、こうして次なる壁が現れた。

第三の壁: DRAM

次の壁は、まだまだ Linux ではない。DRAM だ。DRAM は初期化をしてあげないと読み書きすらできない。

i.MX 28 の評価ボードは DDR2 の DRAM を搭載している一方で、Brain は LPDDR を搭載しているので、そのままの実装では DRAM と話せない。初期化コードを手で書き換えるにしても、これまた気の遠くなる話だが、DRAM に関するレジスタは数にして 200 近いので対応が大変だ。Windows が動いている実機からレジスタをダンプするだけでは足りず、それを解析するスクリプトを Python で書き、刻印から機種とデータシートを探し当て、インターネットを訪ね歩き、試行錯誤を重ねていった。

幾度かの徹夜を経て、ついにその時は訪れた。

「ああ!!!よっしゃ!!!よっしゃ!!!」

プログラムが思ったように動いたときの、その何十倍にも思える達成感!Brain で Windows 以外のものが初めて動いた喜びに僕は酔いしれた。

第四の壁: microSD

次の壁は、まだまだまだ Linux ではない。microSD だ。

すごく地味な話だが、U-Boot から microSD を読むのにかなり難儀した。Linux kernel の本体とルートファイルシステム*11は microSD に置くのが自然だし、これから試行錯誤する上では絶対に必要になる。

オシロスコープで信号を見つつ*12、Windows との挙動の比較、microSD の電源を駆動する FET と GPIO の見直し、SoC のクロック設定見直しなどなど…途中で何度も諦めそうになりつつ、それらしい箇所の修正を繰り返した。

そしてやはり、その時はやってきた!

f:id:puhitaku:20200925001147p:plain

mmc dev 1 で microSD を選択し mmc info で情報を表示している。SD High Speed の表示からも eMMC ではなく SD であることがわかる。

かくして、興奮で震える手をなんとかいなしつつ、ようやく見えてきた Linux のコンパイルへと駒を進めた。

第五の壁: Linux kernel と Debian

ようやく、ついにようやく、Linux と戦う瞬間がやってきた。どんな苦労が待ち構えているだろうかと、武者震いする気分だった。

ところがどっこい、Linux のブートはあっさりと実現できた。

  • U-Boot から渡る cmdline
  • Device Tree に書かれたメモリサイズや I/O 設定

これらを直すだけで、kernel log 表示 → rootfs が見つからず panic(想定通り)まで到達できた。

以下のツイートで「deboostrap ポン付け」と言っているように、Linux からの microSD の読み書きも一瞬で実現し、Debian の移植がすぐに達成できた。前職で debootstrap と戯れた経験が大いに活きた。

第六の壁: LCD

もう Linux がブートしてログインシェルが出るところまでは到達できたものの、「やっぱ本体の LCD が映らないことにはなぁ」という気持ちが残り続けていた。そこで、次は画面表示の実現に向けてハックを進めた。

f:id:puhitaku:20200925070646j:plain
この時点では、Linux がブートしても画面が灯りすらしなかった

LCD すら特殊なパーツを使っているのがこの Brain というデバイスで、一般的なものと違い、画面に変更が生じた時だけ信号を送る特殊な規格を使用している*13。この特殊さ故に、Linux に既に入っているドライバは使えないこともすぐに判明した。

LCD はただ映像信号を送ればいいわけではなく、初期設定をしてやる必要がある*14。信号線を全部引き出す道具を作ったのでロジックアナライザで信号は読み取れるが、初期化の信号が何を意味するのか当初はまったくわからなかった。

ドライバが使えず、信号の意味も不明確。かなり厳しい状況だった。

f:id:puhitaku:20200925011340j:plain
LCD の信号を引き出すやつ。メイン基板と LCD フレキの間の基板から取り出している。

f:id:puhitaku:20200925010731p:plain
デコード結果の FF FF 98 05 は 「拡張コマンドセットの有効化」を意味する

ブレイクスルーは突然にやってきた。LCD ドライバの型番を Windows が奇跡的にログへ出力していたのだ。

"ILI" は、SPI 液晶のドライバでメジャーな ILITEK 社の製品を表す。この ILI9805 は、ILI9806 に特定顧客向けのカスタマイズを加えたものと推測される*15。ILI9806 (ILI9806G) のデータシートは普通に手に入るので、さながらロゼッタストーンのように命令列を類推し解読することに成功した。

f:id:puhitaku:20200925183232p:plain
デコード結果(下)の FF FF 98 05 は、データシート(上)の FF FF 98 06 と似ていて、FF FF に続いて型番が16進で入っていると推測できる

そういった努力の結果、まず U-Boot で LCD の初期化と映像伝送を実装できた。簡単そうに書いているが、ほんまめっちゃくそ大変だった。作業してて何度朝を迎えたかわからんくらい。

できた瞬間は友達と作業通話していて、思わず「キタキタキタキタ!来た!やった!!」と興奮してしまった。

そう来れば次は Linux だ。今までのキャリアで得た Linux のグラフィックドライバ、もとい DRM の知識を再結集し、数日後には実装が完了した。フレームバッファがあると、できる事が一気に増える。思いつくソフトを動かしては、喜びを噛み締めた。

次なる壁は?

やるべきことはまだまだ残っている。キーボードのドライバ。音。開閉検知。バッテリ管理。パフォーマンスチューニングと安定性向上。課題は山積しているが、まだ触れたことのない領域を掘るワクワクは途絶える兆しがない。自分の技術力を高める意味でも楽しく続けられるだろうと思っている。

分解なしで Linux をブートするために、Windows から microSD ブートさせるアプリも実現したい。これはリセットを工夫すればできるはずなので*16実現は難しくないだろう。Windows プログラミングはからっきしなので、知識のある協力者がいればより早いかもしれない。分解なしの Linux ブートが実現した暁には、Rasbperry Pi のごとく SD カードのイメージデータにまとめて公開したり、OSのビルド方法について文章化したりしたい。

まとめ

時々、人からこう聞かれることがある。「なんで Linux とか低レイヤーが好きなんですか?」と。この記事を読んでもらえれば(多少引かれるかもしれないが)きっと理由は理解してもらえることだろう。

Linux は、「コンピューターを使っている」という実感を力強く得ることができる素晴らしい趣味だ。10年前の僕が Brain ハックに心を躍らせたように、この記事が世の中の知的好奇心をくすぐり、ハッカーの卵たち、そして読んでくれたあなたを鼓舞できるならとても嬉しい。

*1:2017年以降に発売された機種では、EXEに細工を加えると起動できる・あるいはそのテクニックも塞がれるといったイタチゴッコ状態になっている。

*2:例えば、キーボードをそれなりに使えるようにするソフト、ランチャー、オフラインで Wikipedia を閲覧するソフト、テキストエディタ、matplotlib、ノベルゲーのエンジン、ソフトウェア3D、ピアノ音源、エミュなど。

*3:10年も経てば時効だよね…。

*4:MMU を搭載しない SoC で動く Linux "uCLinux" は、世界で最初に Freescale Dragonball プロセッサへと移植された。i.MX はその Dragonball の直系の子孫。組み込み Linux の歴史について詳しくは弊ブログの翻訳記事を参照。なお、uCLinux は現在は mainline に merge されている。

*5:元々自作ソフトが動くのだから、昔の Windows Embedded のスマホのように Windows から直接 Linux をブートするソフト的手法もあるだろう。しかし周辺回路を知らないことには結局何もすることができないので、チップ剥がしはいずれ必須となる。

*6:IOMUXと呼ばれる。

*7:Nexhawks 氏作の Scalpel 。氏は Brain 開発を引退し別名義で活動している。

*8:プリインストールされているブートローダは Windows 専用なので Linux のブートはできない。

*9:ところでブートローダ以前はIOMUXも初期状態なので、入出力の配置が奇想天外なことになっている。例えばシリアルポートは「SoC に接続されたマイコンの足」から引き出す必要がある。

*10:現代の SoC のブートは多段構造が普通で、Boot ROM は本当に一番最初の段階を指す。

*11:Linux ディストリビューションをなすソフトウェアなどを含むファイルシステムのこと。今回は利便性を優先し Debian 10 を採用。

*12:Sparkfun の Sniffer board が超便利だったのでオススメ。

*13:RGB の信号を水平同期 (HSYNC) と垂直同期 (VSYNC) に合わせてずっと送り続けるパラレル伝送が一番王道で、近年のデバイスではフットプリントの小さい MIPI DSI というシリアルI/Fが一般的。Brain の規格は極めて古典的なバス方式で、MIPI DBI として一応 MIPI には含まれているが、Linux ですら MIPI DBI の実装は存在しない。それくらいマイナーだ。

*14:例えば、伝送規格、ガンマ曲線、リセットの解除、などなど。

*15:ある筋によれば、パブリックな製品の型番から1引いてカスタム品を出すというのはよくある手法らしい。実際、Nintendo Switch の サウンドチップも似じルールで採番されている

*16:Brain を意図的にリセットするツールというのも Nexhawks 氏の手によって実装されているのでそれがベースになるだろう。

Nintendo Switchからデジタル音声を「直接」取り出す。FPGAで!

f:id:puhitaku:20200814153753j:plain

Switchはイヤホン出力の音がひどいことで有名だ*1

ブズズズズ………バババババ……

と熱雑音では明らかに説明できない周辺回路のお気持ちが音となって伝わってくる。

そこでUSB DACを使いたいのだが、どうも手持ちだとハイエンド機に限って動かない*2。別なやり方でデジタル音声を取り出さなければ。

取り出し方は様々ある。

  • ドックのHDMI出力 + S/PDIFスプリッター(TVモード必須)
  • Switch用Bluetoothトランスミッター(ぶっちゃけこれが一番オススメ)
  • Switchが認識できるUSB DDCで同軸デジタルなどにする
  • Raspberry PiのUACガジェット

Switchに直に接続できないDACを使いたい場合は、DDC必須*3でゴテゴテしてしまう。

「それなら本体に流れるデジタル信号(I2S)を直接取り出して、S/PDIFに変換するしかないよね!」と思いついた26の夜。早速やっていこう。なお、「自作が一番ゴテゴテするのでは?」というツッコミは受け付けておりません。

音声出力の解析

iFixitのページにあるように、Switchの中ではスマートアンプ Realtek ALC5639 が使われていて、DAC・ヘッドホン出力・スピーカー出力を一手に引き受けている。I2S(音声) + I2C(制御)でSoCと接続するこういうスマートアンプは組み込みでよく使われる*4

このどこかのピンにI2Sが入力されている!ということで早速プローブを当てる。結果は以下の通り。

f:id:puhitaku:20200814063512j:plain

そしてはんだ付け。

f:id:puhitaku:20200812032335j:plain

下はオシロで波を出してみた図。上からBCLK(ビットクロック)、LRCLK(左右のチャンネルを区別するクロック*5)、SDATA(シリアライズされた音声データ)。

f:id:puhitaku:20200814070540p:plain

S/PDIFへの変換

I2SとS/PDIFは信号の種類が違うので変換しなければならない。信号を変換する装置といえば!?

デケデケデケデケ…デン!!

そう!FPGA!!!(予定調和)

今回はこのSiPeed Tang Nanoという開発ボードを使う。Shigezone店頭価格でなんと800円。意味不明なくらい安い*6

f:id:puhitaku:20200814071346j:plain
画像は公式より拝借。

GOWIN GW1N-1 (LittleBee) が搭載されているので開発はGOWIN EDAで行う*7

FPGA内部では以下のタスクをやらせる。

  • I2Sの信号をデシリアライズして左右合計32bitのフレームを得る
  • フレームをFIFOに入れる*8
  • FIFOからフレームを取り出してS/PDIF TXに入れる
  • ええ感じにTXからS/PDIF信号が出る

前段のデシリアライズは手でSerdesを書いて対応。後段のS/PDIF TXはOpenCoresに実装があったのでこちらを使わせてもらった。

実装はもちろんGitHubに置いておきました。英語でのインストラクション付き。回路だけはんだ付けすればポン付けで動くようにしているので、GitHubのインストラクションやブレッドボードを参考に試してみて欲しい。

github.com

ちなみに面白いことに、Tang Nanoの赤LEDで光デジタルを直接出すことができるため、光デジタル対応DACであれば外付け部品ゼロで音を聴くことができる。最高!

今後

このままだと持ち運べないのでうまいことまとめて外でも使えるようにしたい。3Dプリンターも届いたし。

参考ページ

*1:現行機だと変わってるかもしれないが未確認

*2:UAC 1しか対応してない説が濃厚。ローエンド機はそれで十分だが、ハイエンド機は対応ビットレートが高いので必然的にUAC 2/3を選択することになる。

*3:ところでDDCを好き好んで「「ジッタの減少」」に使ってる人々は果たして【体感】できてるんだろうか?そんな人間に体感できないオーダーのジッタなんてプラセボに比べたら数桁小さいですよ。個人的な感想です。

*4:NVIDIA Jetson TXで同じフットプリントの姉妹チップALC5640が使われているので、ひょっとするとNVIDIAの開発ボードのデザインを任天堂がそのまま採用したんじゃないかな〜とか推測できるのが楽しい。5639のデータシートは見つからないが、5640のデータシートは普通に手に入るため解析にも役に立った。

*5:厳密にはこれはクロックではなくデータ

*6:1万すら切れないAltera DE-0 Nanoとか鼻で笑えるような最高のFPGA学習キットが作れるのではないだろうかと妄想している。顧客が本当に必要としていた「入門機」はこれや!

*7:大手に比べると情報が少なくて最初ちょっと戸惑うが、素朴なぶんめっちゃ軽くて良い開発環境

*8:駆動するクロックが異なるため非同期FIFOを挟んでいる。I2S = 6.144MHz, S/PDIF TX = 6.144MHz * 8 = 49.152MHz。S/PDIFにはvalidフラグがあるので、FIFOのalmost_fullフラグをvalidにつなげることで予期せぬアンダーフローによるノイズを防ぐ意味合いもある。

NikonのカメラをWebカメラにする方法(もちろんマルチOSで)

がなかったので、自分で開発した。公式アプリやSparkoCamとの差についてはこちらを参照。

tl;dr

f:id:puhitaku:20200727221049j:plain
概観

github.com

  • カメラからライブビュー (LV) を取ってきてOBS経由で仮想カメラにするソフト mtplvcap をGoで書いた
  • PCやスマホからカメラのフォーカス等を制御できるリモコンもある
  • libusbとOBSのおかげで Windows/macOS/Linux 全対応!
  • マジめっちゃ動作確認情報を欲してるので、動いた/動かなかった情報を@puhitakuにガンガン送ってほしい(DM可)

最新情報(2020/8/27現在)

Version 1.1.0リリース。

  • D5000に対応(rch850さん、多大なるデバッグ協力ありがとうございます!)

新たに動作が確認されたもの

  • D3200
  • D3300(unasuke さんありがとうございます!)
  • D5000(rch850 さんありがとうございます!)
  • D5300
  • D5500(nasustim さんありがとうございます!)
  • D600(ohtayo さんありがとうございます!)
  • D610(hazlitt さんありがとうございます!)
  • D7000(takashi0314 さんありがとうございます!)
  • D7200(br_spike_love さんありがとうございます!)
  • Z6
  • Z7

Nikon公式やSparkoCamとの比較

Nikon公式のWebカメラ化アプリ「Webcam Utility」(以下、WU)やSparkoCam(以下、SC)との違いは以下の通り。(情報は2020年8月8日現在)

mtplvcap WU SC
OS Win, Mac, Linux Win(MacはTBA) Win
課金 Free Free Paid
PCからの撮影設定 Yes No Yes
機種 D3200, D3300, D5300, D5500, D600, D610, D7000, D7200, Z6 他随時追加中*1 D6, D850, D780, D500, D7500, D5600, Z5, Z6, Z7, Z50 D3, D3s, D3X, D300, D300s, D4, D4s, D5, D500, D5000, D5100, D5200, D5300, D5500, D5600, D6, D600, D610, D700, D750, D780, D7000, D7100, D7200, D7500, D800, D810, D810A, D800E, D850, D90, Df, 1 V3, Z6, Z7, Z50

Webcam Utility の動作分析とお気持ちは以下。

使い方

日本語のREADMEを書いておいたのでそれを参照してください。

経緯

昨今のアレにより手持ちの一眼をWebカメラにするのが流行っている。CanonのWebカメラソフトはBetaながらMacとWindowsに対応してきたし、SIGMA fpはなんとUVCに対応している。 Sonyも2020年8月現在Windowsのみながら公式対応を発表した*2。しかしどうもそれ以外のベンダーは対応がしょっぱい。Nikon*3ユーザーな僕は、家で寂しく指をくわえるしかなかった。

4月以降はインカメが最高なPixel 3で快適にビデオ通話できてたし、一眼をわざわざ使う理由は正直ないのだが、

やっぱり…一眼で撮れたら嬉しいよね…。

ということで、開発に踏み切った。

日頃はほぼMac + ヘッドレスなLinuxの構成なので、Macで写したい。既存手法で出ているv002 Camera Live謎の海外ソフト頑張る方法だと、Macのみ・Windowsのみの対応となり汎用性がない。ソフトでライブビューを表示してそのウインドウをキャプチャするなんていう記事もあるけど、ゲーム配信じゃないんだし…とてもエレガントとはいえない。

ではどうやって画を取ってきて仮想カメラに流し込むか?その算段はついている。カメラからMTPで画を取り出し、WebSocketで配信、それをOBSのBrowserSource(OBSにエンベッドされたChromiumの画面をソースにできるやつ)で表示してOBSの仮想カメラに流すというものだ。

MTPとはMedia Transfer Protocolの略で、デバイスの制御とファイルのやり取りを網羅しているプロトコルだ。ベネッセ個人情報流出事件で用いられたテクニックはMTPと言えば思い出す人もいるかもしれない。USB Mass Storageとは異なるのでWindows以外では扱いにくくよく厄介者扱いされるが、機能的にはMass Storageよりも豊富なため携帯プレーヤーなどではデファクトスタンダードとなっている。

開発

要件は以下のように決めた。

  • Windows/macOS/Linux全対応
    • 昨今のカメラベンダーの対応に業を煮やしていたので
  • Goで書く
    • cgoが
    • リンクが楽
    • ランタイムがいらない
  • USB部分と仮想カメラ部分はOS間で差異があるためlibusbとOBSに任せる

色々探していると、これまたドンピシャなOSS go-mtpfs を発見した。AndroidなどのMTPデバイスとlibusb経由で低レベル通信をしつつ、FUSEを使ってマウントするものだ。

github.com

USBの低レベル通信とWebサーバーによる配信を1プロセスでやるにあたって、Go + libusbで書きたいと思っていたので、まさに願ったり叶ったり。macOSでデバイスの初期化が上手くいかない問題はあったもののGoLandのデバッガーのお陰でスムーズに解決し、forkした。

まず取り掛かったのはライブビューの開始。MTPではuint16の命令番号的なのを送りつけることで動作を命令することができる。例えば、ライブビューの開始は0x9201が対応している。これを送りつけたり応答をパースしたりする部分はgo-mtpfsの力を借りる。

次に取り掛かったのはライブビューの画の取得。ここでちょっと心拍数が上がってくる。これもまたライブビューの開始と同様に 0x9203 を送りつけるとキャプチャできる。

この画像をOBSのBrowserSourceで表示するには、フロントエンドを用意して、画像シーケンスを配信する必要がある。これはWebSocketでサクッと解決できた。

そしてそして、お待ちかねのOBS + Zoomの結合も難なく成功!やったー!

ひとまずのマイルストーンを達成したので、LinuxとWindowsでも動作確認。結論から言うと、WindowsのWSL 1では動作しなかったが、MinGW (MSYS2) では楽勝で動かすことが出来た。やはり年の功というか、歴史が長いだけあってのことか。MinGWだとネイティブのexeが出てきてくれるので、環境を用意する障壁がWSLよりも圧倒的に低い。これは超嬉しかった。

この後、紆余曲折を挟みつつも*4、ISOや絞りも変えることができた。

画像が安定して撮れるようになったところで、リモコンの実装に着手した。

リモコンはBootstrap + jQueryで書いた古式ゆかしいフロントエンド*5で、絞り・ISO・オートフォーカス(一定間隔でAFさせるのも設定可)・フレームレート制限を設定できる。カメラのダイヤルでモードをAUTOにすれば自動で輝度が調整されるので、それで十分であれば絞りやISOを手で変更する必要はない*6

f:id:puhitaku:20200727234015p:plain
リモコン on Mac

./mtplvcap -host 0.0.0.0 という風に外からのアクセスもListenするようにすれば、スマホからでも操作できる。

f:id:puhitaku:20200727234042j:plain
リモコン on Android

まとめ

まだ動作確認がとれている機種がD5300とD3300しかないので、ぜひお手元のNikonカメラで動作を確認してみて欲しい。もちろん一眼でなくても動作するはずだ。

なお、MTPで通信をするカメラであれば、原理上他メーカーでも対応が可能なはずだ。やり方としては、USBのVendor IDとProduct IDを見て動作を振り分ける方法を想定している。今はまだこの仕組みはないが、もしMTPで通信すると判明しているカメラがあって、私に貸していただけるならば実装を引き受けることも可能かもしれない。

*1:現在動作確認ができているもののみを挙げている

*2:対応機種も幅広くて好感が持てる

*3:当初は非公式かつWinのみの仮想カメラソフトを公式が紹介する始末で、最近やっとWindows対応・macOS公開時期未定なWebカメラソフトを公開した。

*4:Arrayなどを内含できるDevice Propertyなる可変な構造体があり、例えばISO値はこれで送られてくる。Goは可変な構造体は扱いにくくたいていreflect祭りになる。この割と複雑なエンコード・デコード処理が実装されておらず、go-mtpfsのコードを手で紐解く必要があった。

*5:いわゆるモダンさはないけど、これでいい。当然node.jsなどに触れることもないので、超シンプルになってくれるのが最高。

*6:現在はカメラの動作モードを見に行く処理がないので、AUTOモードで絞りやISOを変更しようとするとAccessDeniedが返ってくることに注意