OSDPeek
Having played with access card readers for a fair while, I have always wondered why hasn’t anyone come up with a nice portable tool to check whether the readers are running Wiegand or OSDP. It would also be nice to get the used Wiegand framing, and baud rate and security configurations for OSDP.
I took it upon myself to create such a tool. Introducing OSDPeek!

Introduction
Access control readers usually use one of two alternatives: Wiegand or OSDP.
Wiegand is a legacy three-wire interface that sends a string of numbers (ID) from reader to panel with no supervision or encryption.
The more modern alternative OSDP (Open Supervised Device Protocol), runs over half-duplex RS-485. OSDP is a two-way, multi-drop bus with device addressing, polling and an optional Secure Channel, that adds mutual authentication and AES-128 protection to payloads.
Example of using OSDPeek with grabbers
OSDPeek was built to answer two field questions quickly: what bus am I looking at, and if it’s OSDP, is Secure Channel actually active (and not just “configured”)?
OSDPeek features:
- Automatic line detection, Wiegand or OSDP
- Automatic polarity detection (doesn’t matter which way around you clip OSDPeek on the bus)
- Auto baud rate detection for most common OSDP baud rates (9600, 38400, 115200)
- OSDP secure channel configuration detection
- Wiegand framing detection
- Raw bus message/frame logging on USB serial console
The rest of this post walks through the hardware, software and how it works.
Example of using OSDPeek with pogo pins
Disclaimer: OSDPeek has only been tested with a handful of readers in few different test setups and using the OSDP test vectors. If it doesn’t work in your particular setup, well, you have the source code, so fix it. :D
The protocols
Wiegand
Three-wire interface: DATA0, DATA1, GND. Both data lines idle high via pull-ups; a pulse on DATA0 is “0”, a pulse on DATA1 is “1”. Readers just emit a fixed-length unencrypted bitstream to the panel.
The most common framing is “26-bit” (H10301): (Parity bit) + 8-bit facility/site code + 16-bit card number + (Parity bit), with leading even parity and trailing odd parity.
Large number of vendor variants exist (longer bit counts, different field layouts), but the security model stays the same. Anyone on the wires can eavesdrop, replay, or emulate.
The attraction to Wiegand is its simplicity, 5 V signaling with open-collector drivers was designed to tolerate very long cable runs to a closet-mounted panel.
Wiegand in nutshell is an unencrypted one-way protocol carrying an ID number.
OSDP
The more modern alternative, replaces the simple one-way Wiegand with a proper two-way, multi-drop bus over half-duplex RS-485.
A single pair can host multiple addressed readers (PDs) talking to a control panel (CP). There are mechanisms for polling, device discovery, LED/buzzer control, tamper/supervision, and health monitoring.
OSDP also features a “Secure Channel”, the link can be mutually authenticated and encrypted (AES-128). The reader and panel negotiate a session key and the payloads become opaque to anyone tapping the line.
The packet format is explicit and easy to fingerprint on the wire: a Start-of-Message (0x53), address, length (LSB/MSB), and a CTRL byte with flags for sequence, checksum/CRC, and an optional Secure-Channel block.
Hardware
The OSDPeek hardware is built around RPi Pico 2 mounted to a custom PCB housing the frontend components. The design is simple voltage divider using high value resistors and two detection paths.
One through 2ch Schmitt trigger for the Wiegand and a second one through 4ch RS-485 receiver. The frontend has only the bare minimal components, few passives, BAT54s Schottskys for level-shifting to 3V3 and a TVS diode for some added protection.
BOM
| Component | Designation |
|---|---|
| 200k resistors for voltage divider (0603) | R1, R2, R4, R5 |
| 1k inline resistors for buffer (0603) | R6, R7 |
| 100pF capacitors for filtering (0603) | C1, C2 |
| SM712 TVS diode (SOT23) | D1 |
| BAT54s Schottky diode for level-shifting | D2, D3 |
| MAX33076EASE+ 4ch RS485 receiver | U1 |
| SN74LVC2G14DBV 2ch Schmitt trigger | U2 |
| RPi Pico/Pico 2 | A1 |
| Metz Type 059 terminal block | J1 |
| Long pin headers for the OLED | - |
| Waveshare Pico OLED 1.3 | - |
Note: When assembling the PCB, Pico goes to the same side as the other components. The screen goes to the backside (with no components), so you will need to use long pin headers that can be soldered from other side. See the picture in “Case & assembly”
The backside of the PCB is without any components meaning it is possible to use hot-plate to solder the components for fast and easy assembly.
Case & assembly
The case is not going to get any Red Dot Design awards for its looks, but it works. When 3D printing, it is a good idea to use supports for the top part of the case. The case doesn’t have any cutouts for the buttons, as they are not used currently, but the cutouts are easy to add if needed.
The assembly is straightforward. You slot the PCB in between the top and bottom halves and depending on your 3D printer, used filament, accuracy and quality, it should snap-lock due to the small fingers on either side of the case.
Software
At a high level, OSDPeek is two small instruments in one, a OSDP sniffer and a Wiegand sniffer. A lightweight analyzer stitches the bytes/bits into protocol-aware status (frame validity, CRC/checksum, secure-channel state, Wiegand length/parity/facility/card), and simple line monitoring decides whether the wires look like OSDP or Wiegand.
Below you can find, with excruciating detail, how it works.
Architecture at a glance
PIOOSDPSniffer Uses the RP2350’s PIO to run a software 8-N-1 UART receiver at multiple candidate baud rates simultaneously (115200 / 38400 / 9600). Each candidate runs in its own StateMachine bound to the same input pin. This makes it possible to run multiple UART receivers with different baud rates at the same pins and thus perform auto-baud rate detection.
OSDPCollector The collector waits for SOM=0x53, reads the declared length (LSB/MSB), and emits contiguous frames only when the length matches what’s been captured.
WiegandSniffer Two GPIO IRQs record DATA0/DATA1 pulses into a bit list.
DisplayManager Handles the display related tasks and updates.
OSDP path
Pin attach & line watching LineWatcher monitors two candidate RS-485 pins (16/18) and classifies what’s on the bus by looking at idle levels and activity. It distinguishes NC (all high due to pull-ups), Wiegand (both lines held low), and OSDP (one line toggling, the other idle) and raises events: attach, switch, idle, nc.
Multi-baud sniffing (PIO) On attach, the sniffer spins up a bundle of PIO UART receivers on the active pin, one per candidate baud. The background worker drains each RX FIFO quickly in a separate thread on core 1, feeding bytes into a per-baud OSDPCollector. Once a valid frame appears at a specific baud, OSDPeek “locks” by calling select_baud(baud) so only the winning stream consumes CPU.
Framing & validation The collector requires SOM=0x53 and a sane length (>=7) before releasing a frame to the analyzer. The analyzer recomputes CRC-16/ITU-T when CTRL.CRC is set, or validates the legacy 8-bit checksum otherwise; both are legit per the standard. If the recomputed value doesn’t match, the frame is tagged … CRC-BAD/CHK-BAD in the info string.
Secure Channel detection If CTRL.SCB is set and the frame is otherwise valid, the analyzer peeks into the SCB:
0x11 / 0x12 → CP_RAND / PD_RAND nonces -> handshake phase.
0x15 / 0x16 → MAC on (integrity) -> SCS:MAC.
0x17 / 0x18 → MAC + ENC -> SCS:MAC+ENC.
OSDPeek doesn’t derive session keys to verify the MAC (that would require the provisioned SCBK), but it reliably fingerprints mode so you can tell “cleartext” from “secure,” and whether encryption is actually engaged.
Console logging For each accepted frame the analyzer builds a compact string like:
[OSDP] Detected | addr=0 len=22 SC-MAC CRC-OK | [frame contents]
Wiegand path
Edge capture Two IRQ handlers append 0 on DATA0 falling edges and 1 on DATA1 falling edges. A 5 ms periodic timer checks if the line has been idle for > 6 ms; if yes, the current bit list is sealed as one frame. (This is conservative enough to accommodate typical Wiegand pulse/spacing timing.)
Sanity check & orientation tries Frames shorter than 24 or longer than 128 bits are discarded as noise. The decoder then tries normal, reversed, inverted, and reversed-inverted bit orders/polarities to cope with swapped lines and pull-up/pull-down oddities. First valid candidate wins.
26-bit specifics For 26-bit, parity is further checked with even parity covering the first 12 data bits (plus leading parity), and odd over the last 12 data bits (plus trailing parity). If parity passes, OSDPeek reports facility (8 bits) and card (16 bits).
Console output The console prints for every received frame
“[Wiegand] Detected | 26-bit | 0x00ABCD | fc=… id=… [lines?]”
Wiegand decoding is intentionally conservative: It validates parity only for 26-bit and labels other lengths generically (e.g., “34-bit”). There are many vendor formats; dumping every permutation is out of scope for a pocket sniffer, but parity/length already exposes “is this Wiegand?” for threat modeling.
GUI
The GUI is simple and straightforward. You plug the device in, leave it there, go and tap the reader with an access card multiple times and then go back and observe the results. The GUI persists the values even after disconnecting the device from the bus. The values are only cleared when start of a new capture session is detected.
Mode
Mode: OSDP / Wiegand while active. OSDP (I) / Wiegand (I) when idle but still cabled. NC when nothing attached.
OSDP
OSDP: baud + counter of how many frames captured
SCS: Shows HS (handshake), MAC, or MAC+ENC when Secure Channel traffic is observed.
Age: OSDP relative time since last frame, in seconds or minutes.
Wiegand
WG: bits, the detected framing bits on Wiegand bus
Age: Wiegand relative time since last frame, in seconds or minutes.
Summary
OSDPeek is a portable access control bus multitool: it tells you, whether a reader is speaking Wiegand or OSDP. And for OSDP whether you’re looking at cleartext, MAC-only, or MAC + encrypted traffic.
No guesswork, no balancing of a laptop and logic analyzer on top of a access control cabinet door, just clip on, tap a card a few times, and read the results on the display.
The OSDP side validates framing and fingerprints Secure Channel state so you can prove whether encryption is actually enabled. On legacy lines, the Wiegand path confirms bit length and parity so you can document that a site is still running an unencrypted, one-way interface that’s inherently susceptible to sniffing and replay.
Resources
In the usual manner I’m sharing all of the PCB, case designs and source code. Go wild!
- Source code download Waveshare display firmware
- Gerbers download
- Case top bottom
All the material is licensed under the MIT license
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.




