シリアル通信とブラウザJavaScriptをブリッジする IchigoLink のご紹介

この記事は、IchigoJam Advent Calendar 2018 8日目の記事です。

1. はじめに

IchigoLink は Scratch 3.0 と IchigoJam を連携させるために開発している、
シリアル通信とブラウザJavaScriptのブリッジです。

Windows, Mac, Linux でコマンドラインアプリケーションとして動作し、
WebSocket を使って ローカルのシリアルポート と ブラウザのJavaScriptをつなぎます。
(ちなみに、micro:bit は同様のツールの BLE版を提供しています。)

本来は Scratch連携用 のツールですが、それ以外にも面白いことができます。

2. 何ができるのか

下記の動画にあるような IchigoJamと連携する作品を JavaScript と HTML だけで作成できます。

IchigoJam  のボタン操作でWebページを変化させる

IchigoJam から取得したアナログ値で Webページを変化させる

WebページからIchigoJam を操作する

3. [使い方] 準備するもの

  • パソコン
  • IchigoJam
  • シリアル通信に必要なデバイス(USBシリアル変換アダプタなど)
  • IchigoJamで使いたいデバイス(LED、ボタン、可変抵抗、各種センサー、各種モーターなど)

4. [使い方] ダウンロードから起動まで

開発中ということで仮の場所になりますが、下記からダウンロードできます。
http://shizentai.jp/ichigolink/

zip ファイルを解凍すると下記のような中身になっているので、コマンドプロンプトやターミナルを立ち上げて、このディレクトリに移動します。

シリアル通信デバイスを接続した状態で、下記のコマンドをたたきます。

Windows の場合

> ichigo-link [シリアルデバイス名]

Mac/Linux の場合

$ ./ichigo-link [シリアルデバイス名]

シリアルデバイス名は Windows の場合はCOM3やCOM4、Linux の場合は /dev/ttyUSB0 などになることが多いようです。

コマンドプロンプトやターミナルが上のような表示になり、
ブラウザで、 http://localhost:30110 を表示したときに下記のような画面になれば起動成功です。箇条書き程度ですが、仕様をまとめてあるので参考にしてください。

また、このページが ichigo-link-www フォルダの index.html に対応しており、ichigo-link-www フォルダ以下にファイルを置くと、localhost:30110 以下で公開されるようになっています。

5. [使い方] 付属デモアプリケーション

デモツールとして、簡易的なターミナルとコード送信ツールを添付しています。
ichigo-link-www/demo の下にファイルが存在します。

簡易ターミナル

http://localhost:30110/demo/terminal.html

connect ボタンを押すと接続します。
上のテキストボックスに入力したものを行単位で送信し、受信したものを下のテキストエリアに表示します。
RUNを中断したいときなどに esc ボタンを押してエスケープシーケンスも送れます。

現状、テキストエリアにデータを追加しているだけで解放処理をしていないので、
大量のデータを受け取ったり、長時間使用すると固まったりするかもしれません。あしからず。

コード送信ツール

http://localhost:30110/demo/send_source.html

まとめて何行か送りたいとき用のツールです。

6. 動画で紹介した作品の作り方

下記のソースコードをテキストエディタなどに貼り付けて、ichigo-link-www 以下に適当なファイル名で保存すれば、localhost:30110 以下でブラウザからアクセスできます。

IchigoJam 側のプログラムを最初に送って RUNするようにしていますが、この部分だけ IchigoJam 側に最初から用意しておいてもよいと思います。

IchigoJam のボタンを押すと背景色がつくページ

<!DOCTYPE html>
<html>
<head>
  <title>IchigoJamのボタンを押すと緑になるページ</title>
  <meta charset="UTF-8"/>
  <script>
    window.addEventListener('load', function () {
      var ws = new WebSocket('ws://localhost:30110/serial');
      ws.addEventListener('open', function () {
        ws.send(JSON.stringify({"type":"esc"}));
        ws.send(JSON.stringify({"type":"text","value":"NEW"}));
        ws.send(JSON.stringify({"type":"text","value":"10 ?BTN():WAIT3:GOTO10"}));
        ws.send(JSON.stringify({"type":"text","value":"RUN"}));
      });
    
      ws.addEventListener('message', function (event) {
        var obj = JSON.parse(event.data);
        if(obj.value == "0") {
          document.body.style.backgroundColor = "white";
        }
        if(obj.value == "1") {
          document.body.style.backgroundColor = "#00C000";
        }
      });
    
    });
  </script>
</head>
<body>
    <h2>IchigoJamのボタンを押すと画面が緑になるページ</h2>
  
</body>
</html>

ANA(2)の値で背景色の濃さが変わるページ

背景色の計算ロジック、地味に苦労しました。改善求む!

<!DOCTYPE html>
<html>
<head>
  <title>ANA(2)の値で緑の濃さが変わるページ</title>
  <meta charset="UTF-8"/>
  <script>
    window.addEventListener('load', function () {
      var ws = new WebSocket('ws://localhost:30110/serial');
      ws.addEventListener('open', function () {
        ws.send(JSON.stringify({"type":"esc"}));
        ws.send(JSON.stringify({"type":"text","value":"NEW"}));
        ws.send(JSON.stringify({"type":"text","value":"10 ?ANA(2):WAIT3:GOTO10"}));
        ws.send(JSON.stringify({"type":"text","value":"RUN"}));
      });
    
      ws.addEventListener('message', function (event) {
        var obj = JSON.parse(event.data);
        var a = parseInt(obj.value,10);
        var b = Math.floor(a/2);
        var c, d;
        if ( b < 256 ) {
	  c = 255 - b;
	  d = 255;
        } else {
	  c = 0;
	  d = 255 - (b-255);
        }
	var color = "rgb(" + c + "," + d + "," + c + ")";
	console.log(color);	 
	document.body.style.backgroundColor = color;
      });
    
    });
  </script>
</head>
<body>
    <h2>ANA(2)の値で緑の濃さが変わるページ</h2>
  
</body>
</html>

LEDとPWMをコントロールするページ

<!DOCTYPE html>
<html>
<head>
  <title>IchigoJamのLEDとPWMをコントロールするページ</title>
  <meta charset="UTF-8"/>
  <script>
    window.addEventListener('load', function () {
      var ws = new WebSocket('ws://localhost:30110/serial');
      ws.addEventListener('open', function () {
        ws.send(JSON.stringify({"type":"esc"}));
        ws.send(JSON.stringify({"type":"text","value":"LED0"}));
        ws.send(JSON.stringify({"type":"text","value":"PWM3,50"}));
        ws.send(JSON.stringify({"type":"text","value":"PWM4,50"}));
        document.getElementById('ledon').addEventListener('click',function() {
          ws.send(JSON.stringify({"type":"text","value":"LED1"}));
        });
        document.getElementById('ledoff').addEventListener('click',function() {
          ws.send(JSON.stringify({"type":"text","value":"LED0"}));
        });
        document.getElementById('pwm3').addEventListener('change',function() {
          var a = parseInt(document.getElementById('pwm3').value,10);
          ws.send(JSON.stringify({"type":"text","value":"PWM3," + a}));
        });
        document.getElementById('pwm4').addEventListener('change',function() {
          var a = parseInt(document.getElementById('pwm4').value,10);
          ws.send(JSON.stringify({"type":"text","value":"PWM4," + a}));
        });
      });
    });
  </script>
</head>
<body>
  <h1>IchigoJamのLEDとPWMをコントロールするページ</h1>
  <form>
    <h3>LED</h3>
    <input type="radio" name="led" id="ledon" />On
    &nbsp;
    <input type="radio" name="led" id="ledoff" checked="checked"/>Off
    
    <h3>PWM3</h3>
    50 <input type="range" id="pwm3" value="0" min="50" max="240" step="10"/> 240
    
    <h3>PWM4</h3>
    50 <input type="range" id="pwm4" value="0" min="50" max="240" step="10"/> 240

  </form>

  
</body>
</html>  

7. まとめと課題

  • IchigoLink を使うと IchigoJam と連携するブラウザアプリケーションを簡単に作れます。
  • Scratch 対応として配布するまでに GUI化したいです。
  • Windows 版の反応がよくない気がするのが少々気になる。

IchigoJam BASIC RPi を USBメモリから起動する

IchigoJam BASIC RPi を USBメモリ から起動。 意外と簡単でした!

準備物

USBメモリからの起動には、Pi 3から対応しているようです。

  • Raspberry Pi 3 Model B
  • IchigoJam BASIC RPi を コピーした micro SDカード
  • IchigoJam BASIC RPi を コピーした USBメモリ

ご注意!

config.txt に program_usb_boot_mode=1 を追加して起動することで、ラズパイの OTP memory という領域が書き換えられて、USBメモリからのブートが可能になるとのこと。ただ、この設定は元に戻すことができないとのこと。試してみる方は自己責任でお願いします!

やりかた

基本的には、下記を参考にすればOKです。
HOW TO BOOT FROM A USB MASS STORAGE DEVICE ON A RASPBERRY PI 3

  1.  micro SDカード の config.txt の最後に  program_usb_boot_mode=1 を追加する
  2.  micro SDカード から一旦起動する
  3.  micro SDカード を抜いて、 USBメモリ を差して起動する
  4.  micro SDカード の config.txt を元に戻す(お忘れなく)

その他

  • USBメモリから起動するときは、いつもより少し時間がかかります。
  • だめもとで USBハードディスク(USB3.0, 500GB)でも試してみましたが、成功しませんでした。なにか工夫すればできるのかも。
  • 大きな領域が使えるとなると、IchigoJam BASIC にもファイルの WRITE/READ が欲しくなってきますね。

ラズパイ版 IchigoJam でアナログ入力

この記事は、IchigoJam Advent Calendar 2017 8日目の記事です。

1. はじめに

LPC1114版 IchigoJam にあって ラズパイ版にないものはけっこうあります。

  • ディスプレイ関連機能 (VIDEO/SWITCH コマンド)
  • サウンド関連機能 (BEEP/PLAY/TEMPO/SOUND コマンド)
  • マシン語関連機能 (USR コマンド)
  • 省電力機能 (SLEEP コマンド)
  • アナログ入力 (ANA コマンド)

その中でもとくに重要なアナログ入力ですが、今後も本体への実装は厳しそうなので、
回避策をご紹介したいと思います。

今回は、I2C通信対応のADコンバータを外部接続し、
ラズパイ版の IchigoJam BASIC でアナログ入力を実現してみます。

とはいえ、アナログ入力が使えないという課題自体は、Raspbian などのOSで
Raspberry Pi を利用している人にも共通しているため、ネット上にたくさんの情報がありました。

特にこちらの記事がとてもわかりやすく参考になりました。
http://kinokotimes.com/2017/02/02/python-mcp3425-16bits/

2. 準備物

Raspberry Pi本体

今回はPi3で試しました。

ADコンバータ

MCP3425というADコンバータのDIP化モジュールを秋月電子さんで購入しました。
http://akizukidenshi.com/catalog/g/gK-08018/

アナログセンサ

弊社によく転がっているシャープ製 測距センサ GP2Y0A02YK を使いました。
http://akizukidenshi.com/catalog/g/gI-03158/

3. ADコンバータの仕様

I2Cアドレス

上位4bit がデバイスアドレスで 0b1101
下位3bit は発注時に指定がなければ 0b000
ということなので、普通に購入した場合、0b1101000 = 0x68 になります。

設定の書き込み

1バイト送信して設定を行います。
bit7: 1に固定
bit4: (1:設定後、連続して読み取り可能; 0: 読み取りのたびに設定必要;)
bit3,2: (0b00:12ビット値を取得; 0b01:14ビット値を取得; 0b10:16ビット値を取得;)

今回は連続読み取り、12bitモードを試しました。(0b10010000)

電圧の読み取り

2バイト受信して読み取ります。(コマンドは0x00)
0バイト目: 上位8bit(右詰め)
1バイト目: 下位8bit

12bit,14bit,16bit の各先頭のビットは符号ビットなので、
実際の数値は 11bit, 13bit, 15bit マスクして取得するようです。

4. 配線

下記の接続でうまくいきました。(あとで図を載せたいです!)

測距センサ

VDD — RPi(5V)
VSS — RPi(GND)
VO — ADC(VIN+)

ADコンバータ

VIN+ — センサー(VO)
VIN- — RPi(GND)
VSS — RPi(GND)
VDD — RPi(3.3V)
SCL — RPi(SCL)
SDA — RPi(SDA)

5. BASIC コード

下記のコードで、なんとなくそれらしい値が取得できました。

10 POKE #700,`10010000
20 POKE #701,#00
30 R=I2CW(#68,#700,1)
40 R=I2CR(#68,#701,1,#702,2)
50 ?((PEEK(#702)<<8)|PEEK(#703))&`11111111111
60 WAIT5:GOTO40

6. 今後の課題

  • 14bitモードや16bitモードも試してみましたが、うまくいきませんでした。
  • 下位3bitのI2Cアドレスを指定できれば、切り替えて複数アナログ入力もできるかもしれません。