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

89 lines
4.9 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```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 (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.