Raspberry Pi 2Bサーバ/Debian BookwormとESP32+温湿度センサー、MQTT、Node.js+JavaScriptとRDBの1つPostgreSQLで温度・湿度(・気圧)の値及びCSSによるグラフを一覧表示、モニタリングできるようにしてみた話。
PCブラウザだと、こんな感じ。
スマホブラウザで縦だと、こんな感じ。
スマホブラウザで横だと、こんな感じ。
以前から室内各所の温度や湿度の違いや天井と床との温度差、室外と室内の温度や湿度の差などは気になっており、エアコンの自動制御まではやるつもりはありませんが、エアコンをつけるかどうかの判断材料としても良いかなと。
Raspberry Pi 2Bサーバ/Debian Bookworm+MosquittoをMQTTブローカーとし、屋内・屋外各所に配置のESP32+温度・湿度(、気圧)センサー測定都度、MQTT PublishされたデータをNode.js+JavaScriptデーモンでMQTT Subscribeしつつ、RDB(PostgreSQL)に登録、別途、Node.js+JavaScriptデーモンによるWebサーバにブラウザでアクセス都度、各所最新データを1件ずつ取得、データ値及びCSSによるグラフを一覧できるようにします。
ちなみに高/快適/低っぽく温度帯や湿度帯によってグラフ色も変化。
Node.jsの機能的には、MQTT Subscribe、WebSocketで直接受けてブラウザに表示...ということもできました。
が、今回の場合、Publishする温湿度センサーデバイスは1つではなく、多数あり、通信は無線、よって軽量で短時間で一方のみならず双方向に通信でき、不安定な環境でクライアントが切断、再接続といったケースでも再送できたり、メッセージ到達品質を選択できたりといった術があるMQTTを採用することに。
また、最初にデバイスの電源を投入したタイミングや通信ラグなども想定され、同時に全てSubscribeしてブラウザで一覧表示...というわけにはいかないので各所それぞれの最新データを取得できるようにRDBMSも使うことに。
ラズパイサーバをMQTTブローカーにするに当たり、ここでは、Mosquittoをインストールします。
尚、mosquitto-clientsもインストールしておくと端末でSubscribeやPublishすることもでき、以後の作業を行う前でもESP32からMQTT Publishされたデータを確認することもできます。
今日時点、Debian Bookwormでは、mosquittoの設定ファイルは、MQTTに限らず、追加設定用として推奨される任意のファイル名+拡張子[.conf]を配置する/etc/mosquitto/conf.d/ディレクトリとベースとなる/etc/mosquitto/mosquitto.confがあります。
というわけで/etc/mosquitto/conf.d/base.confという名のファイルを配置することにして通信可能なポート指定としてMQTTデフォルトポート1883と匿名で接続を許可する設定としました。
ここではセキュリティを十分に考慮しておらず、必要ならallow_anonymousはfalseに設定、認証必須とします。
尚、サーバ以外の他のデバイスからアクセスしたい場合、ファイアウォールが有効なら、ファイアウォール設定でもMQTTポートを通信許可しておく必要があります。
Node.jsを利用する際のお約束行事。
Node.jsの準備が済んでいる前提で、今回、各所に配置のESP32+温湿度モジュールからMQTT PublishされたデータをMQTTブローカーとしたラズパイサーバでSubscribe、PostgreSQLに登録するNode.js + JavaScriptデーモンを作成していきます。
Node.jsでPostgreSQLを利用したいのでnode-postgresをインストールします。
尚、iは、installでも可。
node-postgresをインストールした時点でデフォルトユーザーは、postgresですが、併せてcreateuserコマンドもインストールされており、ユーザーの追加と同時に各種権限の設定もできます。
ここでは、ホスト名、ポート番号、createuserコマンドを実行するユーザー、DB作成権限、ログイン権限、スーパーユーザー権限を付与しています。
ここで[-P](大文字)を指定した場合、DB接続時にパスワードが必要になります。
尚、後述の作成済みDB接続後にSQLでCREATE USERすることでもユーザーを追加することができます。
node-postgresをインストールした時点でcreatedbやdropdbコマンドもインストールされており、RDB名を渡すとPostgreSQL用データベースの作成、削除ができます。
必要なら実行後、パスワードを入力します。
また、PostgreSQL対話式クライアントpsqlコマンドもインストールされており、RDB名を渡すとRDBに接続できます。
RDBからログアウトしたい場合は、[Ctrl]+[d]で。
RDBに接続したら、CREATE、SELECT、UPDATE、INSERT、DELETEなど一連のRDB操作ができるようになります。
ここでは、今回使用するclient_listテーブルとtemperature_humidityテーブルを作成、前者は、予め必要データをINSERTしておきます。
client_listテーブルは、主キーがclientidで短縮名とフルネーム用のカラムがあり、temperature_humidityテーブルのclientidと紐づけるためにあります。
temperature_humidityテーブルのプライマリキーは、clientidとcreated_atでTIMESTAMP型の後者は、データ登録(INSERT)時のタイムスタンプがデフォルトで自動登録、MQTT Topicテキストと全体4桁、小数点以下2桁の数値な温度と湿度の列から成っています。
ちなみに気圧は省きました。
PostgreSQLのDBとテーブルを作成したのでMQTT SubscribeしたデータをINSERTする準備は整いました。
MQTT Topicは、今回、こんな感じで運用するものとします。
また、[(一意な)場所]は、[client_list]テーブルのカラム[name]と一致するものとします。
そしてプログラムは、こんな感じ。
常にMQTTポートを監視して都度、MQTT Subscribeする内容は、前述の通り、[temp_humi/(複数可な)区分/(一意な)場所]から成る[topic]、また、温度と湿度がスペース区切りな[message]から成っており、[(一意な)場所]は、[client_list]テーブルのカラム[name]と一致する前提となっています。
スラッシュ区切りのTopicから[(一意な)場所]、空白区切りの[message]から得た[温度]と[湿度]でPostgreSQLに接続、アクセス。
[(一意な)場所]=client_listのnameでSELECTしたclientid、引数からtopicと[温度]と[湿度]をINSERT。
temperature_humidityをSELECT COUNTして全数を得て、一定数を超えたらDELETE TABLE。
ファイル名が[mqtt_data_insert_rdb.js]なら、このようにすれば、一時起動でき、MQTT Publishがあれば、MQTT Subscribe、DBにINSERTされます。
良さ気ならsystemdユニットファイルを作成。
systemdユニットファイルで呼んでいるファイル。
なんとなく、shellファイルが相性良さそうと思えたことがあって以来、ラッパ。
バッチリできたら、ユニットファイルに実行権限を付与、systemd/systemctlでstartで一時開始、enableで永続化。
続いてWebサーバアドレス+特定ポートにアクセスした場合、PostgreSQLに登録済みの温湿度データの内、MQTT Topicごとの最新データを各1件取得、Topic単位でデータ値とデータ値を基にした円グラフをCSSで描画するデーモンを、やはり、Node.js + JavaScriptで実装していきます。
Node.jsの準備が済んでいる前提でexpress、path、pg(node-postgres)を利用したいのでインストールします。
expressは、Webサーバ用、pathはパスの階層構造指定用、pg(node-postgres)は、PostgreSQLアクセス用です。
わざわざインストールしなくともNode.jsには、標準Webサーバとしてhttpというものがあるのですが、なんとなく。
もちろん、同一プロジェクトディレクトリ内で既にインストール済みなもの(npm listやpackage.jsonで列挙されているもの)は、改めてインストールする必要はありません。
プログラムは、こんな感じ。
仮に9001ポートでサーバ起動、ホストはlocalhostとしていますが、サーバがIP固定してあれば、何を調べるまでもなく、LAN内からIPアドレス:9001で(Androidでなければ、mDNSを効かせておけばHOSTNAME.local:9001等でも)アクセスできます。
ただし、ファイアウォールが有効なら、ファイアウォール設定でも前述のMQTTポートも含め、Webサーバ用の9001ポートを開放(後、マシンを再起動)しておく必要がありますが。
当該ポートにアクセスすると[temp_humi/(複数可な)区分/(一意な)場所]から成る[topic]の内、[(一意な)場所](client_listのname)ごとに最新データを1件取得、[name]、[温度]、[湿度]の値と、それらを元にしたグラフをCSSで表示します。
尚、昔からレイアウトには使ってくれるなよと注意喚起されていたものの、当初、楽だからと試したtableタグだとグラフをうまく収められなかったため、spanタグ、divタグのfloatの設定・解除で並べることにしました。
CSSの外部ファイル。
Webサーバについては、expressの標準的なもの、CSSでグラフについては、Dounut PieChart/ドーナツ型の円グラフに倣いました。
(JavaScriptの)backgroundImage(htmlではbackground-image)に画像ファイル以外を設定できるとは知りませんでした。
グラデーション設定でなんでこうなるのか理解しきれていませんが、試してみるとbackgroundImageのconic-gradientが、グラフ値に相当する帯?部のカラー設定だったので湿度は、値そのまま、温度は、仮に50度を100%として(ブラウザのコンソールで見たら、稀に1つだけ小数点以下桁数が10桁くらいあるのもあって気分的に小数点以下を四捨五入して丸めたものを)設定。
ハマったのは、温湿度ともに快適度を示すべく温度帯と湿度帯で色を変えるためのJavaScriptのif文での温度や湿度の範囲設定...、未だに明確には理解できていませんが、どこに起因するのか、範囲指定によっては、うまく適用されなかったり、外部CSSで設定した部分を判定に入れた途端、おかしなことになったり...ブラウザを替えても同様で、?がたくさんな謎現象に悩まされました。
ファイル名が[temp_humi_monitor]なら、このようにすれば、一時起動できます。
良さ気ならsystemdユニットファイルを作成。
systemdユニットファイルで呼んでいるファイル。
なんとなく、shellファイルが相性良さそうと思えたことがあって以来、ラッパースクリプト。
いけると確信もてたら、ユニットファイルに実行権限を付与、systemd/systemctlでstartで一時開始、enableで永続化。
前述のようにファイアウォールが有効でLAN内の他デバイスからアクセスしたい場合は、MQTTサーバ、Webサーバなど、必要な通信ポートを開放(、必要なら再起動)しておく必要があります。
ufwなら、こんな感じで。
できたところでESP32+Webサーバでブラウザ版の自作スマートホームコントロールパネルに温湿度モニタを追加しました。