Add ESP32-C3 SPI slave with ESP-NOW, Raspberry Pi test tools, and updated project structure
- ESP32-C3 SPI slave project with ESP-NOW broadcast functionality - Raspberry Pi SPI master test tools and CLI for JSON communication - Merged src/ directory from full branch with lighting controller code - Updated Pipfile with system install scripts and ESP32 monitoring - Added comprehensive test suite for SPI communication
This commit is contained in:
114
test/send_json.py
Executable file
114
test/send_json.py
Executable file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
CLI to send JSON over SPI to the ESP32-C3 SPI slave.
|
||||
|
||||
Examples:
|
||||
./send_json.py --data '{"d":{"t":"b","br":128},"bar":{"pt":"off"}}'
|
||||
./send_json.py --file payload.json
|
||||
./send_json.py --beat --brightness 180 --delay 30 --pattern wave
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Ensure we can import the local test helper
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
if SCRIPT_DIR not in sys.path:
|
||||
sys.path.insert(0, SCRIPT_DIR)
|
||||
|
||||
from spi_master_test import SPIMasterTest # noqa: E402
|
||||
|
||||
|
||||
def build_payload_from_args(args: argparse.Namespace) -> dict:
|
||||
"""Build a control payload matching the receiver format from args."""
|
||||
if args.data:
|
||||
try:
|
||||
return json.loads(args.data)
|
||||
except Exception as exc:
|
||||
print(f"Invalid JSON in --data: {exc}")
|
||||
sys.exit(2)
|
||||
|
||||
if args.file:
|
||||
try:
|
||||
with open(args.file, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
except Exception as exc:
|
||||
print(f"Failed to read JSON file {args.file}: {exc}")
|
||||
sys.exit(2)
|
||||
|
||||
# Build from individual params (beat or update)
|
||||
msg_type = "b" if args.beat else "u"
|
||||
defaults = {"t": msg_type}
|
||||
if args.brightness is not None:
|
||||
defaults["br"] = int(args.brightness)
|
||||
if args.delay is not None:
|
||||
defaults["dl"] = int(args.delay)
|
||||
if args.n1 is not None:
|
||||
defaults["n1"] = int(args.n1)
|
||||
if args.n2 is not None:
|
||||
defaults["n2"] = int(args.n2)
|
||||
if args.n3 is not None:
|
||||
defaults["n3"] = int(args.n3)
|
||||
if args.step is not None:
|
||||
defaults["s"] = int(args.step)
|
||||
|
||||
bar = {}
|
||||
if args.pattern:
|
||||
bar["pt"] = args.pattern
|
||||
if args.name_value:
|
||||
# Allow sending a specific bar (device) override by name
|
||||
# Usage: --name-value mybar '{"br":200,"pt":"off"}'
|
||||
try:
|
||||
name, value = args.name_value
|
||||
bar = json.loads(value)
|
||||
return {"d": defaults, name: bar}
|
||||
except Exception as exc:
|
||||
print(f"Invalid --name-value: {exc}")
|
||||
sys.exit(2)
|
||||
|
||||
# Default field name could be populated on receiver side via settings.get("name")
|
||||
# We only send defaults and a generic "bar" override here for convenience.
|
||||
return {"d": defaults, "bar": bar}
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(description="Send JSON over SPI to ESP32-C3")
|
||||
src = p.add_mutually_exclusive_group()
|
||||
src.add_argument("--data", help="Raw JSON string to send")
|
||||
src.add_argument("--file", help="Path to JSON file to send")
|
||||
|
||||
p.add_argument("--beat", action="store_true", help="Send as beat message (t=b)")
|
||||
p.add_argument("--brightness", type=int, help="Brightness (br)")
|
||||
p.add_argument("--delay", type=int, help="Delay (dl)")
|
||||
p.add_argument("--pattern", help="Pattern name (pt)")
|
||||
p.add_argument("--n1", type=int, help="n1 parameter")
|
||||
p.add_argument("--n2", type=int, help="n2 parameter")
|
||||
p.add_argument("--n3", type=int, help="n3 parameter")
|
||||
p.add_argument("--step", type=int, help="step (s)")
|
||||
p.add_argument("--name-value", nargs=2, metavar=("NAME", "JSON"), help="Send override under NAME key with JSON object value")
|
||||
|
||||
# SPI params
|
||||
p.add_argument("--bus", type=int, default=0, help="SPI bus (default 0)")
|
||||
p.add_argument("--device", type=int, default=0, help="SPI device/CE (default 0)")
|
||||
p.add_argument("--speed", type=int, default=1_000_000, help="SPI speed Hz (default 1MHz)")
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
payload = build_payload_from_args(args)
|
||||
|
||||
spi = SPIMasterTest(bus=args.bus, device=args.device, max_speed_hz=args.speed)
|
||||
try:
|
||||
spi.send_json(payload)
|
||||
finally:
|
||||
spi.cleanup()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
|
Reference in New Issue
Block a user