Skip to content

Instantly share code, notes, and snippets.

@MSakamaki
Last active April 21, 2023 14:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MSakamaki/c998b2e27a97411deb90dc8db6646ec6 to your computer and use it in GitHub Desktop.
Save MSakamaki/c998b2e27a97411deb90dc8db6646ec6 to your computer and use it in GitHub Desktop.
advent calendar

JavaScript Robotics Advent Calendar 2016 13日目

WebGPIOのPolyfillを実装してみて

こんにちは、 JavaScript Robotics Advent Calendar 2016 13日目の記事です。

趣味でWebGPIO/I2Cのpolyfillを実装し、実際にいろいろな人々に使ってもらいフィードバックを受けての感想と今後についてつらつらと書きたいと思います。

WebGPIO WebI2Cってなぁに?

W3CのBrowsers and Robotics Community Groupグループで仕様策定中のセンサーや物理デバイスをJavaScriptから制御するためのAPIです。

実際に遊んでみる場合現時点ではSwitch Scienceさんで販売されているEchigo Rev.1を購入する必要があります。

で、こちらのWebGPIOがどのような仕組みで動いているのかと言うのをざっくりと図にすると以下のような関係をもっています。

  • アプリ
    • その名の通りアプリケーションです。開発者の方はWebGPIO/WebI2Cを利用してハードウェアを制御するアプリを書きます。
  • webGPIO(polyfill)
    • W3C提案中の標準仕様に基づいたインタフェースを持つAPIのpolyfillです。
  • mozGPIO
    • ブラウザ上でとりあえずハードウェア制御を行うために作られた仮APIです。
  • CHIRIMEN
    • CHIRIMEN Open Hardwareコミュニティーで作られたWeb技術のみでセンサーを動作させるためのデバイスです。
  • センサー
    • 開発者が制御したいハードウェアセンサーのことです。

今回お話に上がるのは、この図にある「webGPIO(polyfill)」にあたる部分です。

今まで出てきた様々な問題を振り返る(WebGPIOについて)

今までソフトウェアを中心に開発してきた私ですが、今回WebGPIO/I2Cのpolyfillを書いて、様々な問題に直面しました。

複数デバイス制御の問題

CHIRIMENボードには18 x2 の36pinのポートはGPIOが14、I2C2つになっています。 (詳しくはこちら)

I2CやGPIOポート制御にはまずポートの開放(exported)という手順が必要なのですが、現在はアプリケーションの読み込み時に全ポートの開放を行っています。

このときに全ポートのexportが上手くいかないと言う問題がありました。

原因は少し考えれば分かる内容でしたが「polyfill側のexportがノンブロッキングになっていた」というお話。

polyfill側からはexportを並列で開放を指示するのですが、mozGPIO <--> CHIRIMEN間では上手く開放ができておらず一部のGPIOが開放フラグが立っているにもかかわらず制御不可能という事象が発生していました。

ここで問題に気づかなかった原因として mozGPIO <--> CHIRIMEN のexport処理で正常にexportが終了しない場合でも例外が発生しないために発見が遅れてしまったという経緯があります。

ライブラリやフレームワークに依存したソフトウェア開発をしていると気づきにくいのですが、外部リソース(ネットワークやその先のリソース)と連携する場合には handshakingのように接続後その先で正常に意図したことが行われているかの確認手順を設ける必要があると実感しました。

現在はその点を改善し、同期的にportを開放する処理を行っています(起動時間がかかるという問題が残りましたが・・・)

ソフトウェア・ハードウェア例外の切り分け

上にもありましたが、もう一つの問題としてソフトウェア+ハードウェア開発を行っていると「例外の切り分け」と言うものがとてつもなく大変で経験が物を言う作業になっていると言う問題が残っています。

これは、現時点で「ソフトウェアもハードウェアも両方共動作するものを作ってから」でないと動作確認ができないということが問題です。

ソフトウェアの開発に例えると、今時はいきなり本番機にのせないとアプリケーションの動作確認ができないとうことはなく、モックや検証環境などのスタブやドライバなどを用いで小さく開発していくことが容易になっています。

しかし、現状のCHIRIMENではセンサーを扱ったアプリを作った場合ソフトウェアだけ物理デバイスだけの構築というのが不可能なため両方を一気に作る必要があり、 動作しなかった場合そのどちらに問題があるのかを経験からしか切り分けできないという問題があります。

こちらの問題を解決する手法として、個人的に今取り組んでいるのが以下2つです。

  • CHIRIMENエミュレーター
  • CHIRIMEN port制御Tools

CHIRIMENエミュレーター

CHIRIMENエミュレーターは名前の通りCHIRIMENを持っていなくてもある程度のセンサーエミュレーションをしてくれるライブラリです。 こちらができれば、センサーを組まなくともソフトウェアだけを先に書き、動作確認を取ることが可能になるとともに実際にpin出力したデータのロギングまでを可能にします。

CHIRIMEN port制御Tools

こちらはCHIRIMENエミュレーターとは逆のアプローチで、pinから出力する電気信号操作に特化したdevToolsを作ることでアプリを作る前からある程度のハードウェア構築を可能にします。

最後に

単純な電気信号の1/0を操作するWebGPIOですらこのように四苦八苦しています、WebI2Cによるシリアル通信についてもまだ色々な問題が残っている状況ですが、コミュニティーのみなさんの活動により改善や提案がなされて着実によくなっていっています。 私個人エンジニアとしては問題解決は好きな部類なので、コミュニティーメンバーとして今後ともWebGPIO/WebI2Cの問題を色々と解決するのに力になればと思っています。

Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
<svg id="mermaidChart0" width="100%" xmlns="http://www.w3.org/2000/svg" height="100%" style="max-width:1250px;" viewBox="-50 -10 1250 511"><style type="text/css" title="mermaid-svg-internal-css">/* */
*, ::before, ::after { box-sizing: inherit;}
.actor { stroke: rgb(204, 204, 255); fill: rgb(226, 236, 255);}
text.actor { fill: black; stroke: none; font-size: 12px;}
.actor-line { stroke: grey;}
.messageLine0 { stroke-width: 1.5; stroke: black;}
#arrowhead { fill: black;}
.messageText { fill: black; stroke: none; font-size: 12px;}
.labelBox { stroke: rgb(204, 204, 255); fill: rgb(236, 236, 255);}
.labelText { fill: black; stroke: none;}
.loopText { fill: black; stroke: none;}
.loopLine { stroke-width: 2; stroke: rgb(204, 204, 255);}
.actor { stroke: rgb(204, 204, 255); fill: rgb(236, 236, 255);}
text.actor { fill: black; stroke: none;}
.actor-line { stroke: grey;}
.messageLine0 { stroke-width: 1.5; stroke: rgb(51, 51, 51);}
#arrowhead { fill: rgb(51, 51, 51);}
#crosshead path { fill: rgb(51, 51, 51) !important; stroke: rgb(51, 51, 51) !important;}
.messageText { fill: rgb(51, 51, 51); stroke: none;}
.labelBox { stroke: rgb(204, 204, 255); fill: rgb(236, 236, 255);}
.labelText { fill: black; stroke: none;}
.loopText { fill: black; stroke: none;}
.loopLine { stroke-width: 2; stroke: rgb(204, 204, 255);}
text { font-family: "trebuchet ms", verdana, arial; font-size: 14px;}
/* */
</style><g></g><g><line id="actor0" x1="75" y1="5" x2="75" y2="500" class="actor-line" stroke-width="0.5px" stroke="#999"></line><rect x="0" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="75" y="37.5" class="actor" style="text-anchor: middle;">requestGPIOAccess</text></g><g><line id="actor1" x1="275" y1="5" x2="275" y2="500" class="actor-line" stroke-width="0.5px" stroke="#999"></line><rect x="200" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="275" y="37.5" class="actor" style="text-anchor: middle;">GPIOAccess</text></g><g><line id="actor2" x1="475" y1="5" x2="475" y2="500" class="actor-line" stroke-width="0.5px" stroke="#999"></line><rect x="400" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="475" y="37.5" class="actor" style="text-anchor: middle;">GPIOPort.export</text></g><g><line id="actor3" x1="675" y1="5" x2="675" y2="500" class="actor-line" stroke-width="0.5px" stroke="#999"></line><rect x="600" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="675" y="37.5" class="actor" style="text-anchor: middle;">Worker.postMessage</text></g><g><line id="actor4" x1="875" y1="5" x2="875" y2="500" class="actor-line" stroke-width="0.5px" stroke="#999"></line><rect x="800" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="875" y="37.5" class="actor" style="text-anchor: middle;">Worker.onmessage</text></g><g><line id="actor5" x1="1075" y1="5" x2="1075" y2="500" class="actor-line" stroke-width="0.5px" stroke="#999"></line><rect x="1000" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="1075" y="37.5" class="actor" style="text-anchor: middle;">mozGpio.export</text></g><defs><marker id="arrowhead" refX="5" refY="2" markerWidth="6" markerHeight="4" orient="auto"><path d="M 0,0 V 4 L6,2 Z"></path></marker></defs><defs><marker id="crosshead" markerWidth="15" markerHeight="8" orient="auto" refX="16" refY="4"><path fill="black" stroke="#000000" stroke-width="1px" d="M 9,2 V 6 L16,4 Z" style="stroke-dasharray: 0, 0;"></path><path fill="none" stroke="#000000" stroke-width="1px" d="M 0,1 L 6,7 M 6,1 L 0,7" style="stroke-dasharray: 0, 0;"></path></marker></defs><g><text x="175" y="93" class="messageText" style="text-anchor: middle;">初期化</text><line x1="75" y1="100" x2="275" y2="100" class="messageLine0" stroke-width="2" stroke="black" marker-end="url(#arrowhead)" style="fill: none;"></line></g><g><text x="375" y="128" class="messageText" style="text-anchor: middle;">内部でCHIRIMENにあるすべてのGPIOPortのexportを行う。</text><line x1="275" y1="135" x2="475" y2="135" class="messageLine0" stroke-width="2" stroke="black" marker-end="url(#arrowhead)" style="fill: none;"></line></g><g><text x="575" y="188" class="messageText" style="text-anchor: middle;">Response</text><line x1="475" y1="195" x2="675" y2="195" class="messageLine0" stroke-width="2" stroke="black" marker-end="url(#arrowhead)" style="fill: none;"></line></g><g><text x="775" y="223" class="messageText" style="text-anchor: middle;">workerへ開放指示</text><line x1="675" y1="230" x2="875" y2="230" class="messageLine0" stroke-width="2" stroke="black" marker-end="url(#arrowhead)" style="fill: none;"></line></g><g><text x="975" y="258" class="messageText" style="text-anchor: middle;">mozGpio.exportを実行</text><line x1="875" y1="265" x2="1075" y2="265" class="messageLine0" stroke-width="2" stroke="black" marker-end="url(#arrowhead)" style="fill: none;"></line></g><g><text x="775" y="293" class="messageText" style="text-anchor: middle;">mozGpio.exportの完了を通知</text><line x1="875" y1="300" x2="675" y2="300" class="messageLine0" stroke-width="2" stroke="black" marker-end="url(#arrowhead)" style="fill: none;"></line></g><g><text x="575" y="328" class="messageText" style="text-anchor: middle;">完了通知を受け取った際に、次のportのexport指示を行う。</text><line x1="675" y1="335" x2="475" y2="335" class="messageLine0" stroke-width="2" stroke="black" marker-end="url(#arrowhead)" style="fill: none;"></line></g><g><line x1="465" y1="145" x2="1085" y2="145" stroke-width="2" stroke="#526e52" class="loopLine"></line><line x1="1085" y1="145" x2="1085" y2="345" stroke-width="2" stroke="#526e52" class="loopLine"></line><line x1="465" y1="345" x2="1085" y2="345" stroke-width="2" stroke="#526e52" class="loopLine"></line><line x1="465" y1="145" x2="465" y2="345" stroke-width="2" stroke="#526e52" class="loopLine"></line><rect x="465" y="145" fill="#526e52" stroke="none" width="50" height="20" rx="0" ry="0" class="labelBox"></rect><text x="472.5" y="160" fill="white" class="labelText"><tspan x="472.5">loop</tspan></text><text x="775" y="160" fill="black" class="loopText" style="text-anchor: middle;"><tspan x="775">[ Promise.chainにより直列に一つずつexportしていく ]</tspan></text></g><g><text x="375" y="373" class="messageText" style="text-anchor: middle;">exportの全完了を通知</text><line x1="475" y1="380" x2="275" y2="380" class="messageLine0" stroke-width="2" stroke="black" marker-end="url(#arrowhead)" style="fill: none;"></line></g><g><text x="175" y="408" class="messageText" style="text-anchor: middle;">全てのexport開放を完了させたGPIOAccessオブジェクトをPromiseにて返却する。</text><line x1="275" y1="415" x2="75" y2="415" class="messageLine0" stroke-width="2" stroke="black" marker-end="url(#arrowhead)" style="fill: none;"></line></g><g><rect x="0" y="435" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="75" y="472.5" class="actor" style="text-anchor: middle;">requestGPIOAccess</text></g><g><rect x="200" y="435" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="275" y="472.5" class="actor" style="text-anchor: middle;">GPIOAccess</text></g><g><rect x="400" y="435" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="475" y="472.5" class="actor" style="text-anchor: middle;">GPIOPort.export</text></g><g><rect x="600" y="435" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="675" y="472.5" class="actor" style="text-anchor: middle;">Worker.postMessage</text></g><g><rect x="800" y="435" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="875" y="472.5" class="actor" style="text-anchor: middle;">Worker.onmessage</text></g><g><rect x="1000" y="435" fill="#eaeaea" stroke="#666" width="150" height="65" rx="3" ry="3" class="actor"></rect><text x="1075" y="472.5" class="actor" style="text-anchor: middle;">mozGpio.export</text></g></svg>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment