TinyPilot: Month 2

Michael Lynch

TinyPilot:2か月目

原文は Michael Lynch により に公開されました。 このブログを購読する

一言でまとめると

TinyPilotで大きな想定外のトラブルに見舞われましたが、前進は続いています。

ハイライト

  • 設計上の問題に対応するため、TinyPilotの販売を一時停止しました。
  • TinyPilot用のカスタムUSB電源コネクタを製造しています。
  • すべてのプロジェクトを合わせた収益は過去最高水準に近く、合計3,600ドルでした。

目標の達成度

毎月のはじめに達成したい目標を宣言しています。結果は以下のとおりです。

TinyPilotキットを30台販売する

  • 結果: 16台販売
  • 評価: C

目標達成のペースで順調に進んでいましたが、歯車に異物が挟まるようなトラブルが起き、販売を停止せざるを得ませんでした。

新しいマーケティングチャネルを3つ試す

  • 結果: どのチャネルも試さず
  • 評価: F

上記と同じ理由で、再び販売できる製品が用意できるまでマーケティングの取り組みは延期しました。

TinyPilotでマウス統合をサポートする

思ったよりも難しい作業になりましたが、月末ぎりぎりで機能を完成させました。マウスを統合したことで、想像以上にユーザー体験が向上しました。

TinyPilot

指標2020年7月2020年8月変化
ユニーク訪問者数4,9302,284-2,646 (-54%)
総ページビュー10,4276,136-4,291 (-41%)
販売収益$8,741.37$3,030.74-$5,710.63 (-65%)
寄付N/A$94.06+$94.06 (+inf%)
総収益$8,741.37$3,124.80-$5,616.57 (-64%)

先月のHacker Newsからの大きな流入に比べるとアクセス数は落ちましたが、見込み顧客が安定して訪れてくれているのは嬉しいことです。

販売目標に届かなかったのは、在庫を十分に確保できなかったこともありますが、大きな理由は月の中旬に販売を停止せざるを得なかったことです。

嬉しいサプライズだったのは寄付です。プロジェクトを支援したいという人々から100ドル近くを受け取りました。中にはボットと思われるものからの寄付もありました。

なぜ、Yケーブルなのか!

TinyPilotのごく初期の段階から、ずっと一つの大きな問題に悩まされてきました。電源です。

Raspberry Piには、他のUSBデバイスになりすます特殊な機能があります。TinyPilotがターゲットのコンピュータにキーストロークを入力できるのはこの機能のおかげです。コンピュータに対して自分はUSBキーボードであると伝え、実際のUSBキーボードと同じようにキー入力を送信します。

問題は、キーボードになりすませる唯一のポートが、同時に電源供給を受けるためのメインポートでもあることです。コンピュータのUSBポートからも多少の電力は供給されますが、Piの公式要件である3.0アンペアを満たすには足りません。初期バージョンのTinyPilotは0.5アンペアで動作しており、一応動くには動きましたが、電力不足で予期せぬ不具合が起きるのではないかと常に不安で、何とかより良い解決策を見つけたいと思っていました。

そしてついに、まさに求めていたものに思えるUSB OTG Yケーブルを見つけました。

Yケーブルの写真

求めていたものに思えたUSB OTG Yケーブル

さっそく一つ購入すると、うまく動作しました!Raspberry Piへの接続を分岐させ、電源とターゲットコンピュータの両方に同時に接続できるようになったのです。在庫すべてをこのケーブルを統合したTinyPilot v2へ切り替えました。6台販売し、新バージョンの宣伝を始めたところ、ある人から「そのケーブルは電源の逆流を防げますか?」と連絡がありました。

電源の逆流?そんな問題が起こり得ることすら知りませんでした。

調べてみると、このYケーブルは別々の電源を接続することを想定していませんでした。理論上は、外部電源もコンピュータのUSBポートも5ボルトを出力します。しかし実際には、両方がぴったり5Vを出力するという保証はありません。USBの電源仕様では4.4〜5.25Vの範囲が許容されており、もしコンピュータ側の出力が4.5Vまで下がれば、外部電源から電流がコンピュータのUSBポートへ逆流し、ポートに過負荷をかけて永久に損傷させる恐れがあるのです。

この危険性を指摘されてすぐに販売を停止し、電気設計会社に調査を依頼しました。リスクが現実のものであることが確認されたため、顧客に連絡を取り、解決策が見つかるまで外部電源の接続を外すようお願いしました。幸い、TinyPilotは外部電源なしでも動作します。多少不便にはなりますが。

ゼロから製造までたった2週間でできるなんて?

この1か月で最も驚いた発見の一つは、製造がいかに速く、安価になったかということです。

ちょうど1週間前の8月27日に、TinyPilotの電気設計パートナーに電源問題を解決するためのコネクタの設計を依頼しました。設計は翌日に完成し、すぐに100枚の基板への実装を発注しました。基板は今週末に届く予定で、テストと組み立てにはさらに数日しかかからない見込みです。

同時に、基板用のケースについて3Dプリントのデザイン会社とも作業を進めています。先方は2日でデザインを完成させ、現在最初の3つの試作品をプリント中です。本格的に稼働すれば、1日あたり50個のケースを3Dプリントできる能力があります。

TinyPilotのブログ投稿が1位に表示されたスクリーンショットReddit上のTinyPilot投稿のスクリーンショット

驚くほどの速さで形になりつつあるTinyPilot用電源コネクタ

すべてが順調に進めば、ケースと基板は早ければ来週にも顧客に提供できるようになります。ゼロの状態から完成した物理的な製品まで、わずか2週間で到達することになります。

設計費、部品代、人件費を含めても、今回の製造コストは1台あたり約13ドルになる見込みです。こんなシンプルなプロジェクトでも、ここまで短期間かつ低コストでできるとは思ってもいませんでした。もちろん、すべてがうまくいけば、という前提ですが。

HID記述子は悪魔だ

前述のとおり、TinyPilotはターゲットのコンピュータに対してUSBキーボードとして自身を認識させる必要があります。そのためにUSB接続越しにhuman interface device(HID)記述子と呼ばれるものを送信します。キーボードやマウス、USBメモリなどのUSBデバイスは、自分が何ができるかを宣言するHID記述子を持っています。

HID記述子は、次のようなバイナリの塊です。

// HID descriptor for a keyboard
// Source: https://www.kernel.org/doc/html/latest/usb/gadget_hid.html
static struct hidg_func_descriptor my_hid_data = {
      .subclass               = 0, /* No subclass */
      .protocol               = 1, /* Keyboard */
      .report_length          = 8,
      .report_desc_length     = 63,
      .report_desc            = {
              0x05, 0x01,     /* USAGE_PAGE (Generic Desktop)           */
              0x09, 0x06,     /* USAGE (Keyboard)                       */
              0xa1, 0x01,     /* COLLECTION (Application)               */
              0x05, 0x07,     /*   USAGE_PAGE (Keyboard)                */
              0x19, 0xe0,     /*   USAGE_MINIMUM (Keyboard LeftControl) */
              0x29, 0xe7,     /*   USAGE_MAXIMUM (Keyboard Right GUI)   */
              0x15, 0x00,     /*   LOGICAL_MINIMUM (0)                  */
              0x25, 0x01,     /*   LOGICAL_MAXIMUM (1)                  */
              0x75, 0x01,     /*   REPORT_SIZE (1)                      */
              0x95, 0x08,     /*   REPORT_COUNT (8)                     */
              0x81, 0x02,     /*   INPUT (Data,Var,Abs)                 */
              0x95, 0x01,     /*   REPORT_COUNT (1)                     */
              0x75, 0x08,     /*   REPORT_SIZE (8)                      */
              0x81, 0x03,     /*   INPUT (Cnst,Var,Abs)                 */
              0x95, 0x05,     /*   REPORT_COUNT (5)                     */
              0x75, 0x01,     /*   REPORT_SIZE (1)                      */
              0x05, 0x08,     /*   USAGE_PAGE (LEDs)                    */
              0x19, 0x01,     /*   USAGE_MINIMUM (Num Lock)             */
              0x29, 0x05,     /*   USAGE_MAXIMUM (Kana)                 */
              0x91, 0x02,     /*   OUTPUT (Data,Var,Abs)                */
              0x95, 0x01,     /*   REPORT_COUNT (1)                     */
              0x75, 0x03,     /*   REPORT_SIZE (3)                      */
              0x91, 0x03,     /*   OUTPUT (Cnst,Var,Abs)                */
              0x95, 0x06,     /*   REPORT_COUNT (6)                     */
              0x75, 0x08,     /*   REPORT_SIZE (8)                      */
              0x15, 0x00,     /*   LOGICAL_MINIMUM (0)                  */
              0x25, 0x65,     /*   LOGICAL_MAXIMUM (101)                */
              0x05, 0x07,     /*   USAGE_PAGE (Keyboard)                */
              0x19, 0x00,     /*   USAGE_MINIMUM (Reserved)             */
              0x29, 0x65,     /*   USAGE_MAXIMUM (Keyboard Application) */
              0x81, 0x00,     /*   INPUT (Data,Ary,Abs)                 */
              0xc0            /* END_COLLECTION                         */
      }
};

キーボード用のHID記述子を作るのは朝飯前でした。Pythonでキーボードを偽装する実装例はたくさんあり、プロセスも十分に文書化されていました。

マウスを偽装する実装はずっと難しく、HID記述子の仕組みについてさらに学ぶ必要がありました。マウスはボタンの数やスクロールホイールの数、位置決めの方式(絶対座標 vs. 相対座標)などバリエーションがはるかに多いのです。デバッグが厄介なのは、記述子は動くか動かないかのどちらかしかないことです。無効な記述子を生成しても、何が間違っているのかフィードバックを得る手段がありません。最悪なのは、記述子を試すたびにRaspberry Piを再起動しなければならないことです。

基本的なマウス機能を動かすまでに、5日間の地道な作業がかかりました。私にとって鍵となったのは、ツール整備に集中することでした。最初は、次のように巨大で構造化されていない塊として記述子を扱っていました。

echo -ne \x05\x01\x09\x02\xA1\x01\x05\x09\x19\x01\x29\x08\x15\x00\x25\x01\x95\x08\x75\x01\x81\x02\x05\x01\x09\x30\x09\x31\x16\x00\x00\x26\xFF\x7F\x75\x10\x95\x02\x81\x02\xC0 > "${MOUSE_FUNCTIONS_DIR}/report_desc"

これでは何か修正するたびに一からやり直さなければならず、記述子について考えるのが困難でした。そこで、さまざまな形式のHID記述子のサンプルを取り込み、ファイルを生成するための同等のシェルコマンドに変換できる簡単なJavaScriptアプリを作りました。

自作のHIDフォーマッターツールのスクリーンショット

HID記述子をフォーマットするために私が作った簡単なJavaScriptアプリ

次に、ホームディレクトリに小さなユーティリティスクリプトを書きました。普段ならわざわざファイルにするほどの価値もない、ごく単純なスクリプトです。

#!/bin/bash

set -x

sudo journalctl -u init-usb-gadget

こうした単純なツールは2つの面で役立ちました。1つ目は、何日も壁に頭を打ち付けているように感じていたときに、達成感を与えてくれたことです。HID記述子はうまく動かなくても、少なくとも思いどおりに動く何らかのコードは生み出せていました。2つ目は、認知的な負荷を減らし、目の前の問題に集中するための精神的な余裕を生み出してくれたことです。systemdのログを表示する構文を思い出す代わりに、~/show-systemd-logと打つだけで済むようになりました。

結局、私の問題のほとんどはHID記述子自体ではなく、それを作成するために使っていたシェルコマンドにありました。他の面倒な作業を頭の中から片付けたことで、ディスク上のファイルが期待どおりになっているか確認すべきだと気づきました。実際にはなっていませんでした。そのことに気づいてから、動作するマウス記述子はすぐに完成しました。

TinyPilotを使ってリモートのノートパソコン上でマウスとキーボードの操作をシミュレートしている様子

既存プロジェクト

現在も維持しているものの、開発の主軸ではないプロジェクトについて、簡単に近況を報告します。

Is It Keto

指標2020年7月2020年8月変化
ユニーク訪問者数48,23149,981+1,750 (+4%)
総ページビュー118,980125,599+6,619 (+6%)
ドメインレーティング (Ahrefs)8.09.0+1.0 (+12%)
AdSense収益$208.86$202.46-$6.40 (-3%)
AdThrive収益N/A$35.00N/A
Amazonアフィリエイト収益$134.45$129.88-$4.57 (-3%)
その他のアフィリエイト収益$26.60$118.88+$92.28 (+347%)
総収益$369.91$486.22+$116.31 (+31%)

Is It Ketoは今月わずかに成長しましたが、TinyPilotの方がROIがはるかに高いため、そちらにすべての注意を向けようとしています。

Is It Ketoで特筆すべき出来事は、ディスプレイ広告をAdSenseからAdThriveに切り替えたことです。苛立たしいことに、この移行は予想以上に多くの手間を要しました。オンボーディングの過程では、フォームへの記入を求められては1週間待ち、また別のフォームへの記入を求められるといった細かなステップが延々と続きました。

ようやくサイトをAdThrive広告に切り替える段階まで来たところ、彼らのJavaScriptスニペットがシングルページアプリでは動作せず、VueベースのIs It Ketoのコードで不具合を起こすことが分かりました。顧客の多くがWordPressサイトなのは理解できますが……ちょっと待ってよ、という感じです。2020年にもなってSPAがそんな想定外の事態になるなんて。

そこからまた新たな厄介事の連続でした。AdThriveは私のサイトとうまく連携させるための場当たり的で壊れたJavaScriptを次々と送ってきて、私が実行して「動きません」と報告すると、また別の箇所が壊れた新しいスニペットを送ってくるのです。

最終的には、コードを向こう側でホストしてもらい、私をデバッグのループから外すよう説得しました。十分なテストがなされていないように感じられる状態で本番環境にプッシュしているのはあまり気に入りませんが、今はそれを気にしている余裕がありません。

Zestful

指標2020年7月2020年8月変化
ユニーク訪問者数440324-116 (-26%)
総ページビュー1,247841-406 (-33%)
RapidAPI収益$18.05$9.36-$8.69 (-48%)
総収益$18.05$9.36-$8.69 (-48%)

Zestfulは相変わらず静かですが、新しいAPIマーケットプレイスを評価中です。現在のプラットフォームであるRapidAPIに代わるものをずっと切望していました。Servernopeという新しい会社が自社のAPIプラットフォームに招待してくれたので、セットアップする時間はないが、私の代わりにZestfulの掲載を作ってくれるなら歓迎すると伝えました。そして彼らはそうしてくれました

まだ完全には乗り気ではありません。RapidAPIで最も不満な点の一つは、アナリティクスがデータを有用な形で提示してくれないことです。Servernopeも同様の問題を抱えているように見えますが、まだServernope経由の有料ユーザーがいないので比較が難しいところです。

まとめ

何を成し遂げたか?

  • TinyPilotの電源問題を調査し、修正用のコンポーネントの製造を開始しました。
  • TinyPilotにマウスサポートを追加しました。
  • 在庫管理と一部のリサーチ業務を引き継ぐフリーランスを雇いました。
  • 海外向けにTinyPilotを販売するため、eBayに出品を設定しました。
    • 最終的にはすべてShopifyで対応する方法を模索中ですが、当面の手軽な解決策としてeBayを利用しています。
  • 新しいブログ記事「How I Collected a Debt from an Unscrupulous Merchant」を公開しました。
    • さらに2本の書評も公開しました。

学んだこと

  • もっと早い段階で電気工学の専門家と連携する。
    • 振り返ると、Raspberry Piの主流的な使い方からかなり外れたことをしていたのだから、計画をレビューしてもらうためにプロの電気エンジニアに相談すべきでした。
  • 難しい問題で行き詰まったら、デバッグ作業を減らすツールを作る。
    • ツールを作ることで前進している感覚が得られ、問題の本質に集中するために頭を解放できます。

来月の目標

  • TinyPilotキットと電源コネクタを60台販売する。
  • 新しいマーケティングチャネルを3つ試す。
  • ITプロフェッショナル7人に、仕事でTinyPilotを使うかどうかインタビューする。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント