# 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. - **One‑shot timer** – 80 ms 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. - **Multi‑format support** – Automatically detects 26‑bit, 34‑bit, and 8‑bit 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 8‑bit keypad frames, validates that the upper nibble is the bitwise complement of the lower nibble, then returns the key number (0–15). - **Safe for MicroPython** – Uses a pre‑allocated `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 ```python 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 (zero‑bit) line. - **`pin1`** – GPIO number for the DATA1 (one‑bit) 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'`: 24‑bit payload (bits 1–24 of the 26‑bit frame, parity bits removed). - `'wg34'`: 32‑bit payload (bits 1–32 of the 34‑bit frame). - `'keypad'`: integer 0–15, 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 (0–15) | 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 pull‑ups and falling‑edge 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. **One‑shot timer** – When the **first** valid bit is recorded, a hardware timer is started in `ONE_SHOT` mode with an 80 ms 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 80 ms 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 pre‑allocated `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 thread‑safe** – it is designed for the single‑threaded MicroPython environment. ## License MIT License.