良くある質問と回答

CHIRIMEN Raspi3 を利用していて良くある質問や回答のメモページです。利用上のテクニックや知っておくと良いことは TIPS ページ に書いているので、そちらも合わせてご覧ください。

できること・できないこと

CHIRIMEN Raspi3 では何が出来ますか?

CHIRIMEN Raspi3 は一般的な Web アプリケーションの開発環境に、GPIO と I2C という IoT プロトタイピングなどでよく使われるハードウェアインターフェイスを ウェブブラウザのJavaScript から直接制御可能にしたものです。WebI2C API, Web GPIO API

デスクトップの Web アプリで出来ることは全て同じようにして実装可能ですし、Web アプリで出来ないことはローカルのサーバと通信させたり拡張機能を作って通信させたりして他のコンテキストや言語の環境で実行させることもあります。

GPIO, I2C 以外によく見聞きするハードのインターフェイスについては次の通りです:

また、GPIO, I2Cに対しては以下の制限があります。

動作環境

Raspberry Pi 3 Model A+ はサポートしないの?

理論的には移植可能と考えられますが、本ページの執筆時点では技適通過モデルの販売が開始されておらず、未検証です。Model B, Model B++ と比べると Model A+ ではメモリ容量が半分となるため、Chromium 以外に多くのアプリケーションを同時に起動すると処理が重たくなることが予想されます。

Raspberry Pi Zero はサポートしないの?

論理的には移植可能と考えられますが、未検証・未対応です。

Raspberry Pi 以外のボードでは動作しないの?

CHRIMEN コミュニティで公式にサポートしている環境はありませんが、Chrome (Chromium) や Firefox のようなブラウザが動作し、Node サーバなどがインストールできる環境であればほぼ同様にして動作するように移植可能と考えられます (実際いくつかのボードで試して使っているコミュニティメンバーもいます)。

もちろん、ポート番号など物理配線が違う場合はそれに合わせてコードを書き換える必要があることには注意が必要です。

サービス連携

メールを送信したい

スパム対策などもあり、ローカルのメールサーバなどからの送信は正しく配信されないことがあるため、Gmail などのメールサービスを利用して送信することが望ましいです。Gmail などのメール送信 API を利用するための認証などの実装が面倒な場合は IFTTT のトリガーとして Webhook を、アクションとして Gmail を利用するなどすれば比較的簡単にメールの送信が可能です。

LINE, Slack その他のサービスと連携したい

IFTTT のトリガーとして Webhook を利用すると簡単に様々なサービスとの連携が可能です。Webhook 用の URL や使い方は IFTTT にログインしてから Webhook ページからリンクされている documents ページや Webhooks settings ページ をご覧ください。

IFTTT では応答性が悪い (トリガーをキックしてからアクションまでの待ち時間が長すぎる) 場合は連携先のサービスの API を使って自分で直接各サービスとの連携を実装してください。

他のプログラムとの連携

CHIRIMEN ではブラウザ上の JavaScript だけでハードウェア制御を可能にしていますが、様々な理由で C, Python, Node, シェルスクリプトなどで書かれたコードと連携したいことがあります。そのような場合はブラウザの JavaScript とバックエンドのコードとの間を WebSocket で接続して通信させながら動作させてください (非常に高速な通信が必要な場合は除く)。

例えば Node で WebSocket サーバを作る場合はこのように index.js ファイルを作成し

// Node で実行する JavaScript (index.js)
var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({ port:8080 });

wss.on('connection', function(ws) {
    // ブラウザからメッセージを受信した時の処理
    ws.on('message', function(message) {
        console.log("Message Received: " + message);
    });

    // ブラウザにメッセージを送信する時の処理
    wss.clients.forEach(function(client){
        client.send("A message from server");
    });
});

次のように ws モジュールのインストールと node サーバの実行をすればサーバが起動します。

// ws モジュールを現在のディレクトリにインストール (初回一度だけ必要)
npm install ws
// index.js を node で実行する:
node index.js

この Node サーバにブラウザ側からは次のようにメッセージの送受信が可能です。

// ブラウザで実行する JavaScript
var ws = new WebSocket('ws://localhost:8080');
ws.addEventListener('open', function(event){
    console.log('WebSocket 接続完了');

    // サーバからメッセージを受け取ったときの処理
    ws.addEventListener('message',function(e){
        console.log("Message Recieved: " + e.data);
    });
    
    // サーバにデータを送信するときの処理
    ws.send("A message from browser");
});

トラブルシューティング

同時に複数のタブで開くと動作しない

制限事項です。API では特に規定されていませんが排他制御をしており同一のページを複数タブで開くなど、同じポートを同時に扱うコードを書くと正しく動作しなくなることがあります。全てのタブを閉じてから目的のページだけを開き直してください。

コンソールに Uncaught ReferenceError: xxx is not defined などと表示される

変数 xxx にアクセスしようとしているがそれが定義されていないというエラーです。単純に変数名などを Typo (入力し間違え) していることが多いです。次に多いのは定義域の外でアクセスしようとしている場合です。JavaScript の変数スコープは var で宣言した場合は関数単位、let や const で宣言した場合にはブロック単位です。別の関数の中からなど、スコープ外からアクセスしようとしていないか確認してください。

コンソールに Uncaught TypeError: Cannot read property 'xxx' of nullUncaught TypeError: Cannot read property 'xxx' of undefined などと表示される

オブジェクトのプロパティ xxx にアクセスしようとしている (some.xxx のようなコード)、プロパティ xxx を持つと考えている変数 some がオブジェクトではなく null や undefined となっており、プロパティアクセスができないというエラーです。変数 some の取得・代入をしているコードに問題がないか確認してください。

コンソールに Uncaught (in promise) TypeError: navigator.requestGPIOAccess is not a function などと表示される

関数 (メソッド) 呼び出ししようとしているがその関数が定義されていないというエラーです。この場合 navigator.requestGPIOAccess が関数ではないと言うことですが、polyfill スクリプトを読み込んでいないか、読み込みより前にアクセスしようとしている場合に発生します。

コンソールに Uncaught SyntaxError: await is only valid in async function などと表示される

await 文は async (非同期) 関数の中でのみ利用可能です。関数の中で await を使って非同期処理を行いたい場合、その関数を async 関数として宣言する必要があります。addEventListener など引数に関数を渡している場合に async を付け忘れているケースが多いので注意してください。

// 関数宣言:
async function wait100ms() { await sleep(100); }
// 匿名関数を使う場合:
element.addEventListener("click", async function() { await sleep(100); }, false);
// アロー(矢印)関数を使う場合
element.addEventListener("click", async () => { await sleep(100); }, false);

JS Bin のコンソールに error: ws error: [object Event] と表示される

コンソールに WebSocket connection to 'wss://localhost:33330/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED などと表示される

CHIRIMEN Raspi3 のバックエンドサーバに接続できていません。デスクトップの reset.sh で再起動してください。

JavaScript から特定の URL にアクセスできない

コンソールに Failed to load https://...: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://...' is therefore not allowed access. などと表示される

コンソールに Access to fetch at 'https://...' from origin 'https://...' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. などと表示される

CHIRIMEN に限らず一般的な Web 開発でよく見かけるエラーです。Web には 同一オリジンポリシー (Same Origin Policy) というセキュリティ制約があり、JavaScript を読み込んでいるのと異なるドメインの URL には相手側のサーバが オリジン管理ソース共有 (CORS) で明示的に許可している場合以外は JavaScript のコード中で XMLHttpRequest や fetch からアクセスできません。

単純化して言えば、サーバからの HTTP レスポンスヘッダに access-control-allow-origin: * が付与されていれば JavaScript からのアクセスが許可されるため、任意の URL へのアクセスをプロキシ (中継) してレスポンスヘッダを勝手に追加してくれるようなサーバを用意すれば任意のドメインから任意の URL にアクセスが可能になります。

そのような機能を持った公開の CORS プロキシサービスには例えば https://cors-anywhere.herokuapp.com/https://cors.io/ などいろいろなものがあります。なお、これらのサービスの利用は本来のセキュリティ機能を無視するものであり、利用に際しては注意が必要です。あくまでもプライバシー情報などを含まないものについて、テストやプロトタイピング時だけに限って利用すべきです。

コードも配線も正しいのにとにかく動作しない!

いろいろな原因が考えらるため、ひとつずつ確認していく必要があります。

ハンズオン・ハッカソン・講義など、講師やチューターなどのいる時は、色々試して分からないときは一人で悩まず遠慮なく質問しましょう。 ひとつのことに悩んで糸口が見つけられないまま何十分も過ごさず、どんどん質問してデバッグテクニックや回避策などを教わってスキルを向上させていってください。

デバッグチェックリスト

問題解決のためのチェックリスト (初心者向け) を書いてみたので参考にしてください: