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