← Guides / Access Control Protocols
May 16, 2025

Decoding the Wiegand protocol

Access Control Protocols
Auston Bunsen
Overview
Wiegand (pronounced vee-gand) is a standardized interface protocol commonly utilized to communicate data from readers to door controllers, within Physical Access Control Systems (PACS). Manufacturers widely adopted the Security Industry Association’s (SIA) 26-bit Wiegand standard to establish a common device interface, enabling cross-compatibility among various access control hardware.

In this guide, we’ll demonstrate how to decode the widely used 26-bit Wiegand protocol using Python on a Raspberry Pi. We’ll detail the hardware wiring, code implementation, and practical decoding of Wiegand card swipes.

Pre-requisites:
  • Raspberry Pi with GPIO pins
  • Python 3.x installed
  • Basic command-line and Python skills
  • Wiegand-compatible card reader (we'll use VTAP)
  • Jumper wires, breadboard, two 550kΩ pull-up resistors, and two current-limiting 330kΩ resistors
  • ethernet cable
  • ethernet to usb-c adapter
  • microsd to usb-c adapter
  • USB-C to power cable

Understanding the 26-bit Wiegand Protocol
The Wiegand protocol transmits data using two separate wires:

  • Data 0 (D0) transmits binary 0 through short low-voltage pulses.
  • Data 1 (D1) transmits binary 1 similarly through short low-voltage pulses.

Both wires idle HIGH (at about 5V or 3.3V, depending on your reader) and only pulse LOW briefly (approximately 50 microseconds) to send bits.

The standard 26-bit Wiegand format consists of:

[ Even parity (1 bit) | Facility Code (8 bits) | Card Number (16 bits) | Odd parity (1 bit) ]

Bit Structure Breakdown:

  • Even Parity (bit 1): Covers bits 2-13
  • Facility Code (bits 2-9): Range 0–255
  • Card Number (bits 10-25): Range 0–65,535
  • Odd Parity (bit 26): Covers bits 14-25

Limitations of the 26-bit Format:

A notable limitation of the SIA 26-bit Wiegand standard is the restricted number of unique IDs it can handle. The facility code field is limited to 8 bits (256 total values from 0–255), and the card number field is limited to 16 bits (65,536 total values from 0–65,535).

The total number of unique IDs that this standard imposes ( 256 x 65536 ) which is 16,777,216 unique IDs.

For large-scale deployments, this limited addressing space can become inadequate, necessitating the adoption of extended Wiegand formats (34-bit, 37-bit, etc.).


Wiegand Data Transmission Explained: 

When an NFC pass is scanned, it sends a binary code to the Wiegand reader over two lines: D0 and D1.

These lines pulse LOW (0V) briefly to represent binary data.
 • D0 pulse (LOW) = Binary 0
 • D1 pulse (LOW) = Binary 1

The transmission begins when the scan is detected (time = 50μs). The signal is divided into two main parts:

 • Facility Code: starts at 100μs and ends at 500μs
 • Card Number: starts at 500μs and ends at 1250μs

Each bit is spaced 50μs apart. The combined binary sequence builds the full identity code used by access control systems to validate the NFC card.

This table shows the data being transmitted over time.

This diagram demonstrates the electrical signal pulses to the D0 and D1 pins over time.

Wiegand data transfer over time
Wiegand data transfer over time



This is a google sheet that shows the bits sent over time and was used to create the diagram above.

Now that you understand how wiegand works, let's proceed with the setup.

Uploading Raspberry OS to SD Card
Now it is time to write the raspberry pi os onto the MicroSD card.

Download the Raspberry Pi Imager from this site.

Insert the MicroSD card into the SD reader of the microsd to usb-c adapter and connect your adapter to the computer.

On your computer, you should be able to see the SD card. When you open the Raspberry Pi Imager app on the computer it should show you three options to choose.

Raspberry Pi Imager Home screen
Raspberry Pi Imager Home screen


  • Select the device, Raspberry Pi 5. Select the Raspberry Pi OS (64 Bit), which should be the default. 
  • Next select Choose storage and the first the device connected should be the MicroSD card. 
  • Click NEXT.
Now you will be prompted to apply customization settings. Select EDIT SETTINGS.

Apply Customization settings Screen
Apply Customization settings Screen



The next steps are very important to get exactly correct.

Set hostname, username, password
Set hostname, username, password


  • Set hostname: raspberrypi.local. {Should be the default}.
    • Set username and password:
      username: pi
      password: raspberrypi
  • Configure Wireless LAN:
    • SSID
    • Password

Note: To connect the Raspberry Pi to the internet you have to connect to a WiFi network that has a WPA2 connection. If your WiFi system uses WPA3, you could either reconfigure the settings to use WPA2 or use a travel router as an intermediary that rebroadcasts the WiFi with WPA2.

  • Set locale settings. It need to be updated to your timezone. We selected America/New_York, keyboard layout: us.
  • Scroll up and toggle to the next tab, Services. 
  • Click Enable SSH and select use password authentication. Click SAVE.
Now you will see the apply customization settings page again and you will select YES.
Erase existing content screen
Erase existing content screen



Select the YES for are you sure you want to continue page. This will upload the raspberry pi os to the MicroSD card.

If you are using the same adapter and SD card as us it should not take more than 10 minutes, but if you are using another adapter it is possible the os upload takes around 30 minutes. 

Once the upload is done, you can remove the microSD card from the reader and insert it into the RaspberryPi MicroSD card slot. Disconnect the usb-c adapter from the computer. (The software automatically ejects the sd card from the mac when the upload is finished.)

Accessing Raspberry Pi
It's time to get access to the Raspberry Pi.  Now, on your computer, connect to the travel router's WiFi network that you just configured using the SSID and Password.

Connected to travel router's Wifi: GL-AXT1800-700
Connected to travel router's Wifi: GL-AXT1800-700



Connect your raspberry pi to power, make sure the green light is flickering.

Wiring your VTAP Reader to the Raspberry Pi
Disconnect your Raspberry Pi from power. Wire your VTAP reader to the raspberry pi as follows:

Reader Wire Raspberry Pi GPIO Pin
Data 0 (D0, usually green) GPIO17 (Pin 11)
Data 1 (D1, usually white) GPIO27 (Pin 13)
Ground (GND, usually black) Ground (Pin 9)

Note: The reason the table and the breadboard view are not identical is because you need to add the two current-limiting 330kΩ resistors to limit any unexpected current surges and help buffer the voltage.


This is the end state of how the breadboard should look:


Breadboard view of System Setup
Breadboard view of System Setup



Establishing a Common Ground

It’s critical to connect the reader’s GND wire directly to the Pi’s GND pin. This ensures a common voltage reference, allowing signals to be read reliably. Failure to establish this common ground can cause erratic behavior or hardware malfunction.

Using Pull-up Resistors

Since Wiegand lines must idle HIGH, use two 550kΩ pull-up resistors (Resistor that is Green Green Brown Gold in diagram) to ensure signal integrity:
  • Connect one pull-up resistor from D0 (GPIO17) to the 3.3V pin (Pin 1) on the Raspberry Pi.
  • Connect another pull-up resistor from D1 (GPIO27) to the 3.3V pin (Pin 1).

Note: If you do not have a 550kΩ resistor you can connect a 220kΩ and a 330kΩ resistor in series by wrapping them together. Here is a link on how to connect resistors in series.



Wrapping of two resistors in series making 550 kΩ
Wrapping of two resistors in series making 550 kΩ


Most wiegand readers do not include these resistors internally, making external pull-ups mandatory.

To protect the Raspberry Pi’s GPIO pins, two additional current-limiting resistors are added in series:

  • Place one resistor (Orange Orange Brown Gold in diagram)  between D0 and GPIO17.
  • Place another resistor (Orange Orange Brown Gold in diagram) between D1 and GPIO27.

Now that we have gone through the steps; again, here is the overview of the breadboard.

Overview of the Breadboard set up
Overview of the Breadboard set up


Note: We recommend using a multimeter to test the voltage across the VTAP ground and the D0 (GPIO17) cable that is attached to Pin 11 and measure again with VTAP ground and D1 (GPIO27) pin that is attached to Pin 13. If you see a voltage greater than 3.3V being sent to the RaspberryPi in any of these readings then you run the risk of frying your raspberry pi because it can only handle 3.3V. To add even more resistors to the setup to try to lower the voltage.


Python Implementation of the Wiegand Decoder
This Python script accurately decodes the 26-bit Wiegand protocol by monitoring GPIO interrupts on a Raspberry Pi. 

import RPi.GPIO as GPIO import time import threading # GPIO pin assignments D0_PIN = 17 D1_PIN = 27 # Global state variables bitstream = [] lock = threading.Lock() timer = None WIEGAND_TIMEOUT = 0.025 # 25 milliseconds timeout def reset_bitstream(): global bitstream bitstream = [] def decode_bitstream(bits): if len(bits) != 26: print(f"Invalid bit length ({len(bits)} bits). Expected 26 bits.") return even_parity = bits[0] facility_code = int(''.join(bits[1:9]), 2) card_number = int(''.join(bits[9:25]), 2) odd_parity = bits[25] print("\n--- Wiegand 26-bit Decoded ---") print(f"Facility Code: {facility_code}") print(f"Card Number: {card_number}") print(f"Even Parity: {even_parity}") print(f"Odd Parity: {odd_parity}") print(f"Raw Bitstream: {''.join(bits)}") print("------------------------------\n") def timer_expired(): global bitstream with lock: bits = bitstream.copy() reset_bitstream() decode_bitstream(bits) def restart_timer(): global timer if timer: timer.cancel() timer = threading.Timer(WIEGAND_TIMEOUT, timer_expired) timer.start() def handle_bit(bit): with lock: bitstream.append(bit) restart_timer() def d0_callback(channel): handle_bit('0') def d1_callback(channel): handle_bit('1') def setup_gpio(): GPIO.setmode(GPIO.BCM) GPIO.setup(D0_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(D1_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.add_event_detect(D0_PIN, GPIO.FALLING, callback=d0_callback) GPIO.add_event_detect(D1_PIN, GPIO.FALLING, callback=d1_callback) print("Ready to decode Wiegand input. Swipe a card...") if __name__ == "__main__": try: setup_gpio() while True: time.sleep(1) except KeyboardInterrupt: print("\nExiting.") finally: if timer: timer.cancel() GPIO.cleanup()

How the Code Works:

  • Falling-edge interrupts on GPIO17 (D0) or GPIO27 (D1) trigger bit capture.
  • Captured bits are stored in a thread-safe list (bitstream).
  • Each bit arrival restarts a 25ms timeout timer.
  • When no bit arrives within the timeout, the timer fires and the bitstream is decoded.

SSH into your Raspberry Pi
Connect the Raspberry Pi to power.  Use SCP to copy the wiegand.py script from your computer to the raspberry pi.

Open a new terminal screen in mac and write this command. The wiegand.py file should be on your desktop. 

scp /path/to/Desktop/wiegand.py [email protected]:~

Once the file exists on the pi, open a new terminal screen in mac and write this command:


It should prompt you for your password.

Note: If you are waiting more than 20 seconds for it show a password option on terminal click CNTRL+C and try to run the SSH command again. Check that all the connections are made and disconnect and reconnect the power to the pi. 


Configuring the VTAP Wiegand reader
AccessGrid has a handy VTAP configuration package generator, but technically speaking, any reader that transmits Wiegand data will get the job done.

If you use AccessGrid to configure the VTAP, you can navigate to the Reader Config area of the web console, and generate some configs:

AccessGrid VTAP config generator
AccessGrid VTAP config generator



Next unzip the package, open the folder in a file explorer (finder for Mac), connect your VTAP via a USB-C to USB-C connection and drag the contents of the folder onto the VTAP. 

Eject/restart the VTAP from the mac. That's it, you're done. 

Now you just need to power the reader and wire it into your Raspberry Pi as explained in the Wiring your Reader to Raspberry Pi section above. 

Issuing a pass with Wiegand data
Issuing a pass for this is perfectly do-able with the AccessGrid platform. Since we're using python on the Raspberry Pi, let's use it on our computer to issue an Apple Pass! 

First we need to install the accessgrid python package:

pip install accessgrid

If you do not have them, you also need to install these other python packages to see the image and qrcode:

pip install pillow pip install qrcode

Now we need to create a script to issue a pass - which would have been set up in our web console. Let's put the following into run.py:

import qrcode from PIL import Image from accessgrid import AccessGrid # get these from your access grid account account_id = "[fill this in]" secret_key = "[fill this in]" card_template_id = "[fill this in]" client = AccessGrid(account_id, secret_key) card = client.access_cards.provision( card_template_id=card_template_id, employee_id="101010101", card_number="42069", site_code="11", full_name="Jose Casanova", email="[email protected]", phone_number="+13058129479", classification="Employee", title="CEO", start_date="2025-04-03T22:46:25.601Z", expiration_date="2026-04-20T22:46:25.601Z" ) print(card.id) print(card.url) qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=4, ) qr.add_data(card.url) qr.make(fit=True) # create the image img = qr.make_image(fill_color="black", back_color="white") img.show()

Run the file, you should be receiving an sms message to the phone_number above that says "Install your NFC Key here: {https://my.nfckey.co/a/{some_token}". 

Open the link on your smartphone and follow the steps to install the pass. 

Once it's installed, move on to the next step and run a demonstration, you need to go back to the terminal screen and ssh into your Raspberry Pi.

Run the code on the Raspberry Pi and Scan the pass
This is the setup looks like for me.

Wiegand VTAP and Raspberry Pi setup
Wiegand VTAP and Raspberry Pi setup


Now that you've got everything set up (raspberry pi, reader, pass on phone). Go ahead and SSH into the Raspberry Pi and write this command on the terminal: 

python3 wiegand.py

Hold your smartphone near the VTAP reader with the pass and wait for a beep. The terminal screen should print a decoded data value like the one below.
Terminal screen demonstrating successful bitstream
Terminal screen demonstrating successful bitstream



If you look at the raw bitstream, the full 26-bit raw bitstream matches the ones that were pulsed.

Conclusion
Congratulations—you’ve successfully implemented a working 26-bit Wiegand decoder using Python and a Raspberry Pi. You’ve learned the physical wiring required, implemented a robust GPIO-based decoding script, and understood the inherent limitations of this legacy protocol, notably the small addressing range of 8 bits (0-255) for facility codes and 16 bits (0-65,535) for card numbers.

For feedback, improvements, or deeper exploration into modern credentialing, please reach out via Intercom in the bottom right. 

Let’s build the future of access control together!

© AccessGrid 2024
Privacy
Terms