bacnet-server-c

This is a fork of Bacnet. This implementation includes an MQTT client sub-system that can used by the Bacnet Server to publish an MQTT message to an MQTT broker for the following conditions:

  1. A write/set operation is performed on the server's object properties.
  2. An new event is triggered or state changes.

Running BACnet Server

Configure config on location: <g>/config/<s>

Start Command:

g=<path for global_directory> s=config.yml ./app

For example:

g=/data/bacnet-server-c s=config.yml ./app 

on your pc

g=/home/user/code/bacnet s=config.yml ./app-amd64 

Updating BACnet Object Property Values Via MQTT

The values of the following object properties can be updated via MQTT publish command:

  1. Object Name (name) - applies to analog input (ai), analog output (ao), analog value (av), binary input (bi), binary output (bo) and binary value (bv) objects
  2. Present Value (pv) - applies to analog input (ai), analog output (ao), analog value (av), binary input (bi), binary output (bo) and binary value (bv) objects
  3. Priority Array (pri) - applies to analog output (ao), analog value (av), binary output (bo) and binary value (bv) objects

The value of the publish message must be in JSON using the following format:

{"value" : "value here", "uuid" : "uuid here"}

Object Name topic format:

bacnet/object/address/write/name

Write "ao_name1" into analog object (ao) name at instance (address) 1

topic: bacnet/ao/1/write/name
json payload: {"value" : "ao_name1", "uuid" : "123456"}

Present Value topic format:

bacnet/object/address/write/pv

Write 10.50 to into analog object (ao) present value at instance (address) 1

topic: bacnet/ao/1/write/pv
json payload: {"value" : "10.50", "uuid" : "123456"}

Priority Array topic formats:

bacnet/object/address/write/pri/priority_index
bacnet/object/address/write/pri/priority_index/all

Write 50.20 into analog object (ao) at instance (address) 1 at priority index 10

topic: bacnet/ao/1/write/pri/10
json payload: {"value" : "10.50", "uuid" : "123456"}

Write 99.99 into analog object (ao) at instance (address) 1 to all priority slots

topic: bacnet/ao/1/write/pri/16/all
json payload: {"value" : "99.99", "uuid" : "123456"}

Reset priority slots #10 of analog object (ao) at instance (address) 1. Reset applies only to analog ouput, analog value, binary output and binary value objects.

topic: bacnet/ao/1/write/pri/10
json payload: {"value" : "null", "uuid" : "123456"}

Reset all priority slots of analog object (ao) at instance (address) 1

topic: bacnet/ao/1/write/pri/16/all
json payload: {"value" : "null", "uuid" : "123456"}

MQTT Library

The MQTT C libary is the Eclipse Paho C Client Library for MQTT protocol. The source can be downloaded from https://github.com/eclipse/paho.mqtt.c. Please follow the install instructions to install the library.

Integrating the MQTT Client Library

In the server Makefile, change or include the following.

--
MQTT_SRC = mqtt_client.c
--
OBJS += ${SRC:.c=.o} ${MQTT_SRC:.c=.o}

MQTT_CFLAGS = -DMQTT -DMQTT_PUBLISH -I/usr/local/include -I${BACNET_SRC_DIR}/../apps/server
MQTT_LFLAGS = -L/usr/local/lib -lpaho-mqtt3c
--
${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET}
       ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} ${MQTT_LFLAGS} -o $@
--
.c.o:
       ${CC} -c ${CFLAGS} ${MQTT_CFLAGS} $*.c -o $@
--

MQTT Client Sub-System

The source files are currently under the Bacnet server module (app/server). Include mqtt_client.h to use the MQTT client sub-system.

#if defined(MQTT)
#include "mqtt_client.h"
#endif /* defined(MQTT) */

To initialize the MQTT client sub-system, call mqtt_client_init().

#if defined(MQTT)
    mqtt_client_init();
#endif /* defined(MQTT) */

To shutdown/clean-up the MQTT client sub-system, call mqtt_client_shutdown().

#if defined(MQTT)
    mqtt_client_shutdown();
#endif /* defined(MQTT) */

To publish an MQTT message, call mqtt_publish_topic() with the pre-defined topic ID (see topic and object/property-name mapping section) and value (either a C-string or a Bacnet string).

#if defined(MQTT)
                    mqtt_publish_topic(DEVICE_OBJECT_NAME_TOPIC_ID,
                      MQTT_TOPIC_VALUE_BACNET_STRING, &value.type.Character_String);
#endif /* defined(MQTT) */

Topic and Object/Property-Name Mapping

Topic and Object/Property-Name mappings are pre-defined. Each topic has an ID and defined in apps/server/mqtt_client.h.

#define DEVICE_OBJECT_NAME_TOPIC_ID            0
#define BINARY_OUTPUT_OBJECT_NAME_TOPIC_ID     1
#define TOPIC_ID_MAX                           2

The textual names of the Object/Property-Name and their Topic ID mappings are defined in apps/server/mqtt_client.c.

/* topic and object/property mappings */
static topic_mapping_t topic_mappings[] = {
  { DEVICE_OBJECT_NAME_TOPIC_ID,
    "device_object_name" },
  { BINARY_OUTPUT_OBJECT_NAME_TOPIC_ID,
    "binary_output_object_name" },
  { TOPIC_ID_MAX, NULL }
};

New ones much be added before the mapping with the topic ID TOPIC_ID_MAX.

Topic Format

The format of the MQTT topic published to MQTT broker is /<device_id>/object-property-name. For example the topic of an MQTT publish message sent when updating the Bacnet device name of a Bacnet server with a device ID 1234 would be:

/1234/device_object_name

The topic for Bacnet Binary Output Propery Name would be:

/1234/binary_output_object_name

MQTT Broker IP/Port

The default MQTT broker IP and port is 127.0.0.1 and 1883 respectively. To set the MQTT broker IP to a different IP, set the IP in the environment variable DEFAULT_MQTT_BROKER_IP. To set the MQTT broker port number to a different port, set the port in the environment variable MQTT_BROKER_PORT.

For example:

MQTT_BROKER_IP=10.20.30.123 MQTT_BROKER_PORT=11883 ./bin/bacserv

Compiling The Bacnet Server

Compile and build the bacnet server on your local development machine. Tested on Ubuntu 18.04.5 LTS.

sudo apt install build-essential net-tools git libssl-dev cmake -y
mkdir bacnet && cd bacnet
git clone https://github.com/eclipse/paho.mqtt.c paho.mqtt.c
cd paho.mqtt.c && cmake -DPAHO_BUILD_STATIC=TRUE && sudo make install
cd .. && git clone https://github.com/json-c/json-c json-c
cd json-c && mkdir -p json-c-build && cmake ../json-c && make && sudo make install
sudo apt-get install libyaml-dev -y
cd .. && git clone https://github.com/tlsa/libcyaml.git
cd libcyaml && sudo make install VARIANT=release
find / -name libyaml.a -print -exec cp -fR {} /usr/lib \;
cd .. && git clone git@github.com:NubeIO/bacnet-server-c.git
cd bacnet-server-c/bacnet-stack && make clean all

Running The Bacnet Server (bacserv)

The bacnet server binary file is at bacnet-stack/bin/bacserv. You need to specify the configuration file and folder to run the server using the "g" and "s" ENV variables like below.

g=/data/bacnet-server-driver s=config.yml ./bin/bacserv

Sample Get/Set Commands

Get and set the device propery name. The client and server are on the same machine here. If the client is on a different machine, you can omit the "mac" option.

Get the device object property name.

./bin/bacrp 1234 8 1234 77 --mac 10.104.0.11:47808
"SimpleServer"

To change the device object property name to "My Simple Server".

./bin/bacwp 1234 8 1234 77 0 -1 7 "My Simple Server" D 10.104.0.11:47808
WriteProperty Acknowledged!

Confirm the change was made.

./bin/bacrp 1234 8 1234 77 --mac 10.104.0.11:47808
"My Simple Server"

Get and set the binary output object propery name. Get the binary output object propery name.

./bin/bacrp 1234 4 1 77 --mac 10.104.0.11:47808
"BINARY OUTPUT"

To change the binary output object property name to "MY BINARY OUTPUT".

./bin/bacwp 1234 4 1 77 0 -1 7 "MY BINARY OUTPUT" D 10.104.0.11:47808
WriteProperty Acknowledged!

Confirm the change was made.

./bin/bacrp 1234 4 1 77 --mac 10.104.0.11:47808
"MY BINARY OUTPUT"

Testing the MQTT Publish/Subscribe Message

You can use any MQTT protocol compliant client. I used the mqtt-cli. To subscribe to changes to the device object and binary output object property name of the bacnet instance with device ID 1234.

mqtt sub -h <mqtt broker ip address> -t "/1234/binary_output_object_name" -t "/1234/device_object_name" -T

The client will receive a publish message when a change is made to the device object and binary output object propery name with the new value in the message.

mqtt sub -h <mqtt broker ip address> -t "/1234/binary_output_object_name" -t "/1234/device_object_name" -T

/1234/device_object_name: My Simple Server
/1234/binary_output_object_name: MY BINARY OUTPUT