Node-REDからCoolMasterにBACnetでON/OFFする方法

Node-REDのnode-red-contrib-bacnetを使って、CoolMasterのON/OFFを行う方法を調べます。

YABEのデータ

YABEを使ってCoolMasterを調べると、

L1.101 on/off(BV:256)がオンオフを制御するデータポイントのようです。

Present Valueが1となっていて、現在稼働中です。

Present Valueを0にすることでCoolMasterに接続された空調機(旧東芝キャリア)の電源もオフになりました。

Node-REDからON/OFFの状態を取得する

このようなフローを作成します。

TypeにはBV(BinaryValue)を意味する5

InstanceはBV:256に設定。

Deviceha接続するCoolMasterのIPアドレス。Client はNode-REDを動かすPCのIPを入れます。

実行すると

ValuesのValueが1で空調機が動いていることを確認。

空調の電源をオフにして再度実行。

Valueが0になりオフになったことが確認できました。

Node-REDからBACnetのONをにしてみる

BACnet WriteノードをつかってONにするフローを作成しました。

プロパティはReadと同じように設定。

このようにしたところ

型が合わないというエラーが出ました。

ON/OFFはBooleanだと思って

App-Tagを1にしたのですが、違うようです。

WireSharkで確認

YABEを使ってL1.101 on/off(BV256)を0にしてWiresharkでパケットを取得します。

Wireshark側ではこのようにパケットがキャプチャされました。

Frame 202: 65 bytes on wire (520 bits), 65 bytes captured (520 bits) on interface \Device\NPF_{0F0CA621-1947-4E3A-81CB-D5506A2CE640}, id 0
    Section number: 1
    Interface id: 0 (\Device\NPF_{0F0CA621-1947-4E3A-81CB-D5506A2CE640})
    Encapsulation type: Ethernet (1)
    Arrival Time: Jun  9, 2025 18:54:29.258290000 東京 (標準時)
    UTC Arrival Time: Jun  9, 2025 09:54:29.258290000 UTC
    Epoch Arrival Time: 1749462869.258290000
    [Time shift for this packet: 0.000000000 seconds]
    [Time delta from previous captured frame: 0.010301000 seconds]
    [Time delta from previous displayed frame: 0.000000000 seconds]
    [Time since reference or first frame: 9.533515000 seconds]
    Frame Number: 202
    Frame Length: 65 bytes (520 bits)
    Capture Length: 65 bytes (520 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: eth:ethertype:ip:udp:bvlc:bacnet:bacapp]
    [Coloring Rule Name: UDP]
    [Coloring Rule String: udp]

このデータをChatGPTに開設してもらいます。

Frame 202: 65 bytes on wire (520 bits)

  • このパケットは物理層で 65バイト(520ビット) です。
  • イーサネットの最小パケットサイズ(60バイト)に近いので、制御や短いデータ(小さいリクエストパケット)の可能性があります。

🟢 Interface id: 0 (\Device\NPF_{…})

  • このパケットは、Wiresharkが使用しているネットワークインターフェイス(例: 有線LAN、仮想ネットワークカード)上でキャプチャされました。
  • そのIDが 0F0CA621-1947-4E3A-81CB-D5506A2CE640 です。

🟣 Arrival Time

  • ローカル時間(東京標準時): 2025年6月9日 18:54:29.258
  • UTC時間: 2025年6月9日 09:54:29.258
  • 参考:Epoch(秒数): 1749462869.258

これは解析時にタイムラインを確認するのに便利です。


🔵 Frame Length / Capture Length

  • 65 bytes = 520 bits
    • Frame Length: パケット全体の物理長
    • Capture Length: Wiresharkが実際に取り込んだ長さ(ここでは同じ)

🟡 Protocols in frame

  • eth:ethertype:ip:udp:bvlc:bacnet:bacapp
    • Ethernet → IP → UDP → BACnet/IP(bvlc)→ BACnet Application(bacapp)
    • Node-REDのBACnet-Writeノードで送信したか、デバイスが応答したBACnet/IPパケットだと思われます。

Building Automation and Control Netwokr NPDUについて確認します。

🔎 NPDUとは?

NPDU(Network Protocol Data Unit) は、BACnetプロトコルスタックの中で「ネットワーク層」に相当する部分のデータ単位です。
BACnet/IPやMSTPなど、BACnetの通信プロトコルで必ず使用されるデータ構造で、BACnetパケットの配送先(アドレス情報)や制御フラグ、後続のAPDU(Application Protocol Data Unit)を包含します。

BACnetはOSI参照モデルをベースに設計されていて、簡略化するとこんなイメージです。

項目内容
バージョンBACnetプロトコルのバージョン番号(通常1)
制御フラグメッセージの種別(例: ネットワーク層だけ or アプリケーション層まで含む)
DNET(宛先ネットワーク)宛先BACnetネットワーク番号(ルーター経由の場合)
DLEN(宛先MAC長)宛先MACアドレス長
DADR(宛先MACアドレス)宛先MACアドレス(BACnet/IPならIP+ポート)
SNET(送信元ネットワーク)送信元BACnetネットワーク番号
SLEN(送信元MAC長)送信元MACアドレス長
SADR(送信元MACアドレス)送信元MACアドレス
Hop Countルーティングのホップ数(デフォルト255)
APDUBACnet Application Protocol Data Unit(BACnetの実データ)

🛠️ NPDUの役割

  • ルーティング: BACnet/IPだけでなく、MSTPやARCNETなど異なるネットワーク間でもBACnetルーターでNPDUを中継します。
  • アドレス情報: 宛先や送信元のBACnetネットワーク情報を含めることで、異なる物理ネットワーク間でBACnet通信が可能になります。
  • アプリケーション層の包み紙: NPDUの中にAPDU(データ操作コマンドやオブジェクトデータ)を格納して送信します。

以上のようにNPDUはネットワークに関するデータのようなので、先ほどのNode-REDで発生したエラーは次のAPDUを調べればわかりそうです。

APDUについて

Building Automation and Control Network APDU
    0000 .... = APDU Type: Confirmed-REQ (0)
    .... 0000 = PDU Flags: 0x0
        .... 0... = Segmented Request: Unsegmented Request
        .... .0.. = More Segments: No More Segments Follow
        .... ..0. = SA: Segmented Response not accepted
    .111 .... = Max Response Segments accepted: Greater than 64 segments (7)
    .... 0101 = Size of Maximum ADPU accepted: Up to 1476 octets (fits in an ISO 8802-3 frame) (5)
    Invoke ID: 13
    Service Choice: writeProperty (15)
    ObjectIdentifier: binary-value, 256
        Context Tag: 0, Length/Value/Type: 4
        0000 0001 01.. .... .... .... .... .... = Object Type: binary-value (5)
        .... .... ..00 0000 0000 0001 0000 0000 = Instance Number: 256
    Property Identifier: present-value (85)
    {[3]
    Present Value (enum index): 0
        Application Tag: Enumerated, Length/Value/Type: 1
    }[3]
        .... 1... = Tag Class: Context Specific Tag
        0011 .... = Context Tag Number: 3
        .... .111 = Named Tag: Closing Tag (7)
    Priority: (Unsigned) 16
        Context Tag: 4, Length/Value/Type: 1
            .... 1... = Tag Class: Context Specific Tag
            0100 .... = Context Tag Number: 4
            Length Value Type: 1

このAPDUはBACnetクライアントからデバイスに送信される「writeProperty」リクエストです。

📦 まず全体像

このパケットはBACnet/IP通信でよく見られる Confirmed-REQ (0)(アプリケーション層の確認応答付きリクエスト)です。
サービス要求内容は「binary-valueオブジェクト(インスタンス256)のpresent-valueを書き込みたい」です。

🔹 APDU Type: Confirmed-REQ (0)

  • 意味: BACnetのAPDUタイプは4種類あります。
    • 0: Confirmed Request(応答を期待する)
    • 1: Unconfirmed Request(応答不要)
    • 2: SimpleACK(成功応答)
    • 3: ComplexACK(成功応答・分割)
    • 4: SegmentACK(分割応答)
    • 5: Error(エラー応答)
    • 6: Reject(拒否応答)
    • 7: Abort(中止応答)
  • ここでは Confirmed Request なので、デバイスからACKが必要です。

ここにConfirmedRequestが表示されています。

🔸 PDU Flags: 0x0

各ビットの意味:

  • Segmented Request: Unsegmented(分割なし)
  • More Segments: No More Segments Follow(最後のセグメント)
  • SA: Segmented Response not accepted(分割応答不要)

🔹 Max Response Segments accepted: 7

  • クライアントがデバイスに対して「最大何個のセグメントなら受け取れるか」を伝えています。
  • 7 → 「64セグメント以上OK」

途中いろいろ省略

項目内容
APDU TypeConfirmed Request(応答必要)
ServicewriteProperty(プロパティ書き込み)
Objectbinary-value, Instance=256
Propertypresent-value(85)
Value0(inactive)
Application TagEnumerated(整数型)
Priority16(最低優先度)

という状況でした。

Node-REDのコマンドをWireSharkでキャプチャ

コマンドをおくりました。

項目内容
送信元192.168.1.14(Node-REDやBACnetクライアント)
送信先192.168.1.100(BACnetデバイス)
プロトコルBACnet/IP(UDP 47808)
BVLC FunctionOriginal-Unicast-NPDU(単一デバイス宛)
APDU TypeConfirmed Request(応答が必要)
サービスwriteProperty(プロパティ書き込み)
オブジェクトbinary-value(256)
書き込み先present-value(85)
書き込み値True(bool)
Priority16

⚠️ 注意点

もしここでBACnetデバイスから Error Class: PROPERTY (2) Code: INVALID_DATA_TYPE (9) のようなエラーが返る場合、原因として:

  • 実は binary-value.present-value が Enumerated(0 or 1)として実装されているデバイスの場合。
    • → Application Tag を Enumerated(App-Tag=9)にしなければならない。
  • デバイスがBoolean型をサポートしていない場合。
    • → Application Tag を変更する必要がある。

とのメッセージ。

App-Tagを9:ENUMERATEDに変更して実行

無事に空調機をBACnetからONにすることができました。

Node-REDからBACnetでCoolMasterのON/OFFをする方法

単純にON/OFFするには、App-Tagを9にして、Valueを0と1のBACnet Writeノードをつなげればよい。

でも、これはValueで渡したいですよね。

Node-REDからValueで渡してBACnetでON/OFFする方法

[{"id":"a3ae6e1135ec2c44","type":"BACnet-Write","z":"11b5d970f1ab28c1","name":"","objectType":"5","instance":"d6dff7e9f9703782","valueTag":"9","valueValue":"","propertyId":"85","priority":"16","device":"54de1b76e537f00b","server":"cd9b93738b4a729c","multipleWrite":false,"x":620,"y":1000,"wires":[["debug"]]},{"id":"e740e03b5766af4e","type":"function","z":"11b5d970f1ab28c1","name":"Format Payload","func":"msg.payload = {\n values: [\n {\n type: 9,\n value: msg.payload\n }\n ]\n};\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":1000,"wires":[["a3ae6e1135ec2c44"]]},{"id":"95a39f71c0add7cd","type":"inject","z":"11b5d970f1ab28c1","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":170,"y":980,"wires":[["e740e03b5766af4e"]]},{"id":"4fa378d03d7fd8a3","type":"inject","z":"11b5d970f1ab28c1","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":170,"y":1020,"wires":[["e740e03b5766af4e"]]},{"id":"d6dff7e9f9703782","type":"BACnet-Instance","name":"ON/OFF","instanceAddress":"256"},{"id":"54de1b76e537f00b","type":"BACnet-Device","name":"CoolMaster","deviceAddress":"192.168.1.100"},{"id":"cd9b93738b4a729c","type":"BACnet-Client","name":"PC(Node-RED)","adpuTimeout":"6000","port":"47808","interface":"","broadcastAddress":"192.168.1.14"}]

フローはこのとおり

InjectノードではON:1、OFF:0にします。

msg.payload = {
 values: [
 {
 type: 9,
 value: msg.payload
 }
 ]
};
return msg;

Functionノードには、TYPEはApp-Tagの値で9:Enumerated。

送る値はmsg.payloadで渡しています。

BACnet WriteノードはApp-Tagを空にするとエラーがでるのでこのようにします。

ちなみに、ここにApp-Tagを記載しても、Functionノードから送られる値に上書きされるので、ここはなんでも大丈夫そうです。

まとめ

以上、Node-REDからCoolMasterにBACnetでON/OFFする方法をご紹介しました。

値を動的に送る際のフォーマットがわからず、ここでかなり時間を費やしました。