Files

236 lines
9.8 KiB
Python

#!/usr/bin/env python3
"""
Olimex ESP32-C5-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.240
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. Both relays (POST /relay/on, GET /relay/status, POST /relay/off)
3. Both 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)
return r.status_code, r.text, None
except requests.RequestException as e:
return None, None, str(e)
def _post(url: str, json_data=None):
try:
r = requests.post(url, json=json_data, timeout=TIMEOUT)
return r.status_code, r.text, None
except requests.RequestException as e:
return None, None, str(e)
# ─────────────────────────────────────────────────────────────────────────────
# Main verification
# ─────────────────────────────────────────────────────────────────────────────
def verify_board(ip: str) -> int:
"""Test all board subsystems. Returns count of failures."""
base = f"http://{ip}"
failures = 0
print(f"\n{'='*70}")
print(f"Olimex ESP32-C5-EVB Verification")
print(f"{'='*70}")
print(f"Target IP: {ip}\n")
# ─ 1. Board Reachability ──────────────────────────────────────────────────
print("1. Board Status")
status_code, resp_text, err = _get(f"{base}/api/status")
if err:
record("Board reachable", False, err)
return 1 # Can't proceed without board connectivity
if not (status_code and status_code == 200):
record("Board reachable", False, f"HTTP {status_code}")
return 1
try:
status_json = json.loads(resp_text)
record("Board reachable", True, f"HTTP 200")
except json.JSONDecodeError:
record("Board reachable (partial)", False, "Response not JSON")
status_json = {}
# ─ 2. Relays ──────────────────────────────────────────────────────────────
print("\n2. Relays (2x 250V/10A)")
for relay_num in [1, 2]:
relay_name = f"Relay {relay_num}"
# Turn ON
_, _, err = _post(f"{base}/relay/on?relay={relay_num}")
if err:
record(f"{relay_name} ON", False, err)
failures += 1
continue
time.sleep(RELAY_DLY)
status_code, resp_text, err = _get(f"{base}/relay/status?relay={relay_num}")
if err or status_code != 200:
record(f"{relay_name} ON verify", False, err or f"HTTP {status_code}")
failures += 1
else:
try:
r = json.loads(resp_text)
if r.get("state") == True:
record(f"{relay_name} ON", True)
else:
record(f"{relay_name} ON verify", False, "State mismatch")
failures += 1
except:
record(f"{relay_name} ON verify", False, "Invalid response")
failures += 1
time.sleep(RELAY_DLY)
# Turn OFF
_, _, err = _post(f"{base}/relay/off?relay={relay_num}")
if err:
record(f"{relay_name} OFF", False, err)
failures += 1
continue
time.sleep(RELAY_DLY)
status_code, resp_text, err = _get(f"{base}/relay/status?relay={relay_num}")
if err or status_code != 200:
record(f"{relay_name} OFF verify", False, err or f"HTTP {status_code}")
failures += 1
else:
try:
r = json.loads(resp_text)
if r.get("state") == False:
record(f"{relay_name} OFF", True)
else:
record(f"{relay_name} OFF verify", False, "State mismatch")
failures += 1
except:
record(f"{relay_name} OFF verify", False, "Invalid response")
failures += 1
# ─ 3. Digital Inputs ──────────────────────────────────────────────────────
print("\n3. Opto-Isolated Inputs (110VAC-240VAC)")
for input_num in [1, 2]:
status_code, resp_text, err = _get(f"{base}/input/status?input={input_num}")
if err or status_code != 200:
record(f"Input {input_num}", False, err or f"HTTP {status_code}")
failures += 1
else:
try:
r = json.loads(resp_text)
state = "HIGH" if r.get("state") else "LOW"
record(f"Input {input_num}", True, f"State: {state}")
except:
record(f"Input {input_num}", False, "Invalid response")
failures += 1
# ─ 4. LED ─────────────────────────────────────────────────────────────────
print("\n4. LED Control")
_, _, err = _post(f"{base}/led/on")
if err:
record("LED ON", False, err)
failures += 1
else:
record("LED ON", True)
time.sleep(0.2)
_, _, err = _post(f"{base}/led/off")
if err:
record("LED OFF", False, err)
failures += 1
else:
record("LED OFF", True)
# ─ 5. NFC Status ──────────────────────────────────────────────────────────
print("\n5. NFC Module (PN532 via UEXT, optional)")
status_code, resp_text, err = _get(f"{base}/nfc/status")
if err or status_code != 200:
record("NFC Status API", False, err or f"HTTP {status_code}")
else:
try:
nfc_status = json.loads(resp_text)
init = nfc_status.get("initialized", False)
record("NFC Status API", True, f"Init: {init}")
except:
record("NFC Status API", False, "Invalid response")
# ─ 6. NFC Config ──────────────────────────────────────────────────────────
status_code, resp_text, err = _get(f"{base}/nfc/config")
if err or status_code != 200:
record("NFC Config API", False, err or f"HTTP {status_code}")
else:
try:
config = json.loads(resp_text)
record("NFC Config API", True, f"Relay: {config.get('relay_num')}, Pulse: {config.get('pulse_ms')}ms")
except:
record("NFC Config API", False, "Invalid response")
# ─────────────────────────────────────────────────────────────────────────
# Summary
# ─────────────────────────────────────────────────────────────────────────
print(f"\n{'='*70}")
passed = len([r for r in results if r["pass"]])
total = len(results)
print(f"Result: {passed}/{total} tests passed")
if failures == 0:
print("✓ All systems functional!")
else:
print(f"{failures} failures detected")
print(f"{'='*70}\n")
return failures
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("ip", nargs="?", default="192.168.0.240", help="Board IP (default: 192.168.0.240)")
parser.add_argument("--json", action="store_true", help="Output results as JSON")
args = parser.parse_args()
failures = verify_board(args.ip)
if args.json:
print(json.dumps(results, indent=2))
sys.exit(min(failures, 1))