from machine import Pin, PWM import time, network, espnow import motors BUZZ_PIN = 39 PAIR_BEEP_INTERVAL_MS = 1400 # ---------------- ESP-NOW helpers (match transmitter.py) ---------------- _espnow_sta = None _espnow = None _espnow_peers = [] _espnow_broadcast = b'\xff\xff\xff\xff\xff\xff' def _espnow_parse_mac(mac): if isinstance(mac, bytes): return mac if isinstance(mac, bytearray): return bytes(mac) if isinstance(mac, str): mac = mac.replace(':', '').replace('-', '').replace(' ', '') if len(mac) != 12: raise ValueError('MAC must be 12 hex chars') return bytes(int(mac[i : i + 2], 16) for i in range(0, 12, 2)) raise ValueError('MAC must be bytes or string') def _espnow_init(channel=6, rate='RATE_LORA_250K'): global _espnow_sta, _espnow _espnow_sta = network.WLAN(network.STA_IF) _espnow_sta.active(True) _espnow_sta.disconnect() _espnow_sta.config(channel=int(channel), protocol=network.WLAN.PROTOCOL_LR) _espnow = espnow.ESPNow() _espnow.active(True) rate_val = getattr(espnow, str(rate), espnow.RATE_LORA_250K) _espnow.config(rate=rate_val) return _espnow def _espnow_my_mac(): if _espnow_sta is None: _espnow_init() return _espnow_sta.config('mac') def _espnow_add_peer(mac): global _espnow_peers if _espnow is None: _espnow_init() peer = _espnow_parse_mac(mac) if peer not in _espnow_peers: _espnow.add_peer(peer) _espnow_peers.append(peer) return peer def _espnow_pack(data): if isinstance(data, (list, tuple)): return ','.join(str(v) for v in data) return str(data) def _espnow_send_peer(mac, data): if _espnow is None: _espnow_init() peer = _espnow_add_peer(mac) payload = _espnow_pack(data) _espnow.send(peer, payload) def _espnow_try_num(s): try: return int(s) except: pass try: return float(s) except: return s def _espnow_unpack(msg): if msg is None: return [] if isinstance(msg, bytes): msg = msg.decode('utf-8', 'ignore') parts = str(msg).split(',') out = [] for p in parts: p = p.strip() if p == '': continue out.append(_espnow_try_num(p)) return out def _espnow_recv(timeout_ms=10): if _espnow is None: _espnow_init() host, msg = _espnow.recv(timeout_ms) return host, _espnow_unpack(msg) def _mac_eq(a, b): if a is None or b is None: return False return bytes(a) == bytes(b) # ---------------- Pairing (receiver side) ---------------- PAIR_PREFIX = 'PAIR_REQ:' PAIR_ACK_PREFIX = 'PAIR_ACK:' PEER_FILE = 'peer_mac.txt' peer_mac = None def load_peer(): try: with open(PEER_FILE, 'rb') as f: mac = f.read() if mac and len(mac) == 6: return mac except: pass return None def save_peer(mac): with open(PEER_FILE, 'wb') as f: f.write(mac) def buzz_pairing_chirp(): """Very short tone while waiting to pair (passive piezo: PWM; active: GPIO pulse).""" try: pwm = PWM(Pin(BUZZ_PIN, Pin.OUT), freq=3800, duty_u16=24576) time.sleep_ms(14) pwm.deinit() except: b = Pin(BUZZ_PIN, Pin.OUT) b.on() time.sleep_ms(12) b.off() def try_pair_from_message(host, msg, my_hex): """If msg is a PAIR_REQ from a transmitter, ACK and return True when paired.""" global peer_mac if not (msg and isinstance(msg, list) and len(msg) >= 2 and msg[0] == PAIR_PREFIX): return False tx_hex = str(msg[1]).strip().lower() tx_mac = None if len(tx_hex) == 12: try: tx_mac = _espnow_parse_mac(tx_hex) except: tx_mac = None if tx_mac is None and host: tx_mac = bytes(host) if not isinstance(host, bytes) else host if not tx_mac: return False _espnow_add_peer(tx_mac) _espnow_send_peer(tx_mac, [PAIR_ACK_PREFIX, my_hex]) save_peer(tx_mac) peer_mac = tx_mac print('Paired with transmitter', tx_hex if len(tx_hex) == 12 else tx_mac.hex()) return True # Same channel and rate as transmitter.py _espnow_init(1, 'RATE_LORA_500K') peer_mac = load_peer() if peer_mac: _espnow_add_peer(peer_mac) my_mac_hex = _espnow_my_mac().hex() last_pair_beep = time.ticks_ms() print('Receiver ready. My MAC:', my_mac_hex, 'Peer:', peer_mac.hex() if peer_mac else '(searching)') leftMotor = motors.Motor(13, 14) rightMotor = motors.Motor(15, 16) # ---------------- Main loop ---------------- while True: if not peer_mac: host, msg = _espnow_recv(timeout_ms=120) if try_pair_from_message(host, msg, my_mac_hex): pass else: if time.ticks_diff(time.ticks_ms(), last_pair_beep) >= PAIR_BEEP_INTERVAL_MS: buzz_pairing_chirp() last_pair_beep = time.ticks_ms() time.sleep_ms(10) continue host, data = _espnow_recv(timeout_ms=50) if host and data and _mac_eq(host, peer_mac): # Transmitter payload: 4 axes, adc_1, adc_10, btn2,5,6,7,11,12 print(data) time.sleep_ms(5) from machine import Pin, PWM import time, network, espnow import motors BUZZ_PIN = 39 PAIR_BEEP_INTERVAL_MS = 1400 # ---------------- ESP-NOW helpers (match transmitter.py) ---------------- _espnow_sta = None _espnow = None _espnow_peers = [] _espnow_broadcast = b'\xff\xff\xff\xff\xff\xff' def _espnow_parse_mac(mac): if isinstance(mac, bytes): return mac if isinstance(mac, bytearray): return bytes(mac) if isinstance(mac, str): mac = mac.replace(':', '').replace('-', '').replace(' ', '') if len(mac) != 12: raise ValueError('MAC must be 12 hex chars') return bytes(int(mac[i : i + 2], 16) for i in range(0, 12, 2)) raise ValueError('MAC must be bytes or string') def _espnow_init(channel=6, rate='RATE_LORA_250K'): global _espnow_sta, _espnow _espnow_sta = network.WLAN(network.STA_IF) _espnow_sta.active(True) _espnow_sta.disconnect() _espnow_sta.config(channel=int(channel), protocol=network.WLAN.PROTOCOL_LR) _espnow = espnow.ESPNow() _espnow.active(True) rate_val = getattr(espnow, str(rate), espnow.RATE_LORA_250K) _espnow.config(rate=rate_val) return _espnow def _espnow_my_mac(): if _espnow_sta is None: _espnow_init() return _espnow_sta.config('mac') def _espnow_add_peer(mac): global _espnow_peers if _espnow is None: _espnow_init() peer = _espnow_parse_mac(mac) if peer not in _espnow_peers: _espnow.add_peer(peer) _espnow_peers.append(peer) return peer def _espnow_pack(data): if isinstance(data, (list, tuple)): return ','.join(str(v) for v in data) return str(data) def _espnow_send_peer(mac, data): if _espnow is None: _espnow_init() peer = _espnow_add_peer(mac) payload = _espnow_pack(data) _espnow.send(peer, payload) def _espnow_try_num(s): try: return int(s) except: pass try: return float(s) except: return s def _espnow_unpack(msg): if msg is None: return [] if isinstance(msg, bytes): msg = msg.decode('utf-8', 'ignore') parts = str(msg).split(',') out = [] for p in parts: p = p.strip() if p == '': continue out.append(_espnow_try_num(p)) return out def _espnow_recv(timeout_ms=10): if _espnow is None: _espnow_init() host, msg = _espnow.recv(timeout_ms) return host, _espnow_unpack(msg) def _mac_eq(a, b): if a is None or b is None: return False return bytes(a) == bytes(b) # ---------------- Pairing (receiver side) ---------------- PAIR_PREFIX = 'PAIR_REQ:' PAIR_ACK_PREFIX = 'PAIR_ACK:' PEER_FILE = 'peer_mac.txt' peer_mac = None def load_peer(): try: with open(PEER_FILE, 'rb') as f: mac = f.read() if mac and len(mac) == 6: return mac except: pass return None def save_peer(mac): with open(PEER_FILE, 'wb') as f: f.write(mac) def buzz_pairing_chirp(): """Very short tone while waiting to pair (passive piezo: PWM; active: GPIO pulse).""" try: pwm = PWM(Pin(BUZZ_PIN, Pin.OUT), freq=3800, duty_u16=24576) time.sleep_ms(14) pwm.deinit() except: b = Pin(BUZZ_PIN, Pin.OUT) b.on() time.sleep_ms(12) b.off() def try_pair_from_message(host, msg, my_hex): """If msg is a PAIR_REQ from a transmitter, ACK and return True when paired.""" global peer_mac if not (msg and isinstance(msg, list) and len(msg) >= 2 and msg[0] == PAIR_PREFIX): return False tx_hex = str(msg[1]).strip().lower() tx_mac = None if len(tx_hex) == 12: try: tx_mac = _espnow_parse_mac(tx_hex) except: tx_mac = None if tx_mac is None and host: tx_mac = bytes(host) if not isinstance(host, bytes) else host if not tx_mac: return False _espnow_add_peer(tx_mac) _espnow_send_peer(tx_mac, [PAIR_ACK_PREFIX, my_hex]) save_peer(tx_mac) peer_mac = tx_mac print('Paired with transmitter', tx_hex if len(tx_hex) == 12 else tx_mac.hex()) return True # Same channel and rate as transmitter.py _espnow_init(1, 'RATE_LORA_500K') peer_mac = load_peer() if peer_mac: _espnow_add_peer(peer_mac) my_mac_hex = _espnow_my_mac().hex() last_pair_beep = time.ticks_ms() print('Receiver ready. My MAC:', my_mac_hex, 'Peer:', peer_mac.hex() if peer_mac else '(searching)') leftMotor = motors.Motor(13, 14) rightMotor = motors.Motor(15, 16) # ---------------- Main loop ---------------- while True: if not peer_mac: host, msg = _espnow_recv(timeout_ms=120) if try_pair_from_message(host, msg, my_mac_hex): pass else: if time.ticks_diff(time.ticks_ms(), last_pair_beep) >= PAIR_BEEP_INTERVAL_MS: buzz_pairing_chirp() last_pair_beep = time.ticks_ms() time.sleep_ms(10) continue host, data = _espnow_recv(timeout_ms=50) if host and data and _mac_eq(host, peer_mac): # Transmitter payload: 4 axes, adc_1, adc_10, btn2,5,6,7,11,12 print(data) time.sleep_ms(5)