Skip to content

Latest commit

Β 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

BACnet B-SS (Smart Sensor) - C# example

A minimal, copy-paste-friendly example showing how to implement the BACnet B-SS (Smart Sensor) device profile in C# / .NET, using the CAS BACnet Stack. It listens on BACnet/IP (UDP 47808), answers ReadProperty, and is discoverable via Who-Is / I-Am.

This is the first C# example in the BACnet profile example series. There is no prior C# example repo to copy structurally - this one was built from the C++ edition's device model and the Node edition's documentation skeleton, with a binding mechanism new to the series: native P/Invoke against the vendored C# adapter, not an embedded C++ link or a Node N-API addon.

  • 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.0.0, built and verified against the CAS BACnet Stack submodule pinned at 6.x, at Protocol_Revision 24, with the vendored C# common/ helper at v1.0.0. Running the example prints the app and API versions - if what it prints disagrees with this line, trust the program and check CHANGELOG.md.

What is the B-SS (Smart Sensor) profile?

B-SS (BACnet Smart Sensor), defined in Annex L of ANSI/ASHRAE 135, is the simplest BACnet device profile - the standard describes it as "a simple sensing device with very limited resources." It is meant for inexpensive, fixed-function sensors (temperature, humidity, occupancy, a contact, ...) that mostly just need to report what they measure when asked.

A B-SS device answers ReadProperty and is discoverable. It does not have to support WriteProperty (a smart sensor is read-only), 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 sensor 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.

The device this example creates

Device 389001  "Rainbow"   (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 (read-only)
    β”œβ”€β”€ Multi-State Input 1  "Hot Pink"    Present_Value  1        (state 1..3; read-only)
    └── Network Port 1       "Vermilion"   the BACnet/IP port     (required on every device)

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 βœ…
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).
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
Device 389001 Rainbow
Analog Input 1 Bronze
Binary Input 1 Emerald
Multi-state Input 1 Hot Pink
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 (tracking the 6.x branch). Unlike the Node edition (npm workspace, compiles a native addon) or the C++ edition (compiles the stack's own source straight into the executable), the C# edition links against a separately built native shared library (CASBACnetStack_x64_Release.dll on Windows / libCASBACnetStack_x64_Release.so on Linux) through P/Invoke - see Build the native CAS BACnet Stack library below. 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.

What's in this repository

This is a self-contained project. It ships:

  • Program.cs - the example device.
  • common/ - the shared C# helper (UDP, callbacks, CLI) vendored in.
  • BACnetProfileExampleBSSCS.csproj - the .NET build (net8.0), the same on Windows and Linux.
  • docs/PICS.md - the conformance statement.
  • docs/objects.json - the input to the objects-and-properties generator.
  • submodules/cas-bacnet-stack/ - the CAS BACnet Stack as a git submodule (private; requires a license - see above). Its adapters/csharp/ is included directly into this project (<Compile Include> in the .csproj, no NuGet package); its source/ and MSVC/CMake build produce the separate native shared library this example loads at run time.

Prerequisites

  • .NET 8 SDK (dotnet --version should report 8.x or later).
  • Git (to fetch the stack submodule).
  • To build the native library (see below): on Windows, Visual Studio 2022 Build Tools (MSBuild) targeting the stack's projects/msvs/BuildCASBACnetStack.sln; on Linux, a C++17 toolchain (g++/clang++).

Build

git clone --recursive https://github.com/chipkin/BACnetProfileExample-B-SS-CS.git
cd BACnetProfileExample-B-SS-CS

dotnet build -c Release

Already cloned without --recursive? Run git submodule update --init --recursive first - the build needs the stack submodule's adapters/csharp/ sources.

dotnet build compiles this example and the vendored C# adapter - it does not build the native CAS BACnet Stack library the adapter calls into via P/Invoke. That is a separate step (below), because it is a native C++ build, not a .NET one.

Build the native CAS BACnet Stack library

The C# adapter's P/Invoke declarations resolve CASBACnetStack_x64_Release.dll (Windows) / libCASBACnetStack_x64_Release.so (Linux) from the process's own directory (or the system library search path) at run time - there is no bundled copy and no NuGet package. Build it once from the stack submodule, then copy it next to this project's build output.

Windows - the stack's MSVC solution has a ReleaseDll|x64 configuration that builds exactly this DLL, under exactly this name:

& "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\MSBuild.exe" `
  submodules\cas-bacnet-stack\projects\msvs\BuildCASBACnetStack.sln `
  /p:Configuration=ReleaseDll /p:Platform=x64

copy submodules\cas-bacnet-stack\bin\CASBACnetStack_x64_Release.dll bin\Release\net8.0\

(Adjust the MSBuild path to your Visual Studio / Build Tools install; vswhere or the "Developer Command Prompt for VS 2022" both resolve it for you if you have Visual Studio Installer set up.)

Linux - the stack does not currently ship a ready-made shared-library CMake target (its CMake adapters/cpp/ builds a static adapter; its Linux projects/linux/ build is for the gtest suite). Build one directly from the stack's own sources with g++:

g++ -shared -fPIC -std=c++17 -O2 \
    -I submodules/cas-bacnet-stack/source \
    submodules/cas-bacnet-stack/source/*.cpp \
    -o bin/Release/net8.0/libCASBACnetStack_x64_Release.so

Either way, the DLL/.so must end up in the same directory as BACnetProfileExampleBSSCS.dll/.exe (bin/Release/net8.0/ by default) - the .NET native-library resolver looks there first.

Why only a Release build

The vendored adapter selects the native library filename with #if DEBUG / #else: CASBACnetStack_x64_Debug in a Debug build, CASBACnetStack_x64_Release in Release. The stack's MSVC solution only has a ReleaseDll configuration (see above) - there is no matching DebugDll that would produce CASBACnetStack_x64_Debug.dll. So build and run this example in Release; a Debug build starts but fails to load the native library the first time it calls into the stack (see Troubleshooting) unless you first add a DebugDll configuration to the stack's solution yourself.

Why PropertyBufferHelper.cs is here but unused

The .csproj includes all three of the adapter's files for parity with its expected set (see submodules/cas-bacnet-stack/adapters/csharp/selftest/PropertyBufferHelperSelfTest.csproj for the precedent), but PropertyBufferHelper.cs packs buffer-shaped Send* calls (ReadProperty/WriteProperty/CreateObject as a client) that this read-only B-SS example never makes - a Smart Sensor only ever answers ReadProperty, it never initiates one. It compiles in unused; that is a documented, deliberate choice, not an oversight.

Run

dotnet run -c Release -- --port 47821 --deviceID 12345
# or, after `dotnet build -c Release`, run the built executable directly:
./bin/Release/net8.0/BACnetProfileExampleBSSCS --port 47821

Expected output:

BACnet B-SS (Smart Sensor) Example - C# v1.0.0
CAS BACnet Stack v6.0.21.0
FYI: listening on <your-ip>:47808 (broadcast <your-subnet-broadcast>)
FYI: Device 389001 ("Rainbow") ready. Vendor ID 389. Press 'h' for help.

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

Command-line options

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

Interactive commands

While the example runs in a real console (not redirected - see TUTORIAL.md for what that means), 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. Ctrl+C also shuts the device down cleanly (an interactive console is not required for that).

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 389001 (vendor 389). It also broadcasts an I-Am at start-up.
  2. Browse the object model - the device shows five objects: the Device (Rainbow), the three sensors, and the Network Port (Vermilion). Reading the Device's Object_List returns all five.
  3. Read the Device - ReadProperty 389001 -> Object_Name returns "Rainbow"; 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; Out_Of_Service returns false; Object_Name returns "Bronze". Repeat for Binary Input 1 ("Emerald", has Polarity) and Multi-State Input 1 ("Hot Pink", has Number_Of_States = 3). Every required property of every object is readable.
  5. Confirm the profile boundary - a WriteProperty to any object is rejected. That is correct: a B-SS Smart Sensor is read-only.

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

How the C# binding differs from the C++/Node editions

If you have read either sibling example, the object model and callback shapes are identical - this section is only about what is mechanically different because C# talks to the stack through P/Invoke instead of an embedded link or an N-API addon. See common/CHANGELOG.md for the full list; the highlights:

  • No LoadBACnetFunctions(). The C# adapter's BACnetStack_* methods are plain [DllImport] static extern declarations; the .NET runtime resolves the native library the first time one is called, not up front. Program.cs treats that first call (inside PrintVersion()) as the load check.
  • Raw pointers, not managed buffers. Every Get*Property callback takes byte*/uint*/float* out-parameters matching the native C ABI directly - this project is unsafe throughout (<AllowUnsafeBlocks>true</AllowUnsafeBlocks>).
  • A separately built native library. There is no "compile the stack into the executable" mode for C# (unlike the C++ edition's default SOURCE link mode) and no addon build step baked into the package install (unlike Node's npm install). You build the native shared library once, explicitly (see above), and it has to be found next to the executable at run time.
  • Delegates must be rooted in static readonly fields so the CLR cannot collect them while native code may still call back into their marshalling thunks - see the comments in common/CASExampleHelper.cs and Program.cs.

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

BACnet B-SS (Smart Sensor) profile example - C#. Uses the CAS BACnet Stack.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages