Files
mpy-wiegand/README.md
2026-07-01 10:34:38 +08:00

4.9 KiB
Raw Permalink Blame History

MicroPython Wiegand Driver

A lightweight, event-driven Wiegand protocol driver for MicroPython.
Supports 26-bit Wiegand, 34-bit Wiegand, and 8-bit keypad input.
No polling, no debouncing just pure interrupt-driven collection with a one-shot timeout.

Features

  • Fully interrupt-driven Records bits on both DATA0/DATA1 falling edges.
  • Oneshot timer 80ms timeout started only on the first bit; zero CPU overhead when idle.
  • No software debouncing Relies on frame length and parity checks to reject noise.
  • Multiformat support Automatically detects 26bit, 34bit, and 8bit keypad frames.
  • Raw payload output Returns the data bits without parity bits (24 bits for WG26, 32 bits for WG34), giving the application full control over facility code / card number extraction.
  • Automatic parity validation Standard Wiegand even/odd parity check.
  • Keypad verification For 8bit keypad frames, validates that the upper nibble is the bitwise complement of the lower nibble, then returns the key number (015).
  • Safe for MicroPython Uses a preallocated bytearray buffer; no memory allocation inside interrupts.

Installation

Copy the file wiegand.py to your MicroPython device (e.g., into /lib/ or the root directory).

Usage

from machine import Pin
from wiegand import Wiegand

def on_wiegand(data_type, value):
    if data_type == 'wg26':
        facility = (value >> 16) & 0xFF
        card = value & 0xFFFF
        print("WG26 - Facility: {}, Card: {}".format(facility, card))
    elif data_type == 'wg34':
        print("WG34 raw payload: 0x{:08X}".format(value))
    elif data_type == 'keypad':
        print("Key pressed:", value)

# DATA0 on GPIO16, DATA1 on GPIO17
reader = Wiegand(pin0=16, pin1=17, callback=on_wiegand)

API

Wiegand(pin0, pin1, callback, buf_len=34)

  • pin0 GPIO number for the DATA0 (zerobit) line.
  • pin1 GPIO number for the DATA1 (onebit) line.
  • callback Function called when a valid frame is received.
    Signature: callback(data_type, value)
    • data_type (str): one of 'wg26', 'wg34', 'keypad'.
    • value (int):
      • 'wg26': 24bit payload (bits 124 of the 26bit frame, parity bits removed).
      • 'wg34': 32bit payload (bits 132 of the 34bit frame).
      • 'keypad': integer 015, the key number.
  • buf_len (optional) maximum buffer size, default 34. Must be at least as large as the longest expected frame.

Supported Formats

Format Total bits Payload bits returned Parity check
WG26 26 24 Even (first half) / Odd (second half)
WG34 34 32 Even (first half) / Odd (second half)
Keypad 8 lower nibble (015) Upper nibble must be ~lower nibble

For WG26 and WG34 the leading and trailing parity bits are stripped. The user is responsible for extracting facility code and card number from the raw payload.

How It Works

  1. Pin setup Both DATA0 and DATA1 pins are configured as inputs with internal pullups and fallingedge interrupts.
  2. Interrupt handler On each falling edge, the handler reads the state of both pins and encodes it as 0b01 (bit 0) or 0b10 (bit 1). Any other combination (e.g., both low) is ignored.
  3. Oneshot timer When the first valid bit is recorded, a hardware timer is started in ONE_SHOT mode with an 80ms period.
  4. Timeout When the timer fires, the collected bit array is copied, parsed, and the buffer is reset.
    • If the frame length is 26 or 34 and parity checks pass, the callback is called with the raw payload.
    • If the frame length is 8 and the keypad nibble inversion check passes, the key number is reported.
    • Otherwise the frame is silently discarded.
  5. Ready for next read The buffer index is set to zero immediately after the timeout, so a new card can be processed right away.

Notes

  • The 80ms timeout works for virtually all standard Wiegand readers. If you have an exceptionally slow reader, adjust the period value in self.timer.init(period=...) inside the source.
  • No debouncing is applied. If your hardware produces spurious edges, the parity or length checks will reject the frame, and no callback will fire. This keeps the interrupt handler extremely short.
  • The driver uses a preallocated bytearray it never allocates memory in the IRQ, making it safe for all MicroPython ports.
  • If your platform does not support Timer.ONE_SHOT, you can replace it with a periodic timer that calls self.timer.deinit() on the first callback.
  • This library is not threadsafe it is designed for the singlethreaded MicroPython environment.

License

MIT License.