Migrate MCP and utilities to CircuitPython, including color GIF and volume fixes
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import urllib.request
|
||||
import json
|
||||
|
||||
def call_mcp(method, params={}):
|
||||
url = "http://192.168.68.118/api/mcp"
|
||||
payload = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": method,
|
||||
"params": params
|
||||
}
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=json.dumps(payload).encode("utf-8"),
|
||||
headers={"Content-Type": "application/json"},
|
||||
method="POST"
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=5.0) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
res = call_mcp("tools/call", {"name": "get_stream_stats", "arguments": {}})
|
||||
print(json.dumps(res, indent=2))
|
||||
@@ -0,0 +1,30 @@
|
||||
import serial
|
||||
import time
|
||||
import sys
|
||||
|
||||
def main():
|
||||
port = "/dev/cu.usbmodem411CF91D65091"
|
||||
print(f"Connecting to {port} at 115200...")
|
||||
try:
|
||||
ser = serial.Serial(port, 115200, timeout=1.0)
|
||||
except Exception as e:
|
||||
print(f"Error opening serial port: {e}")
|
||||
return
|
||||
|
||||
print("Reading serial output for 3 seconds...")
|
||||
start_time = time.time()
|
||||
try:
|
||||
while time.time() - start_time < 3.0:
|
||||
if ser.in_waiting > 0:
|
||||
data = ser.read(ser.in_waiting)
|
||||
sys.stdout.write(data.decode("utf-8", "ignore"))
|
||||
sys.stdout.flush()
|
||||
time.sleep(0.1)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
ser.close()
|
||||
print("\nSerial connection closed.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,39 @@
|
||||
import serial
|
||||
import time
|
||||
import sys
|
||||
|
||||
def main():
|
||||
port = "/dev/cu.usbmodem411CF91D65091"
|
||||
print(f"Connecting to {port} at 115200...")
|
||||
try:
|
||||
ser = serial.Serial(port, 115200, timeout=1.0)
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return
|
||||
|
||||
print("Sending Ctrl-C to interrupt...")
|
||||
ser.write(b'\x03')
|
||||
time.sleep(0.5)
|
||||
print("--- Current buffer contents ---")
|
||||
print(ser.read_all().decode("utf-8", "ignore"))
|
||||
|
||||
print("Sending Ctrl-D to trigger soft reboot...")
|
||||
ser.write(b'\x04')
|
||||
|
||||
print("--- Capturing boot output (10 seconds) ---")
|
||||
start_time = time.time()
|
||||
try:
|
||||
while time.time() - start_time < 10.0:
|
||||
if ser.in_waiting > 0:
|
||||
data = ser.read(ser.in_waiting)
|
||||
sys.stdout.write(data.decode("utf-8", "ignore"))
|
||||
sys.stdout.flush()
|
||||
time.sleep(0.05)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
ser.close()
|
||||
print("\nConnection closed.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,68 @@
|
||||
import serial
|
||||
import time
|
||||
import sys
|
||||
|
||||
def run_diagnostic(port):
|
||||
print(f"Opening serial port {port}...")
|
||||
try:
|
||||
ser = serial.Serial(port, 115200, timeout=1)
|
||||
except Exception as e:
|
||||
print("Failed to open serial port:", e)
|
||||
return False
|
||||
|
||||
print("Interrupting board execution (Ctrl+C)...")
|
||||
for _ in range(30):
|
||||
ser.write(b'\x03')
|
||||
time.sleep(0.02)
|
||||
|
||||
time.sleep(0.5)
|
||||
ser.read_all()
|
||||
|
||||
print("Entering raw REPL...")
|
||||
ser.write(b'\x01')
|
||||
time.sleep(0.2)
|
||||
resp = ser.read_all()
|
||||
if b'raw REPL' not in resp and b'OK' not in resp and b'raw' not in resp:
|
||||
print("Could not enter raw REPL:", resp)
|
||||
ser.close()
|
||||
return False
|
||||
|
||||
# Python code to test playback
|
||||
test_code = """
|
||||
import audio_util
|
||||
try:
|
||||
print("Starting play_tone test...")
|
||||
audio_util.play_tone(440, 1000, 50)
|
||||
print("play_tone test finished!")
|
||||
except Exception as e:
|
||||
import sys
|
||||
sys.print_exception(e)
|
||||
"""
|
||||
|
||||
print("Running diagnostic code on the board...")
|
||||
# Send code
|
||||
ser.write(test_code.encode('utf-8'))
|
||||
ser.write(b'\x04') # Ctrl+D to execute
|
||||
|
||||
time.sleep(0.2)
|
||||
|
||||
# Read output
|
||||
print("\n--- Board Output ---")
|
||||
start = time.time()
|
||||
while time.time() - start < 5.0:
|
||||
line = ser.readline()
|
||||
if line:
|
||||
print(line.decode('utf-8', 'ignore').strip())
|
||||
|
||||
# Reset the board to return to normal
|
||||
print("Resetting board to normal...")
|
||||
ser.write(b'\x02') # Ctrl+B to exit raw REPL
|
||||
time.sleep(0.1)
|
||||
ser.write(b'\x03')
|
||||
time.sleep(0.1)
|
||||
ser.write(b"import machine\nmachine.reset()\n")
|
||||
ser.close()
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_diagnostic('/dev/cu.usbmodem101')
|
||||
@@ -0,0 +1,64 @@
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import time
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
IP = "192.168.68.128"
|
||||
|
||||
def send_raw_frame(payload, x=0, y=0, w=320, h=240, fmt=None):
|
||||
params = {"x": x, "y": y, "w": w, "h": h}
|
||||
if fmt:
|
||||
params["format"] = fmt
|
||||
q = urllib.parse.urlencode(params)
|
||||
url = f"http://{IP}/api/screen/raw?{q}"
|
||||
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=payload,
|
||||
headers={"Content-Type": "application/octet-stream"},
|
||||
method="POST"
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=5.0) as resp:
|
||||
return resp.read().decode("utf-8")
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
# 1. Test Color Frame (RGB565)
|
||||
print("Generating raw color frame...")
|
||||
img_color = Image.new("RGB", (320, 240))
|
||||
draw = ImageDraw.Draw(img_color)
|
||||
for x in range(320):
|
||||
for y in range(240):
|
||||
r = (x * 255 // 319)
|
||||
g = (y * 255 // 239)
|
||||
b = 128
|
||||
img_color.putpixel((x, y), (r, g, b))
|
||||
draw.text((20, 20), "RAW COLOR STREAM (RGB565)", fill=(255, 255, 255))
|
||||
|
||||
# Convert to RGB565 BE bytes
|
||||
color_payload = bytearray(320 * 240 * 2)
|
||||
for idx, (r, g, b) in enumerate(img_color.getdata()):
|
||||
val = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)
|
||||
color_payload[idx * 2] = (val >> 8) & 0xFF
|
||||
color_payload[idx * 2 + 1] = val & 0xFF
|
||||
|
||||
print("Streaming raw color frame...")
|
||||
res = send_raw_frame(bytes(color_payload), fmt="rgb565")
|
||||
print("Response:", res)
|
||||
time.sleep(3.0)
|
||||
|
||||
# 2. Test Monochrome Frame (1-bit)
|
||||
print("\nGenerating raw monochrome frame...")
|
||||
img_mono = Image.new("1", (320, 240), 0)
|
||||
draw_mono = ImageDraw.Draw(img_mono)
|
||||
draw_mono.rectangle([10, 10, 310, 230], outline=1, width=3)
|
||||
draw_mono.text((30, 100), "RAW MONOCHROME STREAM (1-BIT)", fill=1)
|
||||
|
||||
# Convert PIL 1-bit to MHMSB bytearray
|
||||
mono_payload = img_mono.tobytes()
|
||||
|
||||
print(f"Monochrome payload size: {len(mono_payload)} bytes")
|
||||
print("Streaming raw monochrome frame...")
|
||||
res_mono = send_raw_frame(mono_payload, fmt="mono")
|
||||
print("Response:", res_mono)
|
||||
@@ -0,0 +1,48 @@
|
||||
import urllib.request
|
||||
import json
|
||||
import time
|
||||
|
||||
def call_mcp(method, params={}):
|
||||
url = "http://192.168.68.128/api/mcp"
|
||||
payload = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": method,
|
||||
"params": params
|
||||
}
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=json.dumps(payload).encode("utf-8"),
|
||||
headers={"Content-Type": "application/json"},
|
||||
method="POST"
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=5.0) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
# 1. Fetch start stats
|
||||
print("Fetching start stats...")
|
||||
res_start = call_mcp("tools/call", {"name": "get_stream_stats", "arguments": {}})
|
||||
print(json.dumps(res_start, indent=2))
|
||||
|
||||
# 2. Wait 3 seconds
|
||||
print("Waiting 3 seconds...")
|
||||
time.sleep(3.0)
|
||||
|
||||
# 3. Fetch end stats
|
||||
print("Fetching end stats...")
|
||||
res_end = call_mcp("tools/call", {"name": "get_stream_stats", "arguments": {}})
|
||||
print(json.dumps(res_end, indent=2))
|
||||
|
||||
# Calculate FPS
|
||||
try:
|
||||
start_stats = json.loads(res_start["result"]["content"][0]["text"])
|
||||
end_stats = json.loads(res_end["result"]["content"][0]["text"])
|
||||
frames_diff = end_stats["frames_drawn"] - start_stats["frames_drawn"]
|
||||
packets_diff = end_stats["udp_packets_received"] - start_stats["udp_packets_received"]
|
||||
print(f"\nResult: Drew {frames_diff} frames in 3.0 seconds ({frames_diff / 3.0:.1f} FPS)")
|
||||
print(f"Packets received: {packets_diff} in 3.0 seconds ({packets_diff / 3.0:.1f} pps)")
|
||||
except Exception as e:
|
||||
print("Failed to calculate FPS:", e)
|
||||
@@ -0,0 +1,88 @@
|
||||
import serial
|
||||
import time
|
||||
import sys
|
||||
|
||||
def upload_via_serial(port, local_path, remote_path):
|
||||
print(f"Opening serial port {port}...")
|
||||
try:
|
||||
ser = serial.Serial(port, 115200, timeout=0.5)
|
||||
except Exception as e:
|
||||
print("Failed to open serial port:", e)
|
||||
return False
|
||||
|
||||
print("Interrupting board (Ctrl+C)...")
|
||||
for _ in range(50):
|
||||
ser.write(b'\x03')
|
||||
time.sleep(0.02)
|
||||
|
||||
time.sleep(0.5)
|
||||
ser.read_all()
|
||||
|
||||
print("Entering raw REPL...")
|
||||
ser.write(b'\x01')
|
||||
time.sleep(0.2)
|
||||
resp = ser.read_all()
|
||||
if b'raw REPL' not in resp and b'OK' not in resp and b'raw' not in resp:
|
||||
print("Could not enter raw REPL:", resp)
|
||||
ser.close()
|
||||
return False
|
||||
|
||||
with open(local_path, 'rb') as f:
|
||||
content = f.read()
|
||||
|
||||
print(f"Uploading {local_path} to remote {remote_path} ({len(content)} bytes)...")
|
||||
|
||||
# Open file
|
||||
cmd = f"f = open('{remote_path}', 'wb')\n".encode('utf-8')
|
||||
ser.write(cmd)
|
||||
ser.write(b'\x04')
|
||||
time.sleep(0.1)
|
||||
ser.read_all()
|
||||
|
||||
# Write chunks
|
||||
chunk_size = 256
|
||||
for i in range(0, len(content), chunk_size):
|
||||
chunk = content[i:i+chunk_size]
|
||||
cmd = b"f.write(" + repr(chunk).encode('utf-8') + b")\n"
|
||||
ser.write(cmd)
|
||||
ser.write(b'\x04')
|
||||
time.sleep(0.05)
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
|
||||
cmd = b"f.close()\n"
|
||||
ser.write(cmd)
|
||||
ser.write(b'\x04')
|
||||
time.sleep(0.1)
|
||||
ser.read_all()
|
||||
|
||||
print(f"\nUploaded {remote_path} successfully!")
|
||||
ser.write(b'\x02')
|
||||
ser.close()
|
||||
return True
|
||||
|
||||
def main():
|
||||
port = '/dev/cu.usbmodem101'
|
||||
files_to_upload = [
|
||||
("main.py", "main.py"),
|
||||
("demo_websocket_voice.py", "demo_websocket_voice.py")
|
||||
]
|
||||
|
||||
for local, remote in files_to_upload:
|
||||
if not upload_via_serial(port, local, remote):
|
||||
print(f"Failed to upload {local}. Exiting.")
|
||||
sys.exit(1)
|
||||
|
||||
print("Rebooting board...")
|
||||
try:
|
||||
ser = serial.Serial(port, 115200, timeout=0.5)
|
||||
ser.write(b'\x03')
|
||||
time.sleep(0.1)
|
||||
ser.write(b'import machine\nmachine.reset()\n')
|
||||
ser.close()
|
||||
print("Reset triggered successfully!")
|
||||
except Exception as e:
|
||||
print("Could not trigger reset:", e)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user