BACnetとは
BACnet(バックネット)とは、ASHRAE(アメリカ暖房冷凍空調学会;アシュレ)などで標準化されたビルオートメーション用通信プロトコルです。
空調や衛生(給水・排水)、エレベータ、電気、防犯、防災(消火設備)などの装置間の通信において、広く利用されています。
もし、これらビル用装置からデータを取得したり、制御したりする場合、BACnetの知識が役に立ちます。
この投稿では、PythonライブラリであるBAC0を使って、BACnet対応装置と通信してみます。
Pythonライブラリ BAC0
BACnetプロトコルを実装したPythonライブラリとして、BACpypesが有名です。
しかし、データを取得するのには複雑なプログラムの記述が必要です。
そこで、もっとお手軽にデータ取得できるように、BACpypesをラッピングしたライブラリがBAC0です。
BAC0のインストール
Pythonが実行可能な環境で、以下のコマンドでインストールできます。
pip install BAC0
インストールし、以下のプログラムを書いて実行してみましょう。
import BAC0
print('BAC0', BAC0.version)
実行結果は以下の通りです。
BAC0 23.07.03
インストールしたBA0のバージョンが出力されました。
以上で、無事BAC0のインストールが完了したことを確認できました。
BACnetのデバイスを列挙
プログラムを実行する前に、あらかじめBACnetデバイスをネットワークに接続しておいてください。
ネットワークに接続する方法は様々ありますが、今回は簡単にBACnet/IPで接続しています。
つまり、スイッチングハブでPCとBACnetデバイスをイーサネット接続しています。
自身の端末をBACnetデバイスとして初期化し、ネットワークに参加します。
bacnet = BAC0.lite()
次に、同じネットワークに接続されたBACnetデバイスを探します。
bacnet.discover()
print(bacnet.devices)
私の環境における、出力結果は以下です。
[('my-bacnet-device', 'my-vender-name', '192.168.0.248', 6367)]
出力された内容を表に整理します。
# | 項目 | 値 |
---|---|---|
1 | デバイス名 | my-bacnet-device |
2 | ベンダ名 | my-vender-name |
3 | アドレス | 192.168.0.248 |
4 | デバイスインスタンス | 6367 |
このうち、3「アドレス」と4「デバイスインスタンス」は、次のデバイス接続に必要です。
もしかしたら接続するBACnetデバイスにより出力される項目数が異なるかもしれません。
BACnetデバイスに接続
接続したいデバイスの「アドレス」と「デバイスインスタンス」を指定し、デイバスに接続します。
device = BAC0.device(address='192.168.0.248', device_id=6367, network=bacnet)
オブジェクトインスタンスのリストを取得します。
device.read_objects_list()
このデバイスは、6個のオブジェクトインスタンスを持つことが分かりました。
[('device', 6367),
('networkPort', 1),
('analogInput', 0),
('analogOutput', 0),
('binaryInput', 0),
('binaryOutput', 0)]
では、このうちアナログ入力ブジェクトのプロパティを取得します。
bacnetのreadMultipleメソッドに引数として与える文字列は次の順番で記述します。
address object object_instance property
ここで、propertyをallとすると、指定したオブジェクトインスタンスのすべてのプロパティを取得できます。
bacnet.readMultiple('192.168.0.248 analogInput 0 all')
[('analogInput', 0),
'myAnalogInput',
'analogInput',
0.0,
[0, 0, 0, 0],
'normal',
False,
'liters',
'hello world',
'noFaultDetected',
1.0]
出力された内容を表に整理します。
# | 項目 | 値 |
---|---|---|
1 | Object Identifier | ('analogInput', 0) |
2 | Object Name | myAnalogInput |
3 | Object Type | analogInput |
4 | Present Value | 0.0 |
5 | Status Flags | [0, 0, 0, 0] |
6 | Event State | normal |
7 | Out Of Service | False |
8 | Units | liters |
9 | Description | hello world |
10 | Reliability | noFaultDetected |
11 | COV Increment | 1.0 |
このうちデータの値を表すのは4番目の「Present Value」です。
この場合は現在値が1.0を表します。
BACnetデバイスとデータを取得・設定
デバイス間で値の交換として多くの場合使われるのが、analogInput、analogOutput、binaryInput、binaryOutputです。
これらは現在の値を意味するpresentValueプロパティを持ちます。
presentValueプロパティをもつオブジェクトからpresentValueプロパティの値を取得するのが、以下のプログラムです。
device.points
[my-bacnet-device/myAnalogInput : 0.00 liters,
my-bacnet-device/myAnalogOutput : 1.00 percent,
my-bacnet-device/myBinaryInput : False,
my-bacnet-device/myBinaryOutput : False]
以下のようにオブジェクト名を指定しても可能です。
device['myAnalogInput']
my-bacnet-device/myAnalogInput : 0.00 liters
逆に、BACnetデバイスのデータを変更するには、以下のように記述します。
device['myAnalogOutput'] = 2.0
device['myBinaryOutput'] = True
ちなみに、BAC0ではBACnetデバイスに接続した時点で、自動的に定期的にデータを収集しています。
デフォルトでは10秒ごとにすべてのデータを収集します。
収集したデータを出力してみます。
device['myAnalogInput'].history
2024-02-24 15:50:22.588512+09:00 0.0
2024-02-24 15:50:46.295070+09:00 0.0
2024-02-24 15:50:56.744925+09:00 0.0
2024-02-24 15:51:07.195799+09:00 0.0
2024-02-24 15:51:17.626165+09:00 0.0
...
2024-02-24 16:13:16.465508+09:00 0.0
2024-02-24 16:13:26.907661+09:00 0.0
2024-02-24 16:13:37.380533+09:00 0.0
2024-02-24 16:13:47.853849+09:00 0.0
2024-02-24 16:13:58.295078+09:00 0.0
Name: my-bacnet-device/myAnalogInput, Length: 134, dtype: float64
BACnetデバイスの値を監視
BACnetデバイスの中にはCOV(Change Of Value)という値の変化を監視する機能があります。
私が接続しているBACnetデバイスでもCOVが利用可能ですので、BAC0でCOVを設定してみます。
def cb(elements):
print('cov recieved:', elements)
device['myAnalogOutput'].subscribe_cov(callback=cb)
これで、COVの設定ができました。
それでは、値を変更してみます。
device['myAnalogOutput'] = 2.0
2024-02-24 16:16:34,045 - INFO | Received COV Notification for {'source': <Address 192.168.0.248>, 'object_changed': ('analogOutput', 0), 'properties': {'presentValue': 2.0, 'statusFlags': [0, 0, 0, 0]}}
cov recieved: {'source': <Address 192.168.0.248>, 'object_changed': ('analogOutput', 0), 'properties': {'presentValue': 2.0, 'statusFlags': [0, 0, 0, 0]}}
無事、値を変更するとコールバック関数cb
が呼ばれました。
BACnetデバイスと通信切断
COVをBACnetデバイスに設定したあとは、デバイスとの通信切断の前に、COVの設定解除を行いましょう。
これをしないと、BACnetデバイスをリセットするまで、よく分からないパケットがBACnetデバイスからPCに流れてしまいます。
device['myAnalogOutput'].cancel_cov()
次に、BACnetデバイスとの通信を切断します。
これで、BACnetデバイスからの定期的なデータ収集が終了します。
device.disconnect()
最後に、参加しているBACnetネットワークから自身を切断します。
bacnet.disconnect()
BACnetデバイスからのデータ収集、お疲れ様でした。
ビルのIoT化にご活用ください。