ESP32をJavascriptでLチカする：I2Cデバイスを制御する

前回の投稿 「[ESP32をJavascriptでLチカする](https://qiita.com/poruruba/items/96db9d20e7e4ced7635d)」の続きです。

前回は、GPIOを制御するJavascriptを記述しました。
今回は、I2Cを制御するJavascriptを記述します。また、Javascriptで行っているように、複数のファイルにモジュール分割する方法も示します。

ソースコードもろもろは、前回同様以下に上げてあります。

poruruba/QuickJS_ESP32

https://github.com/poruruba/QuickJS_ESP32

#setup()・loop()をasync/await対応にする

以前は、setup()やloop()の定義を以下のようにしていました。

```js:public/main.js
function setup(){
}

function loop(){
}
```

このままでもよいのですが、今後、async/awaitを使う機会が増える可能性があるため、以下のように変更します。

```js:public/main.js
async function setup(){
}

async function loop(){
}
```

async/awaitは、非同期処理を簡単に扱うための構文で、Javascriptではよく目にします。以降は、この記述になっていることを前提としています。

#非同期関数の定義と呼び出し

非同期関数はasyncを付けて定義します。例えば、以下のような、一定時間ウェイトする関数の場合です。

```js
async function wait(msec){
  return new Promise(resolve => {
    setTimeout( () =>{
      console.log("time out");
      resolve();
    }, msec);
  });
}
```

上記を呼び出す場合は以下のようにします。

```js
  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での記法に合わせています。

```js
import * as wire from "wire";

wire.beginTransmission(address);
wire.write(0x00);
wire.write(0x01);
wire.endTransmission();
```

#モジュール化

Javascriptの記述をすべてmain.jsに書くと、記載が込み入ってしまいますし、別ファイル(モジュール)に分けて記載した方が他のプログラムに流用しやすくなります。

①複数の関数や変数をモジュールで宣言する場合

モジュール側で以下のようにexportを付けて複数の関数や変数を定義したとします。

```js:public/math.js
export function add(a, b){
  return a + b;
}

export function sub(a, b){
  return a - b;
}
```

これらを使う側では以下のように呼び出します。

```js:public/main.js
import * as math from "math";

var a1 = math.add(1, 2);
var a2 = math.sub(3, 4);
```

②1つのクラスをモジュールで宣言する場合

モジュール側で以下のようにexportを付けて1つのクラスを定義したとします。

```js
export default class Test {
  constructor() {
  }

  func() {
    return 1;
  }
}
```

これらを使う側では以下のように呼び出します。

```js
import Test from "Test";

var test = new Test();
var a3 = test.func();
```

ファイルがmain.jsとモジュールのJavascriptで複数のファイルに分かれるようになりましたので、ESP32へのロード方法を少し変えます。
以下のJSONファイル「modules.json」を作成します。

```json:public/modules.json
{
	"modules": [
		{
			"name": "math",
			"url": "http://【WebサーバのURL】/math.js"
		}
	]
}
```

このmodules.jsonがWebサーバ上にあれば、ここに記載されたモジュールを先にロードしてから、main.jsをロードするようにしてあります。
ちなみに、nameで指定した名前が、importのfromで指定した文字列になります。

main.cppの以下の部分を環境に合わせて変更してください。

```cpp:Arduino\QuickJS_M5StickC\src\main.cpp
const char *jscode_modules_url = "http://【WebサーバのURL】/modules.json"; //【モジュールJsonの取得先URL】
```


#ENV Ⅱ Hatを制御する

それではさっそくI2Cデバイスを制御してみます。
以下の、M5StickCに取り付け可能なENV Ⅱ Hatを使います。

https://www.switch-science.com/catalog/6559/

これには、2つ(+1つ)のI2Cデバイスが入っているようです。

・SHT30
・BMP280

C言語のサンプル実装を参考に、Javascriptのモジュールに起こしてみました。

```js:public/SHT30.js
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をご参照ください。


#呼び出してみる

```js:public/main.js
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の呼び出し方を紹介する予定です。

以上