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

71 lines
2.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
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.
from machine import Pin, Timer
class Wiegand:
def __init__(self, pin0, pin1, callback, buf_len=34):
self.pin0 = Pin(pin0, Pin.IN, Pin.PULL_UP)
self.pin1 = Pin(pin1, Pin.IN, Pin.PULL_UP)
self.callback = callback
self.buf = bytearray(buf_len)
self.idx = 0
self.timer = Timer(-1)
self.pin0.irq(trigger=Pin.IRQ_FALLING, handler=self._irq)
self.pin1.irq(trigger=Pin.IRQ_FALLING, handler=self._irq)
def _irq(self, p):
# 同时读取两根线,组合成 0b01 (bit=0) 或 0b10 (bit=1)
state = (self.pin1.value() << 1) | self.pin0.value()
if state not in (0b01, 0b10):
return
if self.idx < len(self.buf):
self.buf[self.idx] = state
self.idx += 1
if self.idx == 1:
# 只启动一次80ms 后触发解析
self.timer.init(period=80, mode=Timer.ONE_SHOT, callback=self._timeout)
def _timeout(self, t):
n = self.idx
self.idx = 0
data = bytes(self.buf[:n]) # 拷贝,防止中断覆盖
self._parse(n, data)
def _parse(self, n, data):
if n == 26 and self._check_parity(data, 26):
# 剔除首尾校验位,保留中间 24 位有效载荷
payload = self._extract_bits(data, 1, 24)
self.callback('wg26', payload)
elif n == 34 and self._check_parity(data, 34):
# 剔除首尾校验位,保留中间 32 位有效载荷
payload = self._extract_bits(data, 1, 32)
self.callback('wg34', payload)
elif n == 8:
# 键盘低4位键值高4位为低4位取反
val = self._extract_bits(data, 0, 8)
if ((val >> 4) ^ (val & 0x0F)) == 0x0F:
self.callback('keypad', val & 0x0F)
# 其余长度或校验失败直接丢弃
def _check_parity(self, data, bits):
"""标准 Wiegand 奇偶校验前一半偶数个1后一半奇数个1"""
half = bits // 2
cnt_even = 0
cnt_odd = 0
for i in range(bits):
if data[i] == 0b10: # 该比特为 1
if i < half:
cnt_even += 1
else:
cnt_odd += 1
return (cnt_even % 2 == 0) and (cnt_odd % 2 == 1)
def _extract_bits(self, data, start, count):
"""从 data 中提取连续比特MSB first返回整数"""
val = 0
for i in range(start, start + count):
val <<= 1
if data[i] == 0b10:
val |= 1
return val