Espruino入門【I2C編】 Espruino Advent Calendar 2014
ちょっと(かなり)前に、Espruinoに関する2つのエントリーを書きました。
・Espruino環境を整える - そのボード、僕が積んでおきます。
・Espruino入門【Lチカ編】 - そのボード、僕が積んでおきます。
しばらく他のことに現を抜かしていたのですが、QiitaのAdventCalendar一覧を見ていたら、Espruinoのカレンダーがあったので急遽Espruinoの続きにチャレンジしました。このエントリーはEspruino Advent Calendar 2014の12月10日分のエントリーです。
今回は前回最後の予告通りI2Cの通信にチャレンジしてみたいと思います。
最近はI2Cで通信するセンサー類も増えていますし、簡単な表示器もI2C対応していますのでI2Cのリードライトができれば、結構色々なパターンの工作ができます。まさにIoTの基本はI2Cにあり、ということで、I2Cです。
【作ったもの】
Espruinoでコントロールする大気圧計
今回は、比較的メジャーな2つのデバイスをコントロールします。
・センサー:大気圧センサー「LPS331」
どちらも、秋月で手に入る、と思ったら、大気圧センサーがもう秋月にはないみたいです。なので、参考程度に。今回はハードウェア側は手を抜きたかったので、以前に作っていたトランジスタ技術2014年2月号の付録のI2C学習基板を用いました。
そして、今回のメイン基板は前々回にEspruino化したSTM32F4 Discoveryです。Advent Calendarでは純正基板を使ってる方が多くて羨ましいですが、とりあえず、僕は手持ちのボードで行きます。
さて、Espruinoは標準環境でも様々なデバイスに対応しています。
ところが運悪く、今回使うデバイスは含まれていませんでしたので、I2Cライブラリを使い個別に処理を記述することにします。
基本的にI2C通信はそんなに難しい処理ではありません。デバイスのI2Cアドレスがわかり、制御方法がわかれば移植は容易ですので、Arduinoでの実装例をベースに作っていきます。
delay()関数
デバイスのコントロールを書く前に、I2Cの制御ではタイミングが重要となることもあるので、こちらのページを参考にして、Arduinoでお馴染みのdelay()関数を実装します。
function delay(msec) {
var time0 = getTime();
var time1 = 0;
while(true) {
time1 = getTime();
if ((time1 - time0) * 1000 >= msec) {
break;
}
}
}
LCDのコントロール
僕は何か表示されるとテンションが上がるタイプなので、まずはLCDのコントロールから実装しようと思います。
参考にしたのはこちらのページです。
まずは、I2Cの初期化を行わなければなりません。今回はI2C1を利用します。ピンの位置はこのページを参考にしています。
Espruino - ReferenceSTM32F4DISCOVERY
I2C接続は、電源、GND、I2Cクロック(SCL)、I2Cデータ(SDA)の4本をつなぐだけで良く、非常にシンプルな接続が可能です。複数の機器を繋ぐ場合も、全ておなじ系統につなぐだけでOKです。SCLとSDAはバスのどこかにプルアップ抵抗を挟む必要がありますが、今回はテスト基板でプルアップ抵抗が実装済みですので単純に4本の線を接続しただけです。
そして、EspruinoでのI2Cの初期化は以下のように行います。
I2C1.setup({scl:B6,sda:B7});
これで、B6ピン、B7ピンはI2C接続で利用できるようになります。
あとは、Androidのコードを粛々と移植していきます。
function lcd_cmd(x) {
I2C1.writeTo(0x3e, 0x00, x);
}
function lcd_data(x) {
I2C1.writeTo(0x3e, 0x40, x);
}
function lcd_init() {
delay(500);
lcd_cmd(0x38);
lcd_cmd(0x39);
lcd_cmd(0x14);
lcd_cmd(0x70);
lcd_cmd(0x56);
lcd_cmd(0x6c);
delay(200);
lcd_cmd(0x38);
lcd_cmd(0x0c);
lcd_cmd(0x01);
delay(2);
}
function lcd_puts(str) {
for (var i = 0;i < str.length; i++) {
lcd_data(str.charCodeAt(i));
}
}
function lcd_move(pos) {
lcd_cmd(0x80 | pos);
}
function lcd_clear() {
lcd_move(0x00);
lcd_puts(" ");
lcd_move(0x40);
lcd_puts(" ");
}
今回テスト程度のコードですので、ハードコーディングです。lcd_cmd()とlcd_data()の中でI2C1.writeTo()の第一引数として登場している0x3eがこのデバイスのI2Cアドレスです。あとは、見ての通り、初期化手順に沿って初期化することと、lcd_move()でデータ書き込み位置を指定し、lcd_data()でデータを送り込みます。
画面のクリアはシンプルに「スペースで埋める」という手法で行います。この中で出てくる0x40は、2行目の一番左のアドレスになります。
I2Cの初期化後であれば、
lcd_init();
lcd_move(0x00);
lcd_data("TEST");
で画面にTESTという文字が表示されるはずです。
おお、これはテンション上がる。この勢いでセンサーの方も行けそうです。
大気圧センサーからのデータ取得
大気圧センサーの制御はこちらのページを参考にしました。
こちらはデータの取得があります。writeでデータの書き込み、readがデータの読み取りです。
function pressure_write(address,data) {
I2C1.writeTo(0x5c, address, data);
}
function pressure_read(address) {
I2C1.writeTo(0x5c, address);
return I2C1.readFrom(0x5c, 1);
}
function pressure_init() {
pressure_write(0x20, pressure_read(0x20) & 0x7f);
pressure_write(0x10, 0x7a);
pressure_write(0x20, 0x04);
pressure_write(0x20, 0x84);
delay(2);
}
例によって、pressure_write()とpressure_read()の中に登場する0x5cはこのデバイスのI2Cアドレスです。
データの取得に関しては、参考にさせていただいたページよりも簡易化して以下のようにしました。
function pressure_get() {
pressure_write(0x21, pressure_read(0x21) | 0x01);
while ((pressure_read(0x27) & 0x03) != 0x03) {
}
var press = pressure_read(0x2a);
press = pressure_read(0x29) | (press << 8);
press = pressure_read(0x28) | (press << 8);
return (press / 4096);
}
この関数で、全16桁の小数でヘクトパスカル値が返ってきます。
関数をまとめあげる
この結果を8桁できり捨てて1行目に表示する関数を作成します。この関数をsetInterval()で指定する想定です。
function refresh_display() {
lcd_move(0);
lcd_puts(pressure_get().toString().substr(0, 8));
}
こういう関数を作成しました。
最後にこれらをEspruinoの流儀で呼び出すようにします。
function onInit() {
I2C1.setup({scl:B6,sda:B7});
lcd_init();
pressure_init();
lcd_clear();
lcd_move(0x45);
lcd_puts("hPa");
var interval = setInterval(refresh_display, 500);
}
電源投入後に、I2Cの初期化、LCDの初期化、大気圧センサーの初期化、画面に「hPa」の表示をした後に、500msecごとに画面書き換えをするようにしています。
これで大気圧計の出来上がりです。即席のコードなので、JavaScriptっぽさの全くないベタ書きのプログラムですが。
前にも書きましたが、シリアルで接続したボードで直接プログラムを実行しながら書いていけるので、プロトタイピングにはもってこいです。よく使っている他の環境のような、ビルドして書き込み、という手順に比べて圧倒的な手軽さです。これこそがEspruinoの最大の魅力だと感じます。
次回はもう少しJavaScriptっぽい記述になるように書き換えてみたいと思います。