[ Even parity (1 bit) | Facility Code (8 bits) | Card Number (16 bits) | Odd parity (1 bit) ]
| 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) | 
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()
scp /path/to/Desktop/wiegand.py [email protected]:~
pip install accessgrid
pip install pillow
pip install qrcode
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() 
python3 wiegand.py 
It's janky, but it works - how we got our credentials working with UHPPOTE systems.
This guide demonstrates how to decode the widely used 26-bit Wiegand protocol using Python on a R...