Japanese Stable CLIP を試してみた

node.js でセンサー(i2c)の値を取得する

その1

その2


python を使ってセンサーの値を取得することができたので、次は node.js を使ってセンサーの値を取得します。物理的な接続はできていて、python での値の取得もできている、という前提です。


i2c デバイスファイルの権限変更

まず、root 権限でなくても i2c デバイスの読み書きができるようにします。スクリプトを sudo したりしなくてはならなくて不便なので、ユーザーに権限を付与します。

まず、デバイスファイルの権限を確認します。

$ ls -l /dev/i2c-*
crw-rw---- 1 root i2c 89, 0  9月 15 23:17 /dev/i2c-0
crw-rw---- 1 root i2c 89, 1  9月 15 23:17 /dev/i2c-1
所有権はユーザーが "root"、グループは "i2c" になっています。なので、ユーザーを i2c グループに追加すればよさそうです。
$ sudo gpasswd -a **** i2c
****は実際のユーザー名に置き換えてください。これで再ログインすれば、前回のスクリプトも sudo を付けずに実行することが可能になります。

node.js インストール

armbian の場合、apt でも node.js をインストールできますが、npm のバージョンが古いのか、 python2.7 に依存しています。そのためだけに大量の関連パッケージをインストールするのももったいないので、nodebrew を使ってインストールすることにします。

まず nodebrew のダウンロード。

$ curl -L git.io/nodebrew | perl - setup
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
100 24634  100 24634    0     0  13886      0  0:00:01  0:00:01 --:--:-- 13886
Fetching nodebrew...
Installed nodebrew in $HOME/.nodebrew

========================================
Export a path to nodebrew:

export PATH=$HOME/.nodebrew/current/bin:$PATH
========================================
コマンドの出力にあるとおり、.bashrc などに export で始まる行を追加して、nodebrew をダウンロードしたディレクトリをパスに追加します。
nodebrew を使って、最新版の node.js をインストールします。
$ nodebrew install-binary stable
Fetching: https://nodejs.org/dist/v14.11.0/node-v14.11.0-linux-armv7l.tar.gz
################################################################################################################# 100.0%
Installed successfully
nodebrew はインストール済みの node.js のバージョンを切り替えて使用することができます。そのため、インストールしたうえで、どのバージョンを使用するか指定する必要があります。インストールしただけだと、以下のように「v14.11.0」がインストールされていますが、「current: none」とあるように、使用するバージョンが指定されていない状態になります。
$ nodebrew list
v14.11.0

current: none
なので、使用するバージョンを指定します。
$ nodebrew use v14.11.0
use v14.11.0
無事、current に使用するバージョンが表示されました。
$ nodebrew list
v14.11.0

current: v14.11.0
ついでに、TypeScript を使うことにします。
$ mkdir builders_flash
$ cd builders_flash
$ npm init -y
$ npm i -D typescript @types/node
$ npx tsc --init
センサーの読み書きに使う i2c-bus ライブラリもインストールします。
$ npm i i2c-bus -s


コーディング

TypeScript は初めて使うので、いろいろと試行錯誤しましたが、結果的にはこんな感じのスクリプトになりました。途中の複雑な計算は、BME280 のデータシートに記載されている計算式ほぼそのまま (のつもり) です。

src/bme280.ts

const DEVICE_NUMBER = 0;
//const TARGET_IC_ADDR = 0x23;
const TARGET_IC_ADDR = 0x76;

function getShort(data: any, index: number): number
{
        return (data[index + 1] << 8) + data[index]
}

function getUShort(data: any, index: number): number
{
        return (data[index + 1] << 8) + data[index]
}

function getChar(data: any, index: number): number
{
        let result: number = data[index];
        if(result > 127)
        {
                result = result - 256;
        }
        return result
}

function getUChar(data: any, index: number): number
{
        let result: number = data[index] & 0xFE;
        return result;
}

function sleep(msec: number)
{
        return new Promise(function(resolve) {
                setTimeout(function() {resolve()}, msec);
        })
}

function readBME280(): void
{
        const REG_DATA = 0xF7;
        const REG_CONTROL = 0xF4;
        const REG_CONFIG = 0xF5;

        const REG_CONTROL_HUM = 0xF2;
        const REG_HUM_MSB = 0xFD;
        const REG_HUM_LSB = 0xFE;

        const OVERSAMPLE_TEMP = 2;
        const OVERSAMPLE_PRES = 2;
        const MODE = 1;

        const OVERSAMPLE_HUM = 2;

        var i2c = require('i2c-bus');
        var i2c1 = i2c.openSync(DEVICE_NUMBER);

        // write param
        i2c1.writeByteSync(TARGET_IC_ADDR, REG_CONTROL_HUM, OVERSAMPLE_HUM);
        const control = OVERSAMPLE_TEMP << 5 | OVERSAMPLE_PRES << 2 | MODE;
        i2c1.writeByteSync(TARGET_IC_ADDR, REG_CONTROL, control);

        var cal1 = Buffer.alloc(24);
        var cal2 = Buffer.alloc(1);
        var cal3 = Buffer.alloc(7);

        i2c1.readI2cBlockSync(TARGET_IC_ADDR, 0x88, cal1.length, cal1);
        i2c1.readI2cBlockSync(TARGET_IC_ADDR, 0xA1, cal2.length, cal2);
        i2c1.readI2cBlockSync(TARGET_IC_ADDR, 0xE1, cal3.length, cal3);

        const dig_T1 = getUShort(cal1, 0);
        const dig_T2 = getShort(cal1, 2);
        const dig_T3 = getShort(cal1, 4);

        const dig_P1 = getUShort(cal1, 6);
        const dig_P2 = getShort(cal1, 8);
        const dig_P3 = getShort(cal1, 10);
        const dig_P4 = getShort(cal1, 12);
        const dig_P5 = getShort(cal1, 14);
        const dig_P6 = getShort(cal1, 16);
        const dig_P7 = getShort(cal1, 18);
        const dig_P8 = getShort(cal1, 20);
        const dig_P9 = getShort(cal1, 22);

        const dig_H1 = getUChar(cal2, 0);
        const dig_H2 = getShort(cal3, 0);
        const dig_H3 = getUChar(cal3, 2);

        let dig_H4 = getUChar(cal3, 3);
        dig_H4 = (dig_H4 << 24) >> 20;
        dig_H4 = dig_H4 | (getChar(cal3, 4) & 0x0F)

        let dig_H5 = getChar(cal3, 5);
        dig_H5 = (dig_H5 << 24) >> 20;
        dig_H5 = dig_H5 | (getUChar(cal3, 4) >> 4 & 0x0F)

        const dig_H6 = getChar(cal3, 6)

        // Wait
        const wait_time = 1.25 + (2.3 * OVERSAMPLE_TEMP) + ((2.3 * OVERSAMPLE_PRES) + 0.575) + ((2.3 * OVERSAMPLE_HUM) + 0.575);
        sleep(wait_time / 1000);

        // Read temperature/pressure/humidity
        var data = Buffer.alloc(8);
        i2c1.readI2cBlockSync(TARGET_IC_ADDR, 0xF7, data.length, data);
        const pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
        const temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
        const hum_raw = (data[6] << 8) | data[7]

        // Refine tempreture
        let var1 = (((temp_raw >> 3) - (dig_T1 << 1)) * (dig_T2)) >> 11;
        let var2 = (((((temp_raw>>4) - (dig_T1)) * ((temp_raw>>4) - (dig_T1))) >> 12) * (dig_T3)) >> 14;
        let t_fine = var1+var2;
        let temperature = ((t_fine * 5) + 128) >> 8;

        // Refine pressure and adjust for temperature
        let pressure = 0;
        var1 = t_fine / 2.0 - 64000.0;
        var2 = var1 * var1 * dig_P6 / 32768.0;
        var2 = var2 + var1 * dig_P5 * 2.0;
        var2 = var2 / 4.0 + dig_P4 * 65536.0;
        var1 = (dig_P3 * var1 * var1 / 524288.0 + dig_P2 * var1) / 524288.0;
        var1 = (1.0 + var1 / 32768.0) * dig_P1;
        if(var1 == 0)
        {
            pressure = 0;
        } else {
            pressure = 1048576.0 - pres_raw;
            pressure = ((pressure - var2 / 4096.0) * 6250.0) / var1;
            var1 = dig_P9 * pressure * pressure / 2147483648.0;
            var2 = pressure * dig_P8 / 32768.0;
            pressure = pressure + (var1 + var2 + dig_P7) / 16.0;
        }

        // Refine humidity
        let humidity = t_fine - 76800.0;
        humidity = (hum_raw - (dig_H4 * 64.0 + dig_H5 / 16384.0 * humidity)) * (dig_H2 / 65536.0 * (1.0 + dig_H6 / 67108864.0 * humidity * (1.0 + dig_H3 / 67108864.0 * humidity)));
        humidity = humidity * (1.0 - dig_H1 * humidity / 524288.0);
        if(humidity > 100)
        {
                humidity = 100;
        } else if(humidity < 0)
        {
                humidity = 0;
        }

        console.log(temperature / 100.0, pressure / 100.0, humidity);
}

readBME280();
今頃気が付きましたが、getShort と getUShort 関数が同じになってますね・・・あと、BH1750FVI の値を取得するのを忘れてました。とはいえ、この状態で実行すると、
$ npx ts-node src/bb.ts
25.8 1088.2437277298607 49.58872975883544
と、値が取得できました。

コメント