前回の投稿 「ESP32をJavascriptでLチカする」の続きです。
前回は、GPIOを制御するJavascriptを記述しました。
今回は、I2Cを制御するJavascriptを記述します。また、Javascriptで行っているように、複数のファイルにモジュール分割する方法も示します。
ソースコードもろもろは、前回同様以下に上げてあります。
poruruba/QuickJS_ESP32
#setup()・loop()をasync/await対応にする
以前は、setup()やloop()の定義を以下のようにしていました。
function setup(){
}
function loop(){
}
このままでもよいのですが、今後、async/awaitを使う機会が増える可能性があるため、以下のように変更します。
async function setup(){
}
async function loop(){
}
async/awaitは、非同期処理を簡単に扱うための構文で、Javascriptではよく目にします。以降は、この記述になっていることを前提としています。
#非同期関数の定義と呼び出し
非同期関数はasyncを付けて定義します。例えば、以下のような、一定時間ウェイトする関数の場合です。
async function wait(msec){
return new Promise(resolve => {
setTimeout( () =>{
console.log("time out");
resolve();
}, msec);
});
}
上記を呼び出す場合は以下のようにします。
console.log("waiting");
await wait(1000);
console.log("waked up");
わかりやすくするために、console.logで挟んでいますが、awaitを付けることで、waitの中の処理が完了するまでブロックされて、完了後に次の「console.log(“waked up”)」が実行されます。
もし、awaitを付けないと、waitの関数が終了するのを待たずに、すぐに次の「console.log(“waked up”)」が実行されます。
まとめるとこんな感じです。
(i)awaitを付けた場合
waiting
time out
waked up
(ii)awaitを付けない場合
waiting
waked up
time out
#I2C呼び出し
I2Cは以下のように呼び出します。wireモジュールを使いますので、あらかじめimportでロードしておく必要があります。
見てお分かりの通り、Arduinoでの記法に合わせています。
import * as wire from "wire";
wire.beginTransmission(address);
wire.write(0x00);
wire.write(0x01);
wire.endTransmission();
#モジュール化
Javascriptの記述をすべてmain.jsに書くと、記載が込み入ってしまいますし、別ファイル(モジュール)に分けて記載した方が他のプログラムに流用しやすくなります。
①複数の関数や変数をモジュールで宣言する場合
モジュール側で以下のようにexportを付けて複数の関数や変数を定義したとします。
export function add(a, b){
return a + b;
}
export function sub(a, b){
return a - b;
}
これらを使う側では以下のように呼び出します。
import * as math from "math";
var a1 = math.add(1, 2);
var a2 = math.sub(3, 4);
②1つのクラスをモジュールで宣言する場合
モジュール側で以下のようにexportを付けて1つのクラスを定義したとします。
export default class Test {
constructor() {
}
func() {
return 1;
}
}
これらを使う側では以下のように呼び出します。
import Test from "Test";
var test = new Test();
var a3 = test.func();
ファイルがmain.jsとモジュールのJavascriptで複数のファイルに分かれるようになりましたので、ESP32へのロード方法を少し変えます。
以下のJSONファイル「modules.json」を作成します。
{
"modules": [
{
"name": "math",
"url": "http://【WebサーバのURL】/math.js"
}
]
}
このmodules.jsonがWebサーバ上にあれば、ここに記載されたモジュールを先にロードしてから、main.jsをロードするようにしてあります。
ちなみに、nameで指定した名前が、importのfromで指定した文字列になります。
main.cppの以下の部分を環境に合わせて変更してください。
const char *jscode_modules_url = "http://【WebサーバのURL】/modules.json"; //【モジュールJsonの取得先URL】
#ENV Ⅱ Hatを制御する
それではさっそくI2Cデバイスを制御してみます。
以下の、M5StickCに取り付け可能なENV Ⅱ Hatを使います。
これには、2つ(+1つ)のI2Cデバイスが入っているようです。
・SHT30
・BMP280
C言語のサンプル実装を参考に、Javascriptのモジュールに起こしてみました。
export default class SHT30 {
constructor(wire, address = 0x44) {
this.wire = wire;
this._address = address;
this.cTemp = 0;
this.fTemp = 0;
this.humidity = 0;
}
async get() {
// Start I2C Transmission
this.wire.beginTransmission(this._address);
// Send measurement command
this.wire.write(0x2C);
this.wire.write(0x06);
// Stop I2C transmission
if (this.wire.endTransmission() != 0)
return 1;
await this.sleep(500);
// Request 6 bytes of data
this.wire.requestFrom(this._address, 6);
// Read 6 bytes of data
// cTemp msb, cTemp lsb, cTemp crc, humidity msb, humidity lsb, humidity crc
var data = this.wire.read(6);
await this.sleep(50);
if (this.wire.available() != 0)
return 2;
// Convert the data
this.cTemp = ((((data[0] * 256.0) + data[1]) * 175) / 65535.0) - 45;
this.fTemp = (this.cTemp * 1.8) + 32;
this.humidity = ((((data[3] * 256.0) + data[4]) * 100) / 65535.0);
return 0;
}
async sleep(msec){
return new Promise(resolve => setTimeout(resolve, msec));
}
};
※BMP280.jsは、GitHubをご参照ください。
#呼び出してみる
import * as wire from "wire";
import SHT30 from "SHT30";
import BMP280 from "BMP280";
var sht30;
var bmp280;
async function setup()
{
var ipaddress = esp32.getIpAddress();
console.log("ipaddress=" + ((ipaddress >> 24) & 0xff) + "." + ((ipaddress >> 16) & 0xff) + "." + ((ipaddress >> 8) & 0xff) + "." + (ipaddress & 0xff));
wire.begin(0, 26);
sht30 = new SHT30(wire);
bmp280 = new BMP280(wire);
await bmp280.begin();
}
async function loop()
{
await sht30.get();
console.log("cTemp=" + sht30.cTemp.toFixed(2));
console.log("fTemp=" + sht30.fTemp.toFixed(2));
console.log("humidity=" + sht30.humidity.toFixed(2));
var tmp = bmp280.readTemperature();
console.log("Temperature=" + tmp);
var press = bmp280.readPressure();
console.log("Pressure=" + press);
console.log("");
delay(1000);
}
#終わりに
次回はLCDの呼び出し方を紹介する予定です。
以上