Metadata-Version: 2.4
Name: serial-bridge
Version: 0.2.1
Summary: Relay a physical serial port to N remote machines over TCP
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pyserial>=3.5
Requires-Dist: pySerialMux>=0.2.1

# serial_bridge
 
Relay a physical COM/serial port over TCP to 1–N remote machines.

One machine runs in **server** mode and holds the physical port. Every other machine runs in **client** mode and gets two local virtual transports it can use like any normal serial device:

| Transport | Platform | Access |
|---|---|---|
| PTY (`/dev/pts/X`) | Unix | any program — `pyserial`, `minicom`, `screen`, etc. |
| pySerialMux virtual interface | Unix + Windows | `pySerialMux.Serial(...)` |

All machines see the same data. Writes from any machine go to the physical serial port and are re-broadcast to all other connected machines.

## Requirements

- Python 3.10+
- `pySerialMux >= 0.2.1`
- `pyserial >= 3.5`

```
pip install -r requirements.txt
```

## Quickstart

**Machine A** — has the physical port:
```bash
python -m serial_bridge server --port /dev/ttyUSB0 --baud 115200
```

**Machine B, C, …** — remote machines:
```bash
python -m serial_bridge client --host <machine-a-ip>
```

Client output:
```
Serial bridge connected to 192.168.1.10:5000 (port=/dev/ttyUSB0, baud=115200)
  PTY:          /dev/pts/4
  pySerialMux:  Serial("serial-bridge", virtual_interface="bus")
Press Ctrl-C to stop.
```

## Using the virtual port

### PTY (Unix — any program)

Use the printed path directly:
```bash
minicom -D /dev/pts/4
screen /dev/pts/4 115200
```

Or with plain pyserial:
```python
import serial
ser = serial.Serial("/dev/pts/4")
ser.write(b"hello\n")
print(ser.readline())
```

### pySerialMux virtual interface (Unix + Windows)

```python
from pySerialMux import Serial

ser = Serial("serial-bridge", virtual_interface="bus", client_id="my-app")
ser.write(b"hello\n")
print(ser.readline())
```

Multiple local processes on the same remote machine can all connect simultaneously — pySerialMux handles the local multiplexing automatically.

## CLI reference

### Server

```
python -m serial_bridge server --port PORT [options]

  --port PORT          Serial port path (e.g. /dev/ttyUSB0 or COM3)  [required]
  --baud BAUD          Baud rate (default: 115200)
  --tcp-port TCP_PORT  TCP port to listen on (default: 5000)
  --bind ADDR          Bind address (default: 0.0.0.0)
```

### Client

```
python -m serial_bridge client --host HOST [options]

  --host HOST               BridgeServer IP or hostname  [required]
  --tcp-port TCP_PORT       TCP port of the server (default: 5000)
  --client-id ID            Optional identifier for this machine
  --virtual-port NAME       pySerialMux broker name (default: serial-bridge)
  --virtual-interface NAME  pySerialMux interface name (default: bus)
```

## Python API

```python
from serial_bridge import BridgeServer, BridgeClient
import threading

# Server
server = BridgeServer(port="/dev/ttyUSB0", baud=115200, tcp_port=5000)
threading.Thread(target=server.start, daemon=True).start()
# ...
server.stop()

# Client
client = BridgeClient(host="192.168.1.10", tcp_port=5000)
descriptions = client.connect()   # returns list of transport description strings
client._launch_threads()
# serial port is now live — use PTY path or pySerialMux virtual interface
# ...
client.stop()
```

## Architecture

```
Machine A  ┌─────────────────────────────────────────┐
           │  BridgeServer                           │
           │    pySerialMux.Serial(/dev/ttyUSB0)     │
           │         ↕                               │
           │    TCP server :5000                     │
           └──────────────┬──────────────────────────┘
                          │ TCP
          ┌───────────────┴───────────────┐
          │                               │
Machine B ▼                     Machine C ▼
  BridgeClient                    BridgeClient
    ├── PTY /dev/pts/4               ├── PTY /dev/pts/7
    └── pySerialMux                  └── pySerialMux
         Serial("serial-bridge",          Serial("serial-bridge",
           virtual_interface="bus")         virtual_interface="bus")
```

Data written by any machine reaches the physical serial port and is re-broadcast to all other connected machines.

## Wire protocol

Simple framing over raw TCP — same 5-byte header layout used by pySerialMux internally:

```
[1 byte type][4 bytes payload length, big-endian][payload]
```

| Type | Direction | Payload |
|---|---|---|
| `DATA`   | both | raw binary serial bytes |
| `CONFIG` | client → server | JSON `{"client_id": "..."}` |
| `ACK`    | server → client | JSON `{"port": "...", "baud": ...}` |
| `ERROR`  | server → client | error string; connection closes |
| `CLOSE`  | either | empty; graceful disconnect |

## Notes

- No authentication or TLS. Intended for trusted local networks. Wrap with an SSH tunnel or VPN for remote access over untrusted networks.
- Windows clients get the pySerialMux virtual interface only (no PTY). The pySerialMux broker uses localhost TCP internally, so no Unix sockets are required.
- The server uses `pySerialMux.Serial` to access the physical port, so multiple local processes on the server machine can also share it simultaneously via normal `pySerialMux.Serial` usage.
