From e5f42e099e44b0f80eaaaea7f6c6fcaf055dead0 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sun, 12 Apr 2026 02:39:37 +1200 Subject: [PATCH] chore: remove esp32 firmware tree and dev mpremote helper Made-with: Cursor --- dev.py | 53 -------- esp32/benchmark_peers.py | 112 ----------------- esp32/main.py | 253 --------------------------------------- esp32/msg.json | 21 ---- scripts/cp-esp32-main.sh | 4 - 5 files changed, 443 deletions(-) delete mode 100755 dev.py delete mode 100644 esp32/benchmark_peers.py delete mode 100644 esp32/main.py delete mode 100644 esp32/msg.json delete mode 100644 scripts/cp-esp32-main.sh diff --git a/dev.py b/dev.py deleted file mode 100755 index 6ea8abc..0000000 --- a/dev.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 - -import subprocess -import serial -import sys - -print(sys.argv) - -# Extract port (first arg if it's not a command) -commands = ["src", "lib", "ls", "reset", "follow", "db"] -port = None -if len(sys.argv) > 1 and sys.argv[1] not in commands: - port = sys.argv[1] - - -for cmd in sys.argv[1:]: - print(cmd) - match cmd: - case "src": - if port: - subprocess.call(["mpremote", "connect", port, "fs", "cp", "-r", ".", ":" ], cwd="src") - else: - print("Error: Port required for 'src' command") - case "lib": - if port: - subprocess.call(["mpremote", "connect", port, "fs", "cp", "-r", "lib", ":" ]) - else: - print("Error: Port required for 'lib' command") - case "ls": - if port: - subprocess.call(["mpremote", "connect", port, "fs", "ls", ":" ]) - else: - print("Error: Port required for 'ls' command") - case "reset": - if port: - with serial.Serial(port, baudrate=115200) as ser: - ser.write(b'\x03\x03\x04') - else: - print("Error: Port required for 'reset' command") - case "follow": - if port: - with serial.Serial(port, baudrate=115200) as ser: - while True: - if ser.in_waiting > 0: # Check if there is data in the buffer - data = ser.readline().decode('utf-8').strip() # Read and decode the data - print(data) - else: - print("Error: Port required for 'follow' command") - case "db": - if port: - subprocess.call(["mpremote", "connect", port, "fs", "cp", "-r", "db", ":" ]) - else: - print("Error: Port required for 'db' command") diff --git a/esp32/benchmark_peers.py b/esp32/benchmark_peers.py deleted file mode 100644 index 49f4f6e..0000000 --- a/esp32/benchmark_peers.py +++ /dev/null @@ -1,112 +0,0 @@ -# Benchmark: LRU eviction vs add-then-remove-after-use on ESP32. -# Run on device: mpremote run esp32/benchmark_peers.py -# (add/del_peer are timed; send() may fail if no peer is listening - timing still valid) -import espnow -import network -import time - -BROADCAST = b"\xff\xff\xff\xff\xff\xff" -MAX_PEERS = 20 -ITERATIONS = 50 -PAYLOAD = b"x" * 32 # small payload - -network.WLAN(network.STA_IF).active(True) -esp = espnow.ESPNow() -esp.active(True) -esp.add_peer(BROADCAST) - -# Build 19 dummy MACs so we have 20 peers total (broadcast + 19). -def mac(i): - return bytes([0, 0, 0, 0, 0, i]) -peers_list = [mac(i) for i in range(1, 20)] -for p in peers_list: - esp.add_peer(p) - -# One "new" MAC we'll add/remove. -new_mac = bytes([0, 0, 0, 0, 0, 99]) - -def bench_lru(): - """LRU: ensure_peer (evict oldest + add new), send, update last_used.""" - last_used = {BROADCAST: time.ticks_ms()} - for p in peers_list: - last_used[p] = time.ticks_ms() - # Pre-remove one so we have 19; ensure_peer(new) will add 20th. - esp.del_peer(peers_list[-1]) - last_used.pop(peers_list[-1], None) - # Now 19 peers. Each iteration: ensure_peer(new) -> add_peer(new), send, update. - # Next iter: ensure_peer(new) -> already there, just send. So we need to force - # eviction each time: use a different "new" each time so we always evict+add. - t0 = time.ticks_us() - for i in range(ITERATIONS): - addr = bytes([0, 0, 0, 0, 0, 50 + (i % 30)]) # 30 different "new" MACs - peers = esp.get_peers() - peer_macs = [p[0] for p in peers] - if addr not in peer_macs: - if len(peer_macs) >= MAX_PEERS: - oldest_mac = None - oldest_ts = time.ticks_ms() - for m in peer_macs: - if m == BROADCAST: - continue - ts = last_used.get(m, 0) - if ts <= oldest_ts: - oldest_ts = ts - oldest_mac = m - if oldest_mac is not None: - esp.del_peer(oldest_mac) - last_used.pop(oldest_mac, None) - esp.add_peer(addr) - esp.send(addr, PAYLOAD) - last_used[addr] = time.ticks_ms() - t1 = time.ticks_us() - return time.ticks_diff(t1, t0) - -def bench_add_then_remove(): - """Add peer, send, del_peer (remove after use). At 20 we must del one first.""" - # Start full: 20 peers. To add new we del any one, add new, send, del new. - victim = peers_list[0] - t0 = time.ticks_us() - for i in range(ITERATIONS): - esp.del_peer(victim) # make room - esp.add_peer(new_mac) - esp.send(new_mac, PAYLOAD) - esp.del_peer(new_mac) - esp.add_peer(victim) # put victim back so we're at 20 again - t1 = time.ticks_us() - return time.ticks_diff(t1, t0) - -def bench_send_existing(): - """Baseline: send to existing peer only (no add/del).""" - t0 = time.ticks_us() - for _ in range(ITERATIONS): - esp.send(peers_list[0], PAYLOAD) - t1 = time.ticks_us() - return time.ticks_diff(t1, t0) - -print("ESP-NOW peer benchmark ({} iterations)".format(ITERATIONS)) -print() - -# Baseline: send to existing peer -try: - us = bench_send_existing() - print("Send to existing peer only: {:>8} us total {:>7.1f} us/iter".format(us, us / ITERATIONS)) -except Exception as e: - print("Send existing failed:", e) -print() - -# LRU: evict oldest then add new, send -try: - us = bench_lru() - print("LRU (evict oldest + add + send): {:>8} us total {:>7.1f} us/iter".format(us, us / ITERATIONS)) -except Exception as e: - print("LRU failed:", e) -print() - -# Add then remove after use -try: - us = bench_add_then_remove() - print("Add then remove after use: {:>8} us total {:>7.1f} us/iter".format(us, us / ITERATIONS)) -except Exception as e: - print("Add-then-remove failed:", e) -print() -print("Done.") diff --git a/esp32/main.py b/esp32/main.py deleted file mode 100644 index da091db..0000000 --- a/esp32/main.py +++ /dev/null @@ -1,253 +0,0 @@ -# Serial-to-ESP-NOW bridge: JSON in both directions on UART + ESP-NOW. -# -# Pi → UART (two supported forms): -# A) Legacy: 6 bytes destination MAC + UTF-8 JSON payload (one write = one frame). -# B) Newline JSON: one object per line, UTF-8, ending with \n -# - Multicast via ESP32: {"m":"split","peers":["12hex",...],"body":{...}} -# - Unicast / broadcast: {"to":"12hex","v":"1",...} (all keys except to/dest go to peers) -# -# ESP-NOW → Pi: newline-delimited JSON, one object per packet: -# {"dir":"espnow_rx","from":"<12hex>","payload":{...}} if body was JSON -# {"dir":"espnow_rx","from":"<12hex>","payload_text":"..."} if UTF-8 not JSON -# {"dir":"espnow_rx","from":"<12hex>","payload_b64":"..."} if binary -from machine import Pin, UART -import espnow -import json -import network -import time -import ubinascii - -UART_BAUD = 912000 -BROADCAST = b"\xff\xff\xff\xff\xff\xff" -MAX_PEERS = 20 -WIFI_CHANNEL = 6 - -sta = network.WLAN(network.STA_IF) -sta.active(True) -sta.config(pm=network.WLAN.PM_NONE, channel=WIFI_CHANNEL) -print("WiFi STA channel:", sta.config("channel"), "(WIFI_CHANNEL=%s)" % WIFI_CHANNEL) - -esp = espnow.ESPNow() -esp.active(True) -esp.add_peer(BROADCAST) - -uart = UART(1, UART_BAUD, tx=Pin(21), rx=Pin(6)) - -last_used = {BROADCAST: time.ticks_ms()} -uart_rx_buf = b"" - -ESP_ERR_ESPNOW_EXIST = -12395 - - -def ensure_peer(addr): - peers = esp.get_peers() - peer_macs = [p[0] for p in peers] - if addr in peer_macs: - return - if len(peer_macs) >= MAX_PEERS: - oldest_mac = None - oldest_ts = time.ticks_ms() - for mac in peer_macs: - if mac == BROADCAST: - continue - ts = last_used.get(mac, 0) - if ts <= oldest_ts: - oldest_ts = ts - oldest_mac = mac - if oldest_mac is not None: - esp.del_peer(oldest_mac) - last_used.pop(oldest_mac, None) - try: - esp.add_peer(addr) - except OSError as e: - if e.args[0] != ESP_ERR_ESPNOW_EXIST: - raise - - -def try_apply_bridge_config(obj): - """Pi sends {"m":"bridge","ch":1..11} — set STA channel only; do not ESP-NOW forward.""" - if not isinstance(obj, dict) or obj.get("m") != "bridge": - return False - ch = obj.get("ch") - if ch is None: - ch = obj.get("wifi_channel") - if ch is None: - return True - try: - n = int(ch) - if 1 <= n <= 11: - sta.config(pm=network.WLAN.PM_NONE, channel=n) - print("Bridge STA channel ->", n) - except Exception as e: - print("bridge config:", e) - return True - - -def send_split_from_obj(obj): - """obj has m=split, peers=[12hex,...], body=dict.""" - body = obj.get("body") - if body is None: - return - try: - out = json.dumps(body).encode("utf-8") - except (TypeError, ValueError): - return - for peer in obj.get("peers") or []: - if not isinstance(peer, str) or len(peer) != 12: - continue - try: - mac = bytes.fromhex(peer) - except ValueError: - continue - if len(mac) != 6: - continue - ensure_peer(mac) - esp.send(mac, out) - last_used[mac] = time.ticks_ms() - - -def process_broadcast_payload_split_or_flood(payload): - try: - text = payload.decode("utf-8") - obj = json.loads(text) - except Exception: - obj = None - if isinstance(obj, dict) and try_apply_bridge_config(obj): - return - if ( - isinstance(obj, dict) - and obj.get("m") == "split" - and isinstance(obj.get("peers"), list) - ): - send_split_from_obj(obj) - return - ensure_peer(BROADCAST) - esp.send(BROADCAST, payload) - last_used[BROADCAST] = time.ticks_ms() - - -def process_legacy_uart_frame(data): - if not data or len(data) < 6: - return - addr = data[:6] - payload = data[6:] - if addr == BROADCAST: - process_broadcast_payload_split_or_flood(payload) - return - ensure_peer(addr) - esp.send(addr, payload) - last_used[addr] = time.ticks_ms() - - -def handle_json_command_line(obj): - if not isinstance(obj, dict): - return - if try_apply_bridge_config(obj): - return - if obj.get("m") == "split" and isinstance(obj.get("peers"), list): - send_split_from_obj(obj) - return - to = obj.get("to") or obj.get("dest") - if isinstance(to, str) and len(to) == 12: - try: - mac = bytes.fromhex(to) - except ValueError: - return - if len(mac) != 6: - return - body = {k: v for k, v in obj.items() if k not in ("to", "dest")} - if not body: - return - try: - out = json.dumps(body).encode("utf-8") - except (TypeError, ValueError): - return - ensure_peer(mac) - esp.send(mac, out) - last_used[mac] = time.ticks_ms() - - -def drain_uart_json_lines(): - """Parse leading newline-delimited JSON objects from uart_rx_buf; leave rest.""" - global uart_rx_buf - while True: - s = uart_rx_buf.lstrip() - if not s: - uart_rx_buf = b"" - return - if s[0] != ord("{"): - uart_rx_buf = s - return - nl = s.find(b"\n") - if nl < 0: - uart_rx_buf = s - return - line = s[:nl].strip() - uart_rx_buf = s[nl + 1 :] - if line: - try: - text = line.decode("utf-8") - obj = json.loads(text) - handle_json_command_line(obj) - except Exception as e: - print("UART JSON line error:", e) - # continue; there may be another JSON line in buffer - - -def drain_uart_legacy_frame(): - """If buffer does not start with '{', treat whole buffer as one 6-byte MAC + JSON frame.""" - global uart_rx_buf - s = uart_rx_buf - if not s or s[0] == ord("{"): - return - if len(s) < 6: - return - data = s - uart_rx_buf = b"" - process_legacy_uart_frame(data) - - -def forward_espnow_to_uart(mac, msg): - peer_hex = ubinascii.hexlify(mac).decode() - try: - text = msg.decode("utf-8") - try: - payload = json.loads(text) - line_obj = {"dir": "espnow_rx", "from": peer_hex, "payload": payload} - except ValueError: - line_obj = {"dir": "espnow_rx", "from": peer_hex, "payload_text": text} - except UnicodeDecodeError: - line_obj = { - "dir": "espnow_rx", - "from": peer_hex, - "payload_b64": ubinascii.b64encode(msg).decode(), - } - try: - line = json.dumps(line_obj) + "\n" - uart.write(line.encode("utf-8")) - except Exception as e: - print("UART TX error:", e) - - -print("Starting ESP32 bridge (UART JSON + legacy MAC+JSON, ESP-NOW RX → UART JSON lines)") - -while True: - idle = True - if uart.any(): - idle = False - uart_rx_buf += uart.read() - drain_uart_json_lines() - drain_uart_legacy_frame() - - try: - peer, msg = esp.recv(0) - except OSError: - peer, msg = None, None - - if peer is not None and msg is not None: - idle = False - if len(peer) == 6: - forward_espnow_to_uart(peer, msg) - - if idle: - time.sleep_ms(1) diff --git a/esp32/msg.json b/esp32/msg.json deleted file mode 100644 index f2322b6..0000000 --- a/esp32/msg.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "ch": 6, - - "peers": { - "12:3456789012":{ - "select": [["name1", "preset1"]] - - , - "ff:ff:ff:ff:ff:ff": { - "presets": { - "preset1": { - "pattern": "on", - "colors": ["#FF0000", "#00FF00", "#0000FF"], - "delay": 100, - "brightness": 127, - "auto": true - } - } - } - } -} diff --git a/scripts/cp-esp32-main.sh b/scripts/cp-esp32-main.sh deleted file mode 100644 index 3683e02..0000000 --- a/scripts/cp-esp32-main.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -# Copy esp32/main.py to the connected ESP32 as /main.py (single line, no wrap). -cd "$(dirname "$0")/.." -pipenv run mpremote fs cp esp32/main.py :/main.py