Skip to main content
Glama
README.md
# ScopeMCP — DS1102 Oscilloscope MCP Server

<p align="center">
  <img src="resources/images/ScopeMCP_klein.png" alt="ScopeMCP Logo" width="200">
</p>

A Python MCP server that connects the [**Owon/Abestop DS1102 (Firmware V3.1.0)**](resources/images/Abestop_DS1102.jpg) oscilloscope (Firmware V3.1.0) to AI assistants like Claude via the [Model Context Protocol](https://modelcontextprotocol.io/).



## Available Tools

### Status & Metadata
- `get_connection_status`: Checks connection, model, run status, and timestamp.
- `get_live_metadata`: Returns full device metadata (timebase, sample rate, channel/trigger config).
- `get_measurements`: Provides frequency, period, scale, probe factor, and coupling for both channels.

### Waveform Capture
- `capture_waveform(channel, max_samples)`: Captures a single channel (fixed at 1520 hardware samples, software downsampled).
- `capture_dual_waveform(max_samples)`: Efficiently captures both channels in a single operation.

**Voltage conversion formula**:
```
voltage = (raw - OFFSET) / 250.0 * scale_v * probe_factor
```

**Note:** The DS1102 always transfers 1520 samples over USB. Downsampling occurs in software.

### Vertical & Horizontal Control
- `set_vertical_scale(channel, scale)`: e.g., "1V", "500mV".
- `set_channel_coupling(channel, coupling)`: AC, DC, or GND.
- `set_voltage_offset(channel, offset)`: Vertical position in Volts.
- `set_horizontal_scale(scale)`: e.g., "1ms", "500us".

### Trigger & Device Control
- `set_trigger_mode(mode)`: AUTO, NORMAL, SINGLE.
- `set_trigger_source(source)`: CH1, CH2.
- `set_trigger_slope(slope)`: RISE, FALL.
- `set_trigger_level(level_mv)`: Threshold in millivolts (e.g., 500.0 for 0.5V).
- `set_run_state(state)`: RUN or STOP.
- `run_autoset()`: Performs automatic oscilloscope setup.


## Requirements

- Python 3.10+
- libusb backend (on Windows: install via [Zadig](https://zadig.akeo.ie/))
- DS1102 connected via USB

```bash
pip install mcp libusb-package pyusb
```


## Setup

Detailed installation instructions can be found in [doc/INSTALL.md](doc/INSTALL.md).

Add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "ds1102-scope": {
      "command": "python",
      "args": ["C:/path/to/ds1102_mcp.py"]
    }
  }
}
```


## Live Monitor (Grabber)

In addition to the MCP server, this repository includes `ds1102_grabber.py`. This is a lightweight live oscilloscope frontend (GUI) that allows for a real-time view of both channels directly on your PC.

### Features
- **Dual-Channel Live View**: Simultaneous display of CH1 and CH2.
- **Real-Time Synchronization**: Automatically adjusts scaling (V/div) and probe factor (1X/10X) when changed on the device.
- **Clean Grid Mode**: The grid in the grabber perfectly matches the divisions on the hardware oscilloscope (no confusing numerical labels).
- **Keyboard Shortcuts**: Basic control directly via keyboard (e.g., `SPACE` for RUN/STOP).

### Running the Grabber
Ensure the oscilloscope is connected via USB and dependencies (`matplotlib`, `numpy`, `pyusb`) are installed.

```bash
python ds1102_grabber.py
```

---

## Known Limitations

- **Capture speed (~7–10s):** The DS1102 always transfers the full 1520-sample buffer over USB. The `:DATA:WAVE:POINTS` SCPI command has no effect on transfer size for this firmware. This is a confirmed hardware limitation — see `FINDINGS_USB_Transfer_Limit.md`.
- **`run_status` never shows `STOP`:** The firmware always reports `TRIG` in the status field, even when stopped. This is a firmware behavior, not a server bug.
- **CH2 returns zeros when `DISPLAY: OFF`:** No signal data is available from a channel that is disabled on the scope.


## Protocol Notes (Firmware V3.1.0)

- Every SCPI command is prefixed with a **4-byte little-endian length header**.
- Commands must end with `\n` (0x0A).
- Screen data: 1520 samples × 2 bytes signed 16-bit little-endian = 3040 bytes + 4-byte header.
- Full protocol details: see [doc/ds1102_protocol.md](doc/ds1102_protocol.md).



## License

GNU GPLv3