Japanese Stable CLIP を試してみた

Amplify でセンサー値を書き込む

その1

その3で突然の Node.js 登場でしたが、↓この記事を見ての作業でした。

なので、次は Amplify のセットアップからセンサー値を書き込むところをやっていきます。

Amplify をインストール

% npm install -g @aws-amplify/cli
インストールは以上。簡単ですね。

Amplify の初期設定

まず、configure オプションで初期設定します。
% amplify configure
対話型コンソールになるので、AWS のリージョンをカーソルで選んで Enter で決定します。続いて IAM のユーザー名ですが、特に指定がなければそのまま Enter でランダムの名前が使われます。
すると、コンソールに URL が表示されるので、別途ブラウザでこの URL を開き、IAM ユーザーを作成します。
ユーザー作成が完了すると、アクセスキーとシークレットキーが表示されます。ここを逃すと取得する方法がない(ユーザーを再作成するしかないはず)ので、控えるのを忘れないようにします。
コンソールに戻り、Enter キーを押すとアクセスキーとシークレットキーの入力を促されるので、入力します。
"successfully set up the new user" と表示されれば完了です。 

次に、その3で作成した builders_flash ディレクトリに移動して、amplify を追加します。
% amplify init
ほぼデフォルト値のまま進めてかまわないのですが、"Choose the type of app that you're building" は JavaScript を選びます。

続けて、プロジェクトに API を追加します。
% amplify add api
ほぼそのままです。スキーマを編集するか確認してくるので、Y でファイル編集画面を開き、以下内容を記述します。

type Sensor     @model
                @searchable
                @key(fields: ["id", "datetime"]) {
  id: ID!
  name: String!
  temperature: Float!
  humidity: Float!
  pressure: Float!
  datetime: AWSDateTime!
}
参考元の記事とは、"battery"を無くし"pressure"を追加しています。
完了したら、それを AWS に反映させます。
% amplify push
AWS でリソースが生成されるので、結構待ちます。が、Orange Pi で実行するとメモリ不足でエラーになることもありました。
この時は、以下のように環境変数を指定して push すればエラーを回避できました。
% NODE_OPTIONS=--max_old_space_size=1024 amplify push
このように、"All resources are updated in the cloud" と表示され、GraphQL の endpoint と API Key が表示されれば完了です。

Node.js からセンサー値を書き込む

参考記事とは違い、Node.js では i2c-bus を使用しているので、バージョンを切り替える必要なありません。Amplify を導入したバージョンの Node.js のままで作業を進めます。
まずはプロジェクトに Amplify Framework を追加します。
% npm i aws-amplify
先ほど初期設定した Amplify の構成をもとに、各種設定ファイルが生成されます。それらを使用して、その3で作成した TypeScript によるセンサー値を読み込むスクリプトに、Amplify を使用してデータを書き込む処理を追加していきます。

src/sensor.ts
import Amplify from 'aws-amplify'
import API, { graphqlOperation, GraphQLResult } from '@aws-amplify/api'
import * as Types from './API'
import { createSensor } from './graphql/mutations'
import { listSensors } from './graphql/queries'

import awsconfig from './aws-exports'
Amplify.configure(awsconfig)

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

const MAC_ADDRESS = '00:00:00:00:00:00'
const DEVICE_NAME = 'opi'
const INTERVAL = 1000 * 60 * 30

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);
        })
}

async function readBME280()
{
        const datetime = new Date().toISOString()
        let temperature = 0;
        let pressure = 0;

        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);

        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);

        // write param
        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]
        console.log(pres_raw, temp_raw, hum_raw);

        // 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;
        temperature = ((t_fine * 5) + 128) >> 8;

        // Refine pressure and adjust for temperature
        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);
        try {
                await mutation(MAC_ADDRESS, DEVICE_NAME, temperature / 100.0, humidity, pressure / 100.0, datetime)
        } catch (error) {
                console.log(error)
        }
}

setInterval(() => {
        readBME280();
}, INTERVAL)

async function mutation(id: string, name: string, temperature: number, humidity: number, pressure: number, datetime: string) {
        const input = {
                id: id,
                name: name,
                temperature: temperature,
                humidity: humidity,
                pressure: pressure,
                datetime: datetime,
        }
        const result = await API.graphql(graphqlOperation(createSensor, {input: input}))
        console.log(result)
}

AWS に関する設定項目がありませんが、import Awsconfig とかの行で Amplify Framework の方で用意してくれた設定を読み込んで構成しているので、スクリプトの方で気にすることは少なくて済みます。
あとはコンパイルして実行すると、30分おきにセンサー値を読み込んで、AWS に書き込んでくれます。
% npx tsc
% node dist/sensor.js
実行し続ける場合は、screen とかを使ってコンソールから切断しても動き続けられるようにした方がいいかもしれませんね。
これで、AWS のコンソールから DynamoDB を参照すれば、センサー値が書き込まれるのを確認できると思います。

コメント