# BACnet Stack: a C protocol stack you can link into an embedded device

> The BACnet Stack is a portable C library that implements the BACnet application, network and MAC layers for building automation devices, from bare metal RTOS targets up to Linux and Windows. It ships dozens of command line clients, a GPL exception that lets you link it to proprietary code, and a Makefile plus CMake path for cross-port builds.

**bacnet-stack/bacnet-stack** — BACnet Protocol Stack library provides a BACnet application layer, network layer and media access (MAC) layer communications services.

- Repository: https://github.com/bacnet-stack/bacnet-stack
- Website: http://bacnet.sourceforge.net/
- Stars: 602 · Forks: 311
- Language: C
- License: not declared
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/bacnet-stack-bacnet-stack

## What problem the BACnet Stack solves

BACnet is the data communication standard for building automation and control networks, and the project describes it as an open protocol that anyone may use. The catch sits with the paperwork rather than the technology. The BACnet standard document itself is copyrighted by ASHRAE, who sell it to defray the cost of developing and maintaining the standard, in the same way IEEE, ANSI and ISO do. Reading the protocol from that document is therefore not free, even though implementing it carries no royalties.

What this library provides is the implementation of the application layer, the network layer and the media access layer communications services, so that a device can speak BACnet without the vendor writing a stack from scratch.

The target hardware range is the point of the design. The code is C for portability and is meant for a RTOS or a bare metal embedded system, as well as for full operating systems such as Windows, Linux, macOS and BSD. The repository topics list avr, stm32 and mstp alongside linux, windows, ipv4 and ipv6, which is an accurate summary of a library that has to run on a microcontroller and on a server at the same time. The README states that registration for a BACnet vendor ID is free and that BACnet itself carries no royalties or licensing restrictions.

The users are therefore firmware engineers adding BACnet to a controller, integrators writing a gateway or a router, and developers who want a command line tool to poke at a building network.

## How the stack is layered and why the object model matters

BACnet uses an object-oriented approach in its application layer. Data is defined as objects with properties, and those objects interact with services. The result is that a BACnet device exposes standardized access to its information without the client needing any knowledge of the device's internal design, which is what makes devices from different vendors interchangeable on the same network.

Below the object model sit the network and MAC layers, and the stack supports several data link layers, with MS/TP, BACnet/IP and BACnet/SC named in the documentation. The physical options the standard covers include Ethernet, EIA-485, EIA-232, ARCNET and LonTalk, and the protocol also defines communication over UDP, IP, HTTP as web services, and Websockets.

For an embedded target, the practical consequence is that the data link is a build-time choice rather than a runtime one. The repository's Makefile exposes targets per data link, with BACDL values for bip, bip6, mstp, bsc and ethernet, and separate targets for the router and gateway applications. The demo applications default to the BACnet/IPv4 data link but can be compiled for BACnet IPv6, Ethernet, ARCNET or MS/TP.

The architecture overview is not in the README itself but in a separate developer document on the project's SourceForge tree, which the project links as the place to start for understanding the architecture and how to use the library.

## Building on Linux, macOS and Windows

The build is Makefile first. On Linux, Unix, Cygwin, BSD and macOS, a single command produces the sample applications:

```bash
    $ make clean all
```

Under Windows with MinGW Bash, the target changes:

```bash
    $ make win32
```

And from the Windows command line the project ships a batch file:

```bash
    c:\> build.bat
```

The Makefile itself is thin at the top level and delegates. The all target calls apps, and each data link variant passes a BACDL value down into the apps directory, so the same source tree yields bip, bip6, mstp, ethernet or bsc builds. The linux and bsd targets set BACNET_PORT instead. There is also a mingw32 target that sets PREFIX and compiles with BACDL=bip6, and a mstpwin32 variant that builds the MS/TP stack for Windows.

The library also works with CMake, which is what makes IDE integration practical. The project shows how to generate a Code::Blocks project:

```bash
    $ mkdir build
    $ cd build/
    $ cmake .. -G"CodeBlocks - Unix Makefiles"
```

The same generator name differs on Windows, where the project uses MinGW Makefiles instead. The top-level Makefile carries a matching cmake target that creates the build directory, configures with BUILD_SHARED_LIBS=ON and rebuilds, so a shared library build is one command away.

Since the code is C and designed for portability, it compiles with GCC by default and also with Clang and with IAR, the last of which matters for industrial embedded toolchains.

## Unit tests, coverage and the continuous integration matrix

The project runs several continuous integration workflows, and their names tell you what is checked. There is a CMake workflow building the library and demo apps on Ubuntu, Windows and macOS. There is a GCC workflow covering the Ubuntu Makefile build, BACnet/IP demo apps with and without a BBMD, BACnet/IPv6, BACnet Ethernet, BACnet MS/TP demo apps, gateway, router, router-ipv6 and router-mstp, ARM ports for STM and Atmel, AVR ports, and a MinGW32 compile of the BACnet/IP demo apps.

A separate quality workflow runs scan-build from the LLVM Clang tools, cppcheck, codespell, unit tests and code coverage. A CodeQL workflow runs the CodeQL analysis. Those are static analysis and security scanning steps applied to a C codebase, which for a library that ends up in building controllers is a reasonable baseline rather than a distinguishing feature.

The unit tests run from the project root with:

```bash
    $ make test
```

The framework is a slightly modified ztest, the tests live in the test/ folder, and the builder uses CMake with LCOV for coverage. The HTML coverage report starts at test/build/lcoverage/index.html. The project notes that the tests run in a POSIX environment and that they are run on every commit inside the continuous integration environment, with CMake supporting individual module testing and debugging in Visual Studio Code or another IDE.

One structural detail in the tree is worth noting for contributors: AGENTS.md sits at the top level alongside .clang-format, .editorconfig and .pre-commit-config.yaml, so the formatting and linting conventions are defined in the repository rather than left to each contributor's editor.

## The GPL exception that lets you link proprietary firmware

Licensing is the part of this project that deserves a careful read, because the answer is not a single licence.

The implementation is designed for the embedded BACnet appliance and is released under a GPL with exception licence, in the same style as eCos. The effect as the project describes it is that changes to the core code that are distributed are shared, but the BACnet library can be linked to proprietary code without that proprietary code becoming GPL. The SPDX identifier written into each source file is:

```bash
     * SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
```

The exception is what makes the library usable in a commercial controller. Without it, linking this C code into a closed firmware image would pull the whole product under GPL terms.

The wrinkle is that the library is not uniformly licensed. Some source files are described as skeleton or example or template files, and those are not licensed as GPL. They carry different identifiers:

```bash
     * SPDX-License-Identifier: MIT
     * SPDX-License-Identifier: Apache-2.0
```

So the practical step for an adopter is to check the actual file you are copying. The project documents a way to see the whole picture:

```bash
    $ grep -nrw SPDX --include=*.[c,h]
```

That single command lists the SPDX identifier of every C source and header file, so you can review what you are about to vendor into a product. Read the licence/ directory and the per-file headers yourself rather than relying on the summary, since how the exception applies to your distribution is a question for your own counsel.

## Command line clients, routers and the Docker image

The sample applications are the fastest way to work with a live BACnet network. Most are client applications that provide one main BACnet service, with the addition of server applications, one gateway application, a couple of router applications and a couple of MS/TP specific applications. Each takes command line parameters.

The Dockerfile in the repository builds the applications into a container. The usage is documented in the file itself:

```bash
# docker build -t bacnet --target bacnet .
# docker run --rm --name bacnet -d bacnet <device_id> [<device_name>]
```

The runtime stage copies the built binaries into /opt/bacnet/bin, exposes 47808/udp and sets bacserv as the entrypoint, with a default command of 1234 and test_server. That port is the BACnet/IP port, and the image exposes one UDP port rather than a range.

The Dockerfile also documents how to run a client command against the server running inside the same container, which requires BBMD foreign device registration and three environment variables, BACNET_IP_PORT, BACNET_BBMD_PORT and BACNET_BBMD_ADDRESS. The example inside the file sets those values and then calls bacwi and bacrp from /opt/bacnet/bin.

One caveat is stated in the Dockerfile itself: the image does not add the shell scripts in the bin folder because many of them are broken, though reading them may still be instructive. The documentation for the utilities and environment variables is in bacnet-stack/bin/readme.txt.

## Where the BACnet Stack is the wrong choice

There are clear cases where this library does not apply.

If you are building something for the cloud, the stack has nothing to say to it. There is no REST or GraphQL layer in the tree, no service abstraction, no packaging story for a container registry beyond the one Dockerfile, and no documented way to expose BACnet objects to a web frontend.

If you need a managed service with a support contract, you are outside this project's model. The repository publishes no GitHub releases, so there is no tagged version stream to track, and the upstream project is hosted on SourceForge rather than on GitHub Releases. The library is not archived and the last push was on 2026-10-19, so the code is being worked on, but you should plan on consuming a moving target rather than a sequence of releases.

If you are writing an application that merely needs to read a BACnet value from one device, the full stack may be more than you need. The demo clients already do single service calls, and a small amount of code on top of those may serve better than integrating the whole library.

The protocol coverage is also worth checking against your device list. The stack supports MS/TP, BACnet/IP and BACnet/SC data links, and the standard covers a range of physical layers, but the README does not enumerate every object type or every service the library implements, so a device with an unusual object set needs verification before you commit.

## Conclusion

BACnet Stack fits an engineer who has to put a BACnet device on a network, whether that means a controller on an STM32, a Linux gateway, or a Windows workstation talking to a building, and the demo clients give you a way to probe real devices before writing product code. It is not a fit if you need a managed service, a cloud API, or a library with an active release train under semantic versioning, since the repository publishes no GitHub releases and the upstream project lives on SourceForge. Before adopting it, confirm the BACnet vendor ID you will register is free, read the per-file SPDX identifiers in src/ rather than assuming one licence covers the tree, and run make test in your target port before wiring the library into firmware.

## FAQ

### How do I build the BACnet Stack on Linux?

Run make clean all in the project root, which produces the demo applications and sample ports under Linux, macOS, BSD and Cygwin. Windows users run make win32 under MinGW Bash or build.bat from the command line.

### Can I use BACnet Stack in proprietary firmware?

The core code is licensed under GPL-2.0-or-later WITH GCC-exception-2.0, an exception similar to eCos, so the library can be linked to proprietary code without that code becoming GPL. Some skeleton and template files carry MIT or Apache-2.0 identifiers instead, so check the SPDX header of the file you copy.

### Which data link layers can I compile the BACnet Stack for?

The Makefile exposes BACDL targets for bip, bip6, mstp, bsc and ethernet, plus router and gateway applications. The demo applications default to the BACnet/IPv4 data link and can be compiled for BACnet IPv6, Ethernet, ARCNET or MS/TP.

### How do I run the BACnet Stack unit tests and see coverage?

Run make test from the project root. The tests use a slightly modified ztest framework in the test/ folder, the builder uses CMake, and coverage uses LCOV with the HTML report at test/build/lcoverage/index.html.

### Does the BACnet Stack charge royalties or require a licence fee?

The project states that BACnet has no royalties or licensing restrictions and that registration for a BACnet vendor ID is free. The standard document itself is copyrighted by ASHRAE and sold by them to cover the cost of maintaining the standard.

## Sources

- [bacnet-stack/bacnet-stack on GitHub](https://github.com/bacnet-stack/bacnet-stack)
- [Issues](https://github.com/bacnet-stack/bacnet-stack/issues)
- [Project website](http://bacnet.sourceforge.net/)
- [README](https://github.com/bacnet-stack/bacnet-stack/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/bacnet-stack-bacnet-stack
