Z-bit

OSDP for firmware, without the baggage

A portable, embedded-friendly implementation of SIA OSDP v2.2.2 in freestanding C11. No malloc. No globals. No OS dependencies in the core.

Does your device need OSDP?

The Open Supervised Device Protocol (OSDP) is the SIA standard for communication between access control field devices (card readers, controllers, and peripherals) and the panels that manage them.

Building it in-house means tracking spec revisions, re-testing interop, and fixing framing bugs. OSDP Embedded implements SIA OSDP v2.2.2 and is structured so PD, ACU, or Monitor builds pull in only the code they need.

The PD side is done, production-ready, and validated against OSDP ACUs deployed in the field today. ACU support is under active development.

  • Freestanding C11 core. No dynamic allocation, global state, or OS assumptions.
  • no_std Rust wrapper with an idiomatic API.
  • Secure Channel. AES-128 encryption and message authentication, with pluggable crypto backends such as mbedTLS, wolfCrypt, or a hardware accelerator.
  • Built for interop. Fully verified against SIA's libosdp-conformance capture suite.
  • Message-direction architecture. Code is separated by role, so a PD, ACU, or monitor build links only the messages it actually sends and receives, keeping the memory footprint small.
  • Test tooling included: command-line PD and ACU mocks, a capture-file parser, and an MCP server.

At a glance

TargetContentsUsed by
osdp::coreCRC-16, checksum, frame decode/build, and Secure Channel primitives (key derivation, cryptograms, CBC-MAC, AES-CBC payload encrypt/decrypt)Everything
osdp::messagesOne translation unit per command and per reply, each containing a model, decoder, and builderPD, ACU, Monitor
osdp::dispatchOptional switch-router from raw frame to typed messageMonitor only
osdp::pdPD-side state machine covering address filtering, command dispatch, sequence-number policing, and Secure Channel handshakePD applications
osdp::acuACU-side state machine covering multi-PD management, command issuance, timeout/reply callbacks, and Secure Channel handshakeACU applications

Coverage: framing for every OSDP frame variant, 20 of 25 Annex A commands, 19 of 21 replies, and the complete Secure Channel handshake and operational message set.

Dual-licensed

Use it free under the GPL, or license it commercially for closed-source firmware.

Open Source

Free GPL-3.0-or-later

For products distributed under a GPL-compatible license. Also free for evaluation, proof-of-concept work, and internal testing.

Get the source

One fee, one product family, royalty-free. No per-unit fees, no unit reporting, no audits. A product family is every model, SKU, form factor, faceplate, finish, and enclosure variant built from a common firmware code base on a single processor platform. Products sold under another company's brand require that company to hold its own license.

Support includes direct access to the Z-bit Systems engineering team for break-fix and integration questions, bug fixes and security patches for the support term, and guidance applying updates as ACU support and new protocol features land.

Drops into the firmware you already have

OSDP Embedded doesn't take over your design. It owns no threads, no heap, and no hardware: you keep your main loop, your UART driver, and your crypto, and the library plugs in around them.

  1. Add it to your build

    Add the repository as a CMake subdirectory and link only the targets your role needs.

  2. Connect three callbacks

    read, write, and a millisecond clock over your existing UART, USB CDC, or socket. All non-blocking.

  3. Call tick from your loop

    osdp_pd_tick() never blocks: it reads what has arrived, calls your command handler, and sends the reply.

# CMakeLists.txt
add_subdirectory(OSDP-Embedded)

target_link_libraries(my_reader_firmware PRIVATE
    osdp::core osdp::messages osdp::pd)

# minimal firmware build: skip host-side extras
cmake -S . -B build -DOSDP_BUILD_TESTS=OFF \
      -DOSDP_BUILD_TOOLS=OFF
/* your existing main loop */
osdp_pd_transport_t uart = {
    .read = uart_read, .write = uart_write, .now_ms = millis
};
osdp_pd_init(&pd, 0x00);
osdp_pd_set_transport(&pd, &uart);
osdp_pd_set_command_handler(&pd, on_command, NULL);

for (;;) {
    osdp_pd_tick(&pd);
    /* ...the rest of your firmware... */
}

Targets Windows (MSVC, Clang, MinGW) and embedded toolchains with a C11 compiler.

Building a PD, ACU, or bus monitor?

You need an implementation small enough for constrained firmware and rigorous enough to pass real interop testing. Built by the team behind OSDP.Net and OSDP Bench.