Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

34 Commits

Folders and files

Repository files navigation

BACnet B-SA (Smart Actuator) - C++ example

A minimal, copy-paste-friendly example showing how to implement the BACnet B-SA (BACnet Smart Actuator) device profile in C++ using the CAS BACnet Stack. It listens on BACnet/IP (UDP 47808), answers ReadProperty requests, accepts WriteProperty to its commandable outputs, and is discoverable via Who-Is / I-Am.

Download a prebuilt binary (Windows and Linux x64) - or build it yourself, see Build below.

  • TUTORIAL.md - how to extend this example and how to review it for conformance. Read it when you start turning this into your own device.
  • docs/PICS.md - the Protocol Implementation Conformance Statement: every object, every property, and who answers it.

Versions: this document describes example v1.2.0, built and verified against CAS BACnet Stack 6.0.21 (6.x @ abd4cee1), at Protocol_Revision 24, with the vendored common/ helper at v2.5.0. Running the example prints all three - if what it prints disagrees with this line, trust the program and check CHANGELOG.md.

What is the B-SA (Smart Actuator) profile?

B-SA (BACnet Smart Actuator), defined in Annex L of ANSI/ASHRAE 135, is the next step up from the Smart Sensor - the standard describes it as "a simple actuating device with very limited resources." It is meant for inexpensive, fixed-function actuators (a valve, a damper, a relay, a setpoint) that need to report state and be driven by a controller.

A B-SA device answers ReadProperty and accepts WriteProperty to its commandable outputs - that is what distinguishes an actuator from a sensor. It does not have to support alarming / event reporting, scheduling, or trending, and this example implements none of them on purpose.

But it is still a full BACnet device. Even the simplest profile must present the standard object model - a Device object, a Network Port object (every device needs one), and its objects - and each object must expose all of its required properties. The CAS BACnet Stack generates most of those automatically (Object_Identifier, Object_Type, Status_Flags, Object_List, Protocol_*, ...); this example supplies the handful that are application-specific. The result is conformant for Protocol_Revision 24. docs/PICS.md lists every property and who answers it.

Commandable outputs (the heart of B-SA)

A BACnet output object's Present_Value is not written directly. Instead it is commandable: it is resolved from a 16-slot Priority_Array plus a Relinquish_Default.

  • A WriteProperty(Present_Value, value, priority) stores value in slot priority (1 = highest .. 16 = lowest). If the client omits the priority, BACnet uses 16.
  • A WriteProperty(Present_Value, NULL, priority) relinquishes that slot.
  • The stack reports the value in the highest-priority non-null slot as the effective Present_Value; if every slot is null, it reports Relinquish_Default.

This lets several controllers command the same output at different priorities (e.g. a manual override at priority 8 beats a normal command at 16) and cleanly hand control back by writing NULL. The application just stores the array (see the Commandable struct in main.cpp); the stack does the priority resolution.

The device this example creates

Device 389002  "Chipkin Example B-SA"   (Vendor 389 - Chipkin Automation Systems)
    β”‚
    β”œβ”€β”€ Analog Input  1       "Bronze"      Present_Value  21.5    (REAL, degrees Celsius; read-only)
    β”œβ”€β”€ Binary Input  1       "Emerald"     Present_Value  inactive  (0 = inactive / 1 = active; read-only)
    β”œβ”€β”€ Multi-State Input 1   "Hot Pink"    Present_Value  1       (state, 1..3; read-only)
    β”œβ”€β”€ Analog Output 1       "Chartreuse"  Present_Value  20.0    (REAL setpoint; WRITABLE, commandable)
    β”œβ”€β”€ Binary Output 1       "Fuchsia"     Present_Value  inactive  (0/1; WRITABLE, commandable)
    β”œβ”€β”€ Multi-State Output 1  "Indigo"      Present_Value  1       (state, 1..3; WRITABLE, commandable)
    └── Network Port 1        "Vermilion"   the BACnet/IP port     (required on every device)

The three input objects (Bronze, Emerald, Hot Pink) are the shared minimum every example in this series carries. The three output objects (Chartreuse, Fuchsia, Indigo) are the B-SA additions; their Present_Value starts at the Relinquish_Default (no slots commanded yet). Object names follow this series' colour-naming convention (Device is always "Chipkin Example B-SA").

What this example supports

The example implements exactly the capabilities below - and nothing more, which is the point of a profile example.

BIBBs (BACnet Interoperability Building Blocks)

BIBB Description Supported
DS-RP-B Data Sharing - ReadProperty - B βœ…
DS-WP-B Data Sharing - WriteProperty - B βœ…
DM-DDB-B Device Management - Dynamic Device Binding - B βœ…
DM-DOB-B Device Management - Dynamic Object Binding - B βœ…

Services (executed / B-side)

Service Notes
ReadProperty Responds to property reads (DS-RP-B).
WriteProperty Accepts writes to the commandable outputs' Present_Value (DS-WP-B).
Who-Is / I-Am Answers Who-Is with I-Am, and broadcasts an I-Am on start-up (DM-DDB-B).
Who-Has / I-Have Answers Who-Has with I-Have (DM-DOB-B).

Object types

Object type Instance Name Access
Device 389002 Chipkin Example B-SA -
Analog Input 1 Bronze read-only
Binary Input 1 Emerald read-only
Multi-State Input 1 Hot Pink read-only
Analog Output 1 Chartreuse writable (commandable)
Binary Output 1 Fuchsia writable (commandable)
Multi-State Output 1 Indigo writable (commandable)
Network Port 1 Vermilion -

Every required property of every object, and who answers it, is in docs/PICS.md.

Requires the CAS BACnet Stack (licensed product)

This example builds against the CAS BACnet Stack, which is a commercial Chipkin product - it is not free or open source, and there is no public/trial build. The stack is referenced here as the private git submodule submodules/cas-bacnet-stack; you can only fetch and build it once you have a CAS BACnet Stack license and access to that repository.

To get the CAS BACnet Stack (and access to build this example), contact Chipkin: https://store.chipkin.com/services/stacks/bacnet-stack or sales@chipkin.com.

You do not need a stack licence to read this example, or to run a prebuilt release binary. The licence is what lets you build it - that is the part the stack submodule gates.

What's in this repository

This is a self-contained project. It ships:

  • main.cpp - the example device.
  • common/ - the shared helper (UDP, callbacks, CLI, keyboard) vendored in.
  • CMakeLists.txt - the build, the same on Windows, Linux, and macOS.
  • docs/PICS.md - the conformance statement.
  • submodules/cas-bacnet-stack/ - the CAS BACnet Stack as a git submodule (private; requires a license - see above). Its sources are compiled into the executable, so there is no library or DLL to build, ship, or install.

Prerequisites

  • A C++17 compiler (MSVC, GCC, or Clang).
  • CMake >= 3.15.
  • Git (to fetch the stack submodule).

Windows

Linux / macOS

  • Debian/Ubuntu: sudo apt install build-essential cmake git
  • macOS: xcode-select --install and brew install cmake

Build

CMake only, and the same two commands on every platform:

git clone --recursive https://github.com/chipkin/BACnetProfileExample-B-SA-CPP.git
cd BACnetProfileExample-B-SA-CPP

cmake -B build -S .
cmake --build build --config Release

Already cloned without --recursive? Run git submodule update --init --recursive first - the build needs the stack submodule.

The first build takes a few minutes - it compiles the entire CAS BACnet Stack (~600 source files) into the executable. Rebuilds after that are incremental and take seconds.

If your CAS BACnet Stack lives somewhere other than the bundled submodule, point CMake at it: cmake -B build -S . -D CAS_STACK_DIR=/path/to/cas-bacnet-stack.

Run

# Linux / macOS
./build/BACnetExampleBSA

# Windows
.\build\Release\BACnetExampleBSA.exe

Expected output:

BACnet B-SA (Smart Actuator) Example - C++ v1.2.0
CAS BACnet Stack version: 6.0.21.0
Common helper (common/) version: 2.5.0
FYI: Listening for BACnet/IP on UDP port 47808 (Network Port 1).
TX 21 bytes to 192.168.3.255:47808 (broadcast) (Network Port 1)
FYI: Device 389002 ("Chipkin Example B-SA") ready. Vendor ID 389. Press 'h' for help.

The TX line is the start-up I-Am the device broadcasts to announce itself. It goes to the local subnet broadcast address (here 192.168.3.255, computed from the Network Port's interface), not the global 255.255.255.255. As clients talk to the device you'll see RX ... bytes from ... and TX ... bytes to ... lines showing the traffic; a WriteProperty to an output also prints a line such as WriteProperty: Analog Output 1 (Chartreuse) <- 42.50 @ priority 8.

The device listens on UDP 47808 (BACnet/IP). Allow that port through your firewall. To use a different port, pass --port (see below).

A wall of red Error: lines at start-up is expected and is not your bug - it is the stack's own debug logging (the device hearing its own broadcast I-Am, and a one-time BACnet/SC UUID notice). TUTORIAL.md explains both.

Command-line options

Option Default Meaning
--port <n> 47808 UDP port to listen on (BACnet/IP).
--deviceID <n> 389002 The device's BACnet instance number (BACnet requires this to be configurable).
--help, -h - Show usage and exit.
--version - Print the example, stack, and common/ helper versions, then exit.

Interactive commands

While the example runs, these keys are available:

Key Action
h Show the version information and this command list.
q Quit.
up arrow Increase Analog Input 1 (Bronze) by 1.1.
down arrow Decrease Analog Input 1 (Bronze) by 1.1.

The up/down keys change the live Present_Value of the analog input, so a client re-reading it sees the new value.

Verify

Use a BACnet client such as the CAS BACnet Explorer:

  1. Discover - send a Who-Is. The device replies with I-Am from instance 389002 (vendor 389). It also broadcasts an I-Am at start-up.
  2. Browse the object model - the device shows eight objects: the Device (Chipkin Example B-SA), three inputs, three outputs, and the Network Port (Vermilion). Reading the Device's Object_List returns all eight.
  3. Read the Device - ReadProperty 389002 -> Object_Name returns "Chipkin Example B-SA"; Protocol_Revision returns 24; Description returns the profile description string.
  4. Read a sensor - ReadProperty Analog Input 1 -> Present_Value returns 21.5; Units returns degrees-Celsius; Object_Name returns "Bronze".
  5. Command an output (the B-SA test) - ReadProperty Analog Output 1 -> Present_Value returns 20.0 (its Relinquish_Default, nothing commanded yet). Now WriteProperty Analog Output 1 Present_Value = 42.5 at priority 8. Re-read Present_Value: it returns 42.5, and Priority_Array[8] shows 42.5. WriteProperty Present_Value = NULL at priority 8 to relinquish; Present_Value returns to 20.0. Repeat for Binary Output 1 ("Fuchsia", active/inactive) and Multi-State Output 1 ("Indigo", state 1..3).
  6. Confirm the profile boundary - a WriteProperty to a read-only input (e.g. Analog Input 1) is rejected. That is correct: inputs are sensors.

For a property-by-property review against the conformance statement, see TUTORIAL.md.

The BACnet profile example series

The CAS BACnet Stack supports every standardized device profile in ASHRAE 135-2024 Annex L, and there is one example repository per profile. Pick the profile your device claims, then the language you build in. "Ask" means the example hasn't been built yet for that language - contact Chipkin if you need one.

Controllers (Annex L.4)

Profile C++ Node.js C# Rust Python Go
B-SS Smart Sensor B-SS-CPP B-SS-Node B-SS-CS B-SS-Rust B-SS-Python B-SS-Go
B-SA Smart Actuator B-SA-CPP Ask Ask Ask Ask Ask
B-ASC Application Specific Controller B-ASC-CPP B-ASC-Node Ask Ask Ask Ask
B-AAC Advanced Application Controller B-AAC-CPP Ask Ask Ask Ask Ask
B-BC Building Controller B-BC-CPP Ask Ask Ask Ask Ask

Life safety controllers (Annex L.5)

Profile C++ Node.js C# Rust Python Go
B-LSC Life Safety Controller B-LSC-CPP 🚧 Ask Ask Ask Ask Ask
B-ALSC Advanced Life Safety Controller B-ALSC-CPP Ask Ask Ask Ask Ask

Access control controllers (Annex L.6)

Profile C++ Node.js C# Rust Python Go
B-ACC Access Control Controller B-ACC-CPP Ask Ask Ask Ask Ask
B-AACC Advanced Access Control Controller B-AACC-CPP Ask Ask Ask Ask Ask

Lighting controllers (Annex L.11)

Profile C++ Node.js C# Rust Python Go
B-LD Lighting Device B-LD-CPP Ask Ask Ask Ask Ask
B-LS Lighting Supervisor B-LS-CPP Ask Ask Ask Ask Ask

Elevator controllers (Annex L.13)

Profile C++ Node.js C# Rust Python Go
B-EM Elevator Monitor B-EM-CPP Ask Ask Ask Ask Ask
B-EC Elevator Controller B-EC-CPP Ask Ask Ask Ask Ask
B-AEC Advanced Elevator Controller B-AEC-CPP Ask Ask Ask Ask Ask

Authentication and authorization (Annex L.14)

Profile C++ Node.js C# Rust Python Go
B-AS Authorization Server B-AS-CPP Ask Ask Ask Ask Ask

Miscellaneous (Annex L.7, combinable with any one family)

Profile C++ Node.js C# Rust Python Go
B-BBMD Broadcast Management Device B-BBMD-CPP Ask Ask Ask Ask Ask
B-ACDC Access Control Door Controller B-ACDC-CPP Ask Ask Ask Ask Ask
B-ACCR Access Control Credential Reader B-ACCR-CPP Ask Ask Ask Ask Ask
B-RTR Router B-RTR-CPP Ask Ask Ask Ask Ask
B-GW Gateway B-GW-CPP Ask Ask Ask Ask Ask
B-DAP Device Address Proxy B-DAP-CPP Ask Ask Ask Ask Ask
B-SCHUB BACnet/SC Hub B-SCHUB-CPP Ask Ask Ask Ask Ask
B-GENERAL General device (Annex L.8) (satisfied by every example above) β€” β€” β€” β€” β€”

Operator interfaces and workstations (Annex L.1–L.3, L.9–L.10, L.12)

Client-side profiles.

Profile C++ Node.js C# Rust Python Go
B-OD Operator Display B-OD-CPP Ask Ask Ask Ask Ask
B-OWS Operator Workstation planned β€” β€” β€” β€” β€”
B-AWS Advanced Operator Workstation planned β€” β€” β€” β€” β€”
B-XAWS Extended Advanced Operator Workstation planned β€” β€” β€” β€” β€”
B-LSAP Life Safety Annunciator Panel planned β€” β€” β€” β€” β€”
B-LSWS Life Safety Workstation planned β€” β€” β€” β€” β€”
B-ALSWS Advanced Life Safety Workstation planned β€” β€” β€” β€” β€”
B-ACSD Access Control Security Display planned β€” β€” β€” β€” β€”
B-ACWS Access Control Workstation planned β€” β€” β€” β€” β€”
B-AACWS Advanced Access Control Workstation planned β€” β€” β€” β€” β€”
B-LOD Lighting Operator Display planned β€” β€” β€” β€” β€”
B-ALWS Advanced Lighting Workstation planned β€” β€” β€” β€” β€”
B-LCS Lighting Control Station planned β€” β€” β€” β€” β€”
B-ALCS Advanced Lighting Control Station planned β€” β€” β€” β€” β€”
B-ED Elevator Display planned β€” β€” β€” β€” β€”
B-EWS Elevator Workstation planned β€” β€” β€” β€” β€”
B-AEWS Advanced Elevator Workstation planned β€” β€” β€” β€” β€”

🚧 = in progress. "Ask" = not yet built for that language; contact Chipkin if you need it. Profile definitions: ANSI/ASHRAE 135-2024 Annex L. BIBB definitions: Annex K. Get the stack: https://store.chipkin.com/services/stacks/bacnet-stack.

References

See also TUTORIAL.md, docs/PICS.md, CHANGELOG.md, and AGENTS.md.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages