# macOS: server.py import socket import serial import threading import time import logging import sys from serial.serialutil import SerialException # === Configuration === SERIAL_PORT = "/dev/cu.usbmodem101" # Update for your system BAUD_RATE = 115200 TCP_HOST = "0.0.0.0" TCP_PORT = 33329 RECONNECT_DELAY = 2 # seconds logging.basicConfig( level=logging.INFO, format="[%(asctime)s] [%(levelname)s] %(message)s", datefmt="%H:%M:%S" ) def handle_connection(client_socket, serial_port): def tcp_to_serial(): try: while True: data = client_socket.recv(1024) if not data: break # Control command: 1 byte prefix 0xFF + 1 byte state if data.startswith(b"\xFF") and len(data) >= 2: control = data[1] serial_port.rts = bool(control & 0b10) serial_port.dtr = bool(control & 0b01) logging.info(f"[CTRL] Set RTS={serial_port.rts}, DTR={serial_port.dtr}") continue serial_port.write(data) except Exception as e: logging.warning(f"TCP→Serial thread ended: {e}") finally: client_socket.close() def serial_to_tcp(): try: while True: if serial_port.in_waiting: data = serial_port.read(serial_port.in_waiting) client_socket.sendall(data) else: time.sleep(0.01) except Exception as e: logging.warning(f"Serial→TCP thread ended: {e}") finally: client_socket.close() logging.info("TCP client connected, starting bridge threads") tx_thread = threading.Thread(target=tcp_to_serial, daemon=True) rx_thread = threading.Thread(target=serial_to_tcp, daemon=True) tx_thread.start() rx_thread.start() tx_thread.join() rx_thread.join() logging.info("TCP client disconnected, closing bridge") def run_bridge(): while True: try: logging.info(f"Waiting for serial device: {SERIAL_PORT}") while True: try: serial_port = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=0) break except SerialException: logging.warning(f"Serial port not available: {SERIAL_PORT}") time.sleep(RECONNECT_DELAY) logging.info(f"Serial port opened: {SERIAL_PORT} @ {BAUD_RATE} baud") server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((TCP_HOST, TCP_PORT)) server_socket.listen(1) logging.info(f"Listening on TCP {TCP_HOST}:{TCP_PORT}") while True: logging.info("Waiting for TCP client...") try: client_socket, addr = server_socket.accept() logging.info(f"TCP connection from {addr}") handle_connection(client_socket, serial_port) except Exception as e: logging.warning(f"TCP accept failed: {e}") time.sleep(1) except Exception as e: logging.error(f"Fatal error: {e}") finally: try: serial_port.close() except: pass try: server_socket.close() except: pass logging.info("Restarting bridge in a few seconds...") time.sleep(RECONNECT_DELAY) if __name__ == "__main__": try: run_bridge() except KeyboardInterrupt: logging.info("Bridge interrupted by user. Exiting.") sys.exit(0)