kobayashi

Minecraft統合版(BE)に在・不在情報を表示してみたーEnOcean CPD-J(WH)

本記事では、OPTEX EnOcean ワイヤレス存在検知センサー(在席検知センサー) CPD-Jを使用して、在席状況をMinecraftに送信する方法を紹介しています。

開発環境

PC:Windows 11
※Raspberry Pi 4 Model B (Linux)
開発ツール:Node.js (v18.19.0)
      Node-RED (v.3.1.3)
インターネット:社内の無線LAN
Minecraft:v1.20.51 (執筆時点)

※Node-REDをRaspberry Piで動かしています。Windowsで動かしていれば不要です。

事前準備

EnOcean CPD-J の動作にはリチウム電池(CR123A)を1個使用します。
側面のネジをドライバーで開け、電池を差し込んでください。
(電池1個で約5年間寿命があるそうです。)

また、CPD-J の無線を拾うため、レシーバーとなる USB 400J を、
Node-REDを動かすデバイスに接続します。

完成像


動画のように、センサの前にいると数十秒後に在席中に、センサを外に向けると数十秒後に離席中になりました。
(下の温度はラズパイCPUの温度を表示しています)

フローの概要

フローは上の通りです。「serial in」ノードで USB 400J のシリアルポートを検索・接続し、enocean-decoederノードでデコードします。デコーダーはパレットから追加できます。

初めのSwitchノードには、CPD-J の固有IDで取得情報を絞ります。
IDは本体後ろまたはドライバで開けた中に記載されています。

次のSwitch ノードでは、在席・離席を判断するデータを指定しています。
何度か試行した結果、Data4に含まれる取得値が、頻繁に変化してしまったため、
変化しなかった文字列のみ要素に含むか否かで、判定をしています。

後ろに続く、UUID、functionノードは、以前の記事にて詳細を紹介してます。
今回はMinecraftのスコアボードに表示したいため、
表示切替のできるよう2つのコマンドをリクエストしています。

ノードデータ

[ { "id": "f15c2e3daa13cbc1", "type": "serial in", "z": "94835f73f480d5e8", "name": "", "serial": "", "x": 110, "y": 360, "wires": [ [ "e4af51c315e81fda" ] ] }, { "id": "e4af51c315e81fda", "type": "enocean-decoder", "z": "94835f73f480d5e8", "name": "", "x": 330, "y": 360, "wires": [ [ "50669bf26446908f" ] ] }, { "id": "50669bf26446908f", "type": "switch", "z": "94835f73f480d5e8", "name": "", "property": "payload.OriginatorID", "propertyType": "msg", "rules": [ { "t": "eq", "v": "05860dd1", "vt": "str" } ], "checkall": "true", "repair": false, "outputs": 1, "x": 510, "y": 360, "wires": [ [ "c319bc4718022b23" ] ] }, { "id": "c319bc4718022b23", "type": "switch", "z": "94835f73f480d5e8", "name": "", "property": "payload.Data4", "propertyType": "msg", "rules": [ { "t": "cont", "v": "000f", "vt": "str" }, { "t": "cont", "v": "746", "vt": "str" } ], "checkall": "true", "repair": false, "outputs": 2, "x": 650, "y": 360, "wires": [ [ "18fd0e0c30096e23", "8ce45f8a7af475e1", "ba0ec42c10945ce3" ], [ "5dbd309dfdd1fa50", "62255b46c4d4731d", "4b57a733ed26feae" ] ] }, { "id": "18fd0e0c30096e23", "type": "debug", "z": "94835f73f480d5e8", "name": "離席", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "statusVal": "", "statusType": "auto", "x": 910, "y": 240, "wires": [] }, { "id": "5dbd309dfdd1fa50", "type": "debug", "z": "94835f73f480d5e8", "name": "在席", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "statusVal": "", "statusType": "auto", "x": 910, "y": 480, "wires": [] }, { "id": "62255b46c4d4731d", "type": "uuid", "z": "94835f73f480d5e8", "uuidVersion": "v4", "namespaceType": "", "namespace": "", "namespaceCustom": "", "name": "", "field": "payout", "fieldType": "msg", "x": 910, "y": 400, "wires": [ [ "e3a28ca21a157f19" ] ] }, { "id": "e3a28ca21a157f19", "type": "function", "z": "94835f73f480d5e8", "name": "scoreboardにセット2", "func": "const uuid = msg.payout;\n\nconst commandRequestMessageJSON = {\n    \"header\": {\n        \"version\": 1, // プロトコルのバージョン1.18.2時点では1でOK\n        \"requestId\": uuid, // UUIDv4を生成して指定\n        \"messageType\": \"commandRequest\", // commandRequestを指定\n        \"messagePurpose\": \"commandRequest\", // commandRequestを指定\n    },\n    \"body\": {\n        \"origin\": {\n            \"type\": \"player\" // 誰がコマンドを実行するかを指定(ただし、Player以外にどの値が利用可能かは要調査)\n        },\n        \"version\": 1, // プロトコルのバージョン1.18.2時点では1でOK\n        \"commandLine\": \"scoreboard players set host:§b在席中 test 1\", // マイクラで実行したいコマンドを指定(ここではニワトリをスポーンさせるコマンドを指定)\n    }\n};\n\nmsg.payload = JSON.stringify(commandRequestMessageJSON);\n\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1100, "y": 400, "wires": [ [ "17c76117e65faa44" ] ] }, { "id": "48277ec9869a3af7", "type": "function", "z": "94835f73f480d5e8", "name": "scoreboardにセット", "func": "const uuid = msg.payout;\n\nconst commandRequestMessageJSON = {\n    \"header\": {\n        \"version\": 1, // プロトコルのバージョン1.18.2時点では1でOK\n        \"requestId\": uuid, // UUIDv4を生成して指定\n        \"messageType\": \"commandRequest\", // commandRequestを指定\n        \"messagePurpose\": \"commandRequest\", // commandRequestを指定\n    },\n    \"body\": {\n        \"origin\": {\n            \"type\": \"player\" // 誰がコマンドを実行するかを指定(ただし、Player以外にどの値が利用可能かは要調査)\n        },\n        \"version\": 1, // プロトコルのバージョン1.18.2時点では1でOK\n        \"commandLine\": \"scoreboard players set host:§c離席中 test 1\", // マイクラで実行したいコマンドを指定(ここではニワトリをスポーンさせるコマンドを指定)\n    }\n};\n\nmsg.payload = JSON.stringify(commandRequestMessageJSON);\n\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1100, "y": 280, "wires": [ [ "17c76117e65faa44" ] ] }, { "id": "8ce45f8a7af475e1", "type": "uuid", "z": "94835f73f480d5e8", "uuidVersion": "v4", "namespaceType": "", "namespace": "", "namespaceCustom": "", "name": "", "field": "payout", "fieldType": "msg", "x": 910, "y": 280, "wires": [ [ "48277ec9869a3af7" ] ] }, { "id": "17c76117e65faa44", "type": "websocket out", "z": "94835f73f480d5e8", "name": "", "server": "c838b41f45982027", "client": "", "x": 1340, "y": 360, "wires": [] }, { "id": "4b57a733ed26feae", "type": "uuid", "z": "94835f73f480d5e8", "uuidVersion": "v4", "namespaceType": "", "namespace": "", "namespaceCustom": "", "name": "", "field": "payout", "fieldType": "msg", "x": 910, "y": 440, "wires": [ [ "71b87a59800672cd" ] ] }, { "id": "71b87a59800672cd", "type": "function", "z": "94835f73f480d5e8", "name": "scoreboardをリセット2", "func": "const uuid = msg.payout;\n\nconst commandRequestMessageJSON = {\n    \"header\": {\n        \"version\": 1, // プロトコルのバージョン1.18.2時点では1でOK\n        \"requestId\": uuid, // UUIDv4を生成して指定\n        \"messageType\": \"commandRequest\", // commandRequestを指定\n        \"messagePurpose\": \"commandRequest\", // commandRequestを指定\n    },\n    \"body\": {\n        \"origin\": {\n            \"type\": \"player\" // 誰がコマンドを実行するかを指定(ただし、Player以外にどの値が利用可能かは要調査)\n        },\n        \"version\": 1, // プロトコルのバージョン1.18.2時点では1でOK\n        \"commandLine\": \"scoreboard players reset host:§c離席中 test\", // マイクラで実行したいコマンドを指定(ここではニワトリをスポーンさせるコマンドを指定)\n    }\n};\n\nmsg.payload = JSON.stringify(commandRequestMessageJSON);\n\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1110, "y": 440, "wires": [ [ "17c76117e65faa44" ] ] }, { "id": "ba0ec42c10945ce3", "type": "uuid", "z": "94835f73f480d5e8", "uuidVersion": "v4", "namespaceType": "", "namespace": "", "namespaceCustom": "", "name": "", "field": "payout", "fieldType": "msg", "x": 910, "y": 320, "wires": [ [ "5bf7a95e4ab0a66f" ] ] }, { "id": "5bf7a95e4ab0a66f", "type": "function", "z": "94835f73f480d5e8", "name": "scoreboardをリセット", "func": "const uuid = msg.payout;\n\nconst commandRequestMessageJSON = {\n    \"header\": {\n        \"version\": 1, // プロトコルのバージョン1.18.2時点では1でOK\n        \"requestId\": uuid, // UUIDv4を生成して指定\n        \"messageType\": \"commandRequest\", // commandRequestを指定\n        \"messagePurpose\": \"commandRequest\", // commandRequestを指定\n    },\n    \"body\": {\n        \"origin\": {\n            \"type\": \"player\" // 誰がコマンドを実行するかを指定(ただし、Player以外にどの値が利用可能かは要調査)\n        },\n        \"version\": 1, // プロトコルのバージョン1.18.2時点では1でOK\n        \"commandLine\": \"scoreboard players reset host:§b在席中 test\", // マイクラで実行したいコマンドを指定(ここではニワトリをスポーンさせるコマンドを指定)\n    }\n};\n\nmsg.payload = JSON.stringify(commandRequestMessageJSON);\n\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1100, "y": 320, "wires": [ [ "17c76117e65faa44" ] ] }, { "id": "c838b41f45982027", "type": "websocket-listener", "path": "ws", "wholemsg": "false" } ]