init repo
This commit is contained in:
70
wiegand.py
Normal file
70
wiegand.py
Normal file
@@ -0,0 +1,70 @@
|
||||
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
|
||||
Reference in New Issue
Block a user