【RaspberryPi】Node-REDでローカル環境にあるデータベースSQLiteを使用する

Node-REDでもデータベース(以下DB)を使ってみたいなと思いました。実はNode-REDでは使えるDBが豊富にあります。 結構時期的には昔のエントリーですが、この時点でもかなり豊富な選択肢を選べます。

【参考】 qiita.com

この中で、RaspberryPiでローカルに使用できることを考えるとSQliteあたりがいいのかなと思いました。(難点としてはSDカードへのアクセスは遅い点ですが…)

拡張ノードとしては以下のnode-red-node-sqliteを使用していきます。SQLiteで拡張ノードを検索するともう一つnode-red-contrib-sqlitedbがでてきますが、こちらはクラウド上のDBへのアクセスを行うもののようなので、今回はnode-red-node-sqliteを扱うことにします。

flows.nodered.org

インストール時に少しだけ引っかかる点があるのでご注意ください。(ただし、RaspberryPi固有の可能性もあります)

今後は以下の書籍も参考しています。購入してみましたが非常に内容が良くて理解の助けになります!多分これが大概のことが達成できる人も多いかなと覆いますよ。

拡張ノードnode-red-node-sqliteをインストールする

基本はその他の拡張ノード同様です。 画面の右上にある【三】ボタンをクリックして、プルダウンメニューから【パレットの管理】を選択してクリックします、。

f:id:ueponx:20191030081342p:plain

ユーザー設定の表示が行われたら、【ノードの追加】タブをクリックし、検索のテキストボックスにnode-red-node-sqliteを入力して検索を行います。 1件に候補が絞られますので、【ノードの追加】ボタンをクリックします。

f:id:ueponx:20191030081359p:plain

インストール確認のダイアログが表示されるので、【追加】ボタンをクリックして処理を進めます。

f:id:ueponx:20191030081405p:plain

これでインストール作業が開始されるのですが、数分ほどすると処理進行の処理が止まり、もとの画面に戻ってしまいます。拡張ノードにも追加されている気配はありません。実はバックグラウンドでインストール作業は行われています。

f:id:ueponx:20191030081411p:plain

一応、ドキュメントにも

Note: the install process requires a compile of native code. This can take 15-20 minutes on devices like a Raspberry Pi - please be prepared to wait a long time.

と注記がされていますが、UIの表示でこういうことが起こるとは書いていないので注意かもしれません。

f:id:ueponx:20191030081502p:plain

実際にはどうなっているかというと、ログ上はインストール開始されていること表示されています。

f:id:ueponx:20191030081509p:plain

プロセスを眺めてみると(topコマンドで確認)、ネイティブコードのビルド処理がされていることがわかります。ここでいうと`CPU使用率99.7%となっている``cc1```のプロセスが該当します。インストール時はRaspberryPiの処理はものすごく遅くなっています。

f:id:ueponx:20191030081518p:plain

15分ほど待つとインストール完了のダイアログがWeb画面上にも表示されます。これでインストールは完了となります。

f:id:ueponx:20191030081531p:plain

インストール前には【ノードの追加】となっていたボタンが【追加しました】の表示に変わっていると思います。

f:id:ueponx:20191030081548p:plain

パレット(画面左側のノードがある部分はこのように呼ぶようです。最近知ったw)一覧のストレージカテゴリーにもsqliteノードが追加されています。

f:id:ueponx:20191030081559p:plain

ログも念の為確認するとノードが追加されたことがわかります。

f:id:ueponx:20191030081721p:plain

これでインストールは完了です。 注意点としてはこのインストールではSQLiteCLIは導入されないので注意が必要かもしれません。

実際にSQLiteのローカルデータベースを使用してみる

あとは基本的にはSQLiteの使い方に近いのですが、一応やってみます。 これまでのノードとは異なり、入力時には送信されるJSONのtopicがクエリとして使用されます。 injectノードではトピックもUIとして表示されているのでこちらに入力していくことになります。

f:id:ueponx:20191106084510p:plain

テーブルの作成

では、動作テストを行っていきます。まずはテーブルを作成していこうと思います。 以下3つのノードをワークフローに配置していきます。

  • injectノード
  • sqliteノード
  • debugノード

こんな感じになると思います。

f:id:ueponx:20191104165226p:plain

今回扱う処理の基本の処理はinjectノードでクエリやコマンドを生成、sqliteノードで処理を行い、結果をdebugノードで表示となります。

まずは、injectノードのクリックしてプロパティを編集していきます。これまでは【ペイロード】を編集していましたが、sqliteノードでは【トピック】を使用します。【トピック】のテキストボックスに以下のクエリを入力します。

create table tbl1(one varchar(10), two smallint);

今回の例では2つのカラムから構成されたデータとしています。1つ目は10文字の文字列、2つ目は2バイトの整数で構成しています。

【名前】の値にもテーブルの作成と入力しましょう。今回はinjectノードが多いので…多くなくても名前は入れたほうがいいですけど。

入力が終わったら【完了】ボタンをクリックします。

f:id:ueponx:20191105221806p:plain

続いて、sqliteノードをクリックして、プロパティを編集していきます。初回はデータベースのファイル名の設定が必要になります。これが終わるとsqliteノード側がクエリを受信すると自動的に処理を行ってくれるので、編集の必要はなくなります。

プロパティ画面の【Database】のプルダウンが、ありますが初期段階では「新規にsqlitedbを追加…」となっていますが、ここにデータベースファイルを入れていきます。プルダウンの隣りにある【編集】ボタンをクリックすると【sqlitedbノードを編集】画面が表示されます。

f:id:ueponx:20191105222353p:plain

【sqlitedbノードを編集】画面の【Database】のテキストボックスにデータベースファイルのパスを入力し、【Mode】の項目もプルダウンから適切なものを選ぶことになりますが、今回はRead-Write-Createのモードを設定しています。設定が終わったら右上の【更新】ボタンをクリックして(sqliteノードの編集の)プロパティ画面に戻ります。

f:id:ueponx:20191105222937p:plain

【Database】のフィールドに指定したパスが格納されていればOKです。あとは名前のフィールドにコマンドの実行と入力しておきます。

f:id:ueponx:20191105223643p:plain

ここまでの編集でフローが以下のようになっていると思います。

f:id:ueponx:20191105082752p:plain

あとは各ノードの端子を結んでいきます。

f:id:ueponx:20191105082800p:plain

ここまできたら【デプロイ】ボタンをクリックします。これで準備は完了です。

f:id:ueponx:20191105082830p:plain

では実行してみましょう。テーブル作成のinjectノードの左側のボタンをクリックしてみると…

f:id:ueponx:20191105224527p:plain

デバックタブにはemptyという風には表示されていますが、ちゃんとテーブルは作成されています。データベースのファイルも指定したパスに生成されています。

f:id:ueponx:20191105224718p:plain

これでデータを格納するテーブルが生成されました。

生成したテーブルにデータを格納する

続いてはデータの格納を行います。ワークフローに2つのinjectノードを配置し、

f:id:ueponx:20191105225005p:plain

それぞれにデータを格納するクエリを書いていきます。基本的にはテーブルの生成と同じように設定していくことになりますので少し説明は省きます。プロパティの【名前】と【トピック】を編集していきます。

1つ目の要素の登録のノード】 f:id:ueponx:20191105231040p:plain

2つ目の要素の登録のノード】 f:id:ueponx:20191105231045p:plain

編集が終わったら、以下の画面のようにノードを接続していきます。割と接続の様子がこんな感じのフローになるのが多いのがNode-REDっぽい感じでもあります。

f:id:ueponx:20191105231511p:plain

ここまでできたら【デプロイ】ボタンをクリックします。エラーがなければそれぞれ実行されていきます。

f:id:ueponx:20191105231843p:plain

f:id:ueponx:20191105232000p:plain

とくに問題なく実行できました。エラーが起きてないだけでデータの格納が行われたことは確認できません。今度はデータの取り出しを行っていきます。

テーブルからデータを取り出す

これまでと同様にinjectノードをワークフローに配置し、

f:id:ueponx:20191105232408p:plain

プロパティの編集を以下のように行います。(名前トピックのフィールドを編集)終わったら【完了】ボタンをクリックします。

f:id:ueponx:20191105232159p:plain

フロー画面に戻ったら

f:id:ueponx:20191105232202p:plain

ノード間を結び、【デプロイ】ボタンをクリックします。

f:id:ueponx:20191105232205p:plain

実行すると値は各エントリーごとにオブジェクトの配列として返されるので、デバッグ画面を操作して取り出したデータの確認を行います。

f:id:ueponx:20191105233539p:plain

問題なくデータを取り出すことができました。

テーブルの削除

ここまでて一通りのことはやったので終わってもいいのですが、テーブルの削除も念の為行ってみます。

injectノードを配置して

f:id:ueponx:20191105233859p:plain

プロパティを編集し、

f:id:ueponx:20191105233909p:plain

ノードを接続して、【デプロイ】ボタンをクリックします。

f:id:ueponx:20191105233917p:plain

後は実行でエラーが出なければ完了となります。

f:id:ueponx:20191105234131p:plain

テーブルの削除処理は基本的には必要はありません。実験のレベルとしてはテーブルの作成後に、更に同名のテーブルを作成しようとするとエラーが発生するので必要なこともあるでしょう。(本当か?)

おわりに

RaspberryPiでローカルにデータベースを保つ場合には比較的SQLiteを使用するのは有り得る話かなと思います。機能としてもCLIを叩くような感じで実行できるので、比較的理解しやすかったのではないかなと思います。

ただ、リモートDBに接続するとなると、選択肢は増えますがお手軽さからするとkintoneかなと思います。WebのUI関連をkintoneを任せることもできますし。Node-REDにはkintoneの拡張ノードもあります。データの格納まではRaspberryPi、データの可視化などはkintoneという風に分業もできると思います。

ここまでで、データベースまで格納できるようになったので、次はTwitterあたりのノードを触ってみたいと思います。


【今回作成したフロー】

[
    {
        "id": "2f3780d4.eadb4",
        "type": "tab",
        "label": "フロー 1",
        "disabled": false,
        "info": ""
    },
    {
        "id": "f591ad2a.5511",
        "type": "debug",
        "z": "2f3780d4.eadb4",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 550,
        "y": 80,
        "wires": []
    },
    {
        "id": "38caa3c0.1e6eec",
        "type": "inject",
        "z": "2f3780d4.eadb4",
        "name": "テーブルの作成",
        "topic": "create table tbl1(one varchar(10), two smallint);",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 120,
        "y": 80,
        "wires": [
            [
                "ef7b3bc4.8a1f68"
            ]
        ]
    },
    {
        "id": "ef7b3bc4.8a1f68",
        "type": "sqlite",
        "z": "2f3780d4.eadb4",
        "mydb": "56879991.d56508",
        "sqlquery": "msg.topic",
        "sql": "",
        "name": "コマンドの実行",
        "x": 340,
        "y": 80,
        "wires": [
            [
                "f591ad2a.5511"
            ]
        ]
    },
    {
        "id": "3360e990.2bf7e6",
        "type": "inject",
        "z": "2f3780d4.eadb4",
        "name": "1つ目の要素の登録",
        "topic": "insert into tbl1 values('hello!',10);",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 130,
        "y": 140,
        "wires": [
            [
                "ef7b3bc4.8a1f68"
            ]
        ]
    },
    {
        "id": "37f6b040.824ef",
        "type": "inject",
        "z": "2f3780d4.eadb4",
        "name": "2つ目の要素の登録",
        "topic": "insert into tbl1 values('goodbye', 20);",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 130,
        "y": 200,
        "wires": [
            [
                "ef7b3bc4.8a1f68"
            ]
        ]
    },
    {
        "id": "6f32dcb6.0e9d14",
        "type": "inject",
        "z": "2f3780d4.eadb4",
        "name": "テーブル内のデータの表示",
        "topic": "select * from tbl1;",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 150,
        "y": 260,
        "wires": [
            [
                "ef7b3bc4.8a1f68"
            ]
        ]
    },
    {
        "id": "3593304c.3c048",
        "type": "inject",
        "z": "2f3780d4.eadb4",
        "name": "テーブルの削除",
        "topic": "drop table tbl1;",
        "payload": "",
        "payloadType": "date",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 120,
        "y": 320,
        "wires": [
            [
                "ef7b3bc4.8a1f68"
            ]
        ]
    },
    {
        "id": "56879991.d56508",
        "type": "sqlitedb",
        "z": "",
        "db": "/home/pi/example.db",
        "mode": "RWC"
    }
]