Save current changes before reorganizing drivers and cleaning workspace

This commit is contained in:
Adolfo Reyna
2026-06-17 22:17:59 -04:00
parent 3356e5d4a2
commit 2813c11104
21 changed files with 2904 additions and 132 deletions
+61 -5
View File
@@ -5,7 +5,7 @@ import time
class MCPServer:
"""A lightweight JSON-RPC HTTP server implementing Model Context Protocol (MCP) endpoints."""
def __init__(self, display, led, battery, sensor, rtc, ble, vstream=None):
def __init__(self, display, led, battery, sensor, rtc, ble, vstream=None, touch=None):
self.display = display
self.led = led
self.battery = battery
@@ -13,6 +13,7 @@ class MCPServer:
self.rtc = rtc
self.ble = ble
self.vstream = vstream
self.touch = touch
self.sock = None
self.active_led_mode = "off" # static, breath, rainbow, off
@@ -20,6 +21,7 @@ class MCPServer:
def start(self, port=80):
"""Starts the TCP server non-blockingly."""
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind(('', port))
@@ -39,6 +41,27 @@ class MCPServer:
print("Failed to bind UDP Discovery socket: {}".format(ue))
self.udp_sock = None
def restart(self):
"""Re-initializes the listening sockets after a fatal error."""
print("Restarting MCP Server sockets...")
try:
if self.sock:
self.sock.close()
except:
pass
self.sock = None
try:
if hasattr(self, 'udp_sock') and self.udp_sock:
self.udp_sock.close()
except:
pass
self.udp_sock = None
# Add a short delay to allow socket reuse to settle
time.sleep_ms(100)
port = getattr(self, 'port', 80)
self.start(port)
def update(self):
"""Check for incoming HTTP requests and handle them non-blockingly."""
# 1. Handle UDP Discovery queries
@@ -56,8 +79,18 @@ class MCPServer:
try:
client, addr = self.sock.accept()
except OSError:
# No incoming connection
except OSError as e:
import errno
err = getattr(e, 'errno', None)
if err is None and e.args:
err = e.args[0]
ewouldblock = getattr(errno, 'EWOULDBLOCK', errno.EAGAIN)
if err in (errno.EAGAIN, ewouldblock) or err is None:
# No incoming connection
return
# Fatal socket error
print(f"Fatal socket error in MCP Server accept(): {e}. Re-initializing...")
self.restart()
return
try:
@@ -100,14 +133,21 @@ class MCPServer:
rpc_req = json.loads(body)
rpc_resp = self._handle_rpc(rpc_req)
# Send HTTP Response
resp_body = json.dumps(rpc_resp)
resp = "HTTP/1.1 200 OK\r\n"
resp += "Content-Type: application/json\r\n"
resp += f"Content-Length: {len(resp_body)}\r\n"
resp += "Connection: close\r\n\r\n"
resp += resp_body
client.send(resp.encode('utf-8'))
data_to_send = resp.encode('utf-8')
total_sent = 0
while total_sent < len(data_to_send):
sent = client.write(data_to_send[total_sent:])
if sent is None or sent == 0:
time.sleep_ms(10)
continue
total_sent += sent
else:
# Return 404
resp = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
@@ -171,6 +211,11 @@ class MCPServer:
"description": "Read the current battery voltage and estimated capacity percentage.",
"inputSchema": {"type": "object", "properties": {}}
},
{
"name": "get_touch",
"description": "Read the current touch state and coordinates from the capacitive touchscreen.",
"inputSchema": {"type": "object", "properties": {}}
},
{
"name": "get_sensors",
"description": "Read onboard SHTC3 temperature and relative humidity.",
@@ -404,6 +449,17 @@ class MCPServer:
t, h = self.sensor.read_sensor()
return json.dumps({"temperature_c": t, "humidity_pct": h})
elif name == "get_touch":
if self.touch is None:
return json.dumps({"error": "Touchscreen not configured on this device."})
is_t = self.touch.is_touched()
x, y = None, None
if is_t:
pt = self.touch.read_touch()
if pt:
x, y = pt
return json.dumps({"is_touched": is_t, "x": x, "y": y})
elif name == "scan_ble":
dur = int(args.get("duration_ms", 3000))
# Scan returns dictionary: {mac: {rssi: rssi, name: name}}