[ 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...