updated board
This commit is contained in:
252
board_verify.py
Normal file
252
board_verify.py
Normal file
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Olimex ESP32-C6-EVB — Remote Board Verification Script
|
||||
=========================================================
|
||||
Queries the board's REST API and verifies every subsystem.
|
||||
|
||||
Usage:
|
||||
python3 board_verify.py # uses default IP 192.168.0.181
|
||||
python3 board_verify.py 192.168.0.200 # custom IP
|
||||
python3 board_verify.py --json # machine-readable output
|
||||
|
||||
Requirements: pip install requests (already in location_managemet requirements)
|
||||
|
||||
What it tests:
|
||||
1. Board reachability (GET /api/status)
|
||||
2. All 4 relays (POST /relay/on, GET /relay/status, POST /relay/off)
|
||||
3. All 4 digital inputs (GET /input/status)
|
||||
4. LED (POST /led/on + /led/off)
|
||||
5. NFC reader (GET /nfc/status)
|
||||
6. NFC config API (GET /nfc/config)
|
||||
|
||||
NOTE: Relay tests cycle each relay ON→verify→OFF→verify.
|
||||
You should hear/see the relay click during the test.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import argparse
|
||||
import requests
|
||||
|
||||
TIMEOUT = 5 # seconds per HTTP request
|
||||
RELAY_DLY = 0.4 # seconds to wait between relay on/status/off
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Result tracking
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
results = []
|
||||
|
||||
def record(name: str, ok: bool, detail: str = "") -> bool:
|
||||
results.append({"name": name, "pass": ok, "detail": detail})
|
||||
icon = "\033[32m[PASS]\033[0m" if ok else "\033[31m[FAIL]\033[0m"
|
||||
print(f" {icon} {name}" + (f" — {detail}" if detail else ""))
|
||||
return ok
|
||||
|
||||
|
||||
def _get(url: str):
|
||||
try:
|
||||
r = requests.get(url, timeout=TIMEOUT)
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
except requests.exceptions.ConnectionError:
|
||||
return None
|
||||
except Exception as e:
|
||||
return {"_error": str(e)}
|
||||
|
||||
|
||||
def _post(url: str):
|
||||
try:
|
||||
r = requests.post(url, timeout=TIMEOUT)
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
except requests.exceptions.ConnectionError:
|
||||
return None
|
||||
except Exception as e:
|
||||
return {"_error": str(e)}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Tests
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_reachability(base: str) -> bool:
|
||||
print("\n── Connectivity ──────────────────────────────")
|
||||
data = _get(f"{base}/api/status")
|
||||
if data is None:
|
||||
record("Board reachable", False, f"no response from {base}")
|
||||
return False
|
||||
if "_error" in data:
|
||||
record("Board reachable", False, data["_error"])
|
||||
return False
|
||||
|
||||
record("Board reachable", True,
|
||||
f"IP {base.split('//')[1]} "
|
||||
f"nfc_init={data.get('nfc_initialized','?')} "
|
||||
f"nfc_uid={data.get('nfc_last_uid') or '(none)'}")
|
||||
return True
|
||||
|
||||
|
||||
def test_relays(base: str):
|
||||
print("\n── Relay Tests ───────────────────────────────")
|
||||
for relay in range(1, 5):
|
||||
# Turn ON
|
||||
r_on = _post(f"{base}/relay/on?relay={relay}")
|
||||
if r_on is None:
|
||||
record(f"Relay {relay} ON", False, "no response"); continue
|
||||
time.sleep(RELAY_DLY)
|
||||
|
||||
# Verify state = true
|
||||
r_st = _get(f"{base}/relay/status?relay={relay}")
|
||||
on_ok = r_st is not None and r_st.get("state") is True
|
||||
record(f"Relay {relay} ON", on_ok,
|
||||
("state=true" if on_ok else f"got {r_st}"))
|
||||
|
||||
time.sleep(RELAY_DLY)
|
||||
|
||||
# Turn OFF
|
||||
_post(f"{base}/relay/off?relay={relay}")
|
||||
time.sleep(RELAY_DLY)
|
||||
|
||||
# Verify state = false
|
||||
r_st2 = _get(f"{base}/relay/status?relay={relay}")
|
||||
off_ok = r_st2 is not None and r_st2.get("state") is False
|
||||
record(f"Relay {relay} OFF", off_ok,
|
||||
("state=false" if off_ok else f"got {r_st2}"))
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
def test_inputs(base: str):
|
||||
print("\n── Digital Input Tests ───────────────────────")
|
||||
for inp in range(1, 5):
|
||||
data = _get(f"{base}/input/status?input={inp}")
|
||||
if data is None:
|
||||
record(f"Input {inp} readable", False, "no response"); continue
|
||||
if "_error" in data:
|
||||
record(f"Input {inp} readable", False, data["_error"]); continue
|
||||
state = data.get("state")
|
||||
record(f"Input {inp} readable", state is not None,
|
||||
f"state={'HIGH' if state else 'LOW'}" if state is not None else f"got {data}")
|
||||
|
||||
|
||||
def test_led(base: str):
|
||||
print("\n── LED Test ──────────────────────────────────")
|
||||
on_r = _post(f"{base}/led/on")
|
||||
time.sleep(0.3)
|
||||
off_r = _post(f"{base}/led/off")
|
||||
led_ok = (on_r is not None and "status" in on_r
|
||||
and off_r is not None and "status" in off_r)
|
||||
record("LED on/off", led_ok,
|
||||
"API responded OK — verify LED blinked" if led_ok else f"on={on_r} off={off_r}")
|
||||
|
||||
|
||||
def test_nfc(base: str):
|
||||
print("\n── NFC Test ──────────────────────────────────")
|
||||
data = _get(f"{base}/nfc/status")
|
||||
if data is None:
|
||||
record("NFC endpoint", False, f"GET /nfc/status no response"); return
|
||||
if "_error" in data:
|
||||
record("NFC endpoint", False, data["_error"]); return
|
||||
|
||||
record("NFC endpoint reachable", True, "")
|
||||
|
||||
init = data.get("initialized", False)
|
||||
record("NFC PN532 initialized", init,
|
||||
f"last_uid={data.get('last_uid') or '(none)'} "
|
||||
f"access_state={data.get('access_state','?')}" if init
|
||||
else "PN532 not detected — check hardware")
|
||||
|
||||
# Config endpoint
|
||||
cfg = _get(f"{base}/nfc/config")
|
||||
if cfg and "_error" not in cfg:
|
||||
record("NFC config endpoint", True,
|
||||
f"auth_uid='{cfg.get('auth_uid') or 'any'}' "
|
||||
f"relay={cfg.get('relay_num')} "
|
||||
f"pulse={cfg.get('pulse_ms')} ms")
|
||||
else:
|
||||
record("NFC config endpoint", False, str(cfg))
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Optional: read board_test sketch results directly
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_sketch_results(base: str):
|
||||
"""If the board_test sketch is running it exposes /test.json — use it."""
|
||||
data = _get(f"{base}/test.json")
|
||||
if data is None or "_error" in data:
|
||||
return # main firmware running — no /test.json endpoint
|
||||
print("\n── Board-Test Sketch Results (from /test.json) ──")
|
||||
for t in data.get("tests", []):
|
||||
record(f"[sketch] {t['name']}", t["pass"], t.get("detail", ""))
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Entry point
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Olimex ESP32-C6-EVB board verifier")
|
||||
parser.add_argument("ip", nargs="?", default="192.168.0.181",
|
||||
help="Board IP address (default: 192.168.0.181)")
|
||||
parser.add_argument("--json", action="store_true",
|
||||
help="Output results as JSON")
|
||||
parser.add_argument("--skip-relays", action="store_true",
|
||||
help="Skip relay tests (no load wired)")
|
||||
args = parser.parse_args()
|
||||
|
||||
base = f"http://{args.ip}"
|
||||
|
||||
print(f"\n╔══════════════════════════════════════════╗")
|
||||
print(f"║ Olimex ESP32-C6-EVB Remote Verifier ║")
|
||||
print(f"╚══════════════════════════════════════════╝")
|
||||
print(f" Target: {base}\n")
|
||||
|
||||
# Try board_test sketch first (if deployed)
|
||||
test_sketch_results(base)
|
||||
|
||||
# Connectivity gate — abort if board unreachable
|
||||
if not test_reachability(base):
|
||||
print("\n\033[31m Board unreachable — aborting remaining tests.\033[0m")
|
||||
print(f" Try: ping {args.ip} or wget -qO- {base}/api/status\n")
|
||||
sys.exit(1)
|
||||
|
||||
if not args.skip_relays:
|
||||
test_relays(base)
|
||||
else:
|
||||
print("\n── Relay Tests SKIPPED (--skip-relays) ──────")
|
||||
|
||||
test_inputs(base)
|
||||
test_led(base)
|
||||
test_nfc(base)
|
||||
|
||||
# ── Summary ───────────────────────────────────────────────────────────────
|
||||
passed = sum(1 for r in results if r["pass"])
|
||||
failed = sum(1 for r in results if not r["pass"])
|
||||
total = len(results)
|
||||
all_ok = failed == 0
|
||||
|
||||
print(f"\n╔══════════════════════════════════════════╗")
|
||||
print(f"║ PASSED: {passed:2d} FAILED: {failed:2d} TOTAL: {total:2d} ║")
|
||||
print("║ \033[32m✓ ALL TESTS PASSED — board is OK\033[0m ║" if all_ok else
|
||||
"║ \033[31m✗ FAILURES DETECTED — see above\033[0m ║")
|
||||
print(f"╚══════════════════════════════════════════╝\n")
|
||||
|
||||
if args.json:
|
||||
summary = {
|
||||
"board_ip": args.ip,
|
||||
"pass": passed,
|
||||
"fail": failed,
|
||||
"total": total,
|
||||
"board_ok": all_ok,
|
||||
"tests": results,
|
||||
}
|
||||
print(json.dumps(summary, indent=2))
|
||||
|
||||
sys.exit(0 if all_ok else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user