2026-04-17 03:56:33 +00:00

seismo-relay v0.12.1

A ground-up replacement for Blastware — Instantel's aging Windows-only software for managing MiniMate Plus seismographs.

Built in Python. Runs on Windows, Linux, or macOS. Connects to instruments over direct RS-232 or cellular modem (Sierra Wireless RV50 / RV55).

Status: Active development. Full read + write + erase + monitoring pipeline working end-to-end over TCP/cellular. ACH Auto Call Home server handles inbound unit connections, downloads events, and persists everything to a SQLite database. SFM REST API exposes device control and DB queries. See CHANGELOG.md for full version history.


What's in here

seismo-relay/
├── seismo_lab.py              ← Main GUI (Bridge + Analyzer + Console tabs)
│
├── minimateplus/              ← MiniMate Plus client library
│   ├── transport.py           ←   SerialTransport, TcpTransport, SocketTransport
│   ├── protocol.py            ←   DLE frame layer, SUB command dispatch
│   ├── client.py              ←   High-level client (connect, get_events, push_config, …)
│   ├── framing.py             ←   Frame builders, DLE codec, S3FrameParser
│   └── models.py              ←   DeviceInfo, Event, ComplianceConfig, MonitorLogEntry, …
│
├── sfm/                       ← SFM REST API server (FastAPI, port 8200)
│   ├── server.py              ←   All device + DB endpoints
│   ├── database.py            ←   SeismoDb — SQLite persistence layer
│   └── sfm_webapp.html        ←   Embedded web UI (served at /)
│
├── bridges/
│   ├── ach_server.py          ←   Inbound ACH call-home server (main production server)
│   ├── ach_mitm.py            ←   Transparent MITM proxy for capturing BW sessions
│   ├── s3-bridge/             ←   RS-232 serial bridge (capture tool)
│   ├── tcp_serial_bridge.py   ←   Local TCP↔serial bridge (bench testing)
│   ├── gui_bridge.py          ←   Standalone bridge GUI
│   └── raw_capture.py         ←   Simple raw capture tool
│
├── parsers/
│   ├── s3_analyzer.py         ←   Session parser, differ, Claude export
│   ├── gui_analyzer.py        ←   Standalone analyzer GUI
│   └── frame_db.py            ←   SQLite frame database
│
└── docs/
    └── instantel_protocol_reference.md  ← Reverse-engineered protocol spec

Quick start

ACH inbound server (production)

Listens for inbound unit call-homes, downloads all new events and monitor log entries, and writes everything to bridges/captures/seismo_relay.db.

python bridges/ach_server.py --port 12345 --output bridges/captures/

Point the unit's ACEmanager Remote Host to this machine's IP and Remote Port to 12345.

Options:

--port N            Listen port (default 12345)
--output DIR        Capture directory (default bridges/captures/)
--allow-ip IP       Allowlist an IP (repeat for multiple; default: accept all)
--max-events N      Safety cap for first run (default: unlimited)
--clear-after-download  Erase device memory after successful download
--verbose           Debug logging

SFM REST server

Exposes device control and DB queries as a REST API. Proxied by terra-view.

python sfm/server.py               # default: 0.0.0.0:8200
python -m uvicorn sfm.server:app --host 0.0.0.0 --port 8200 --reload

Open http://localhost:8200 for the embedded web UI, or http://localhost:8200/docs for the interactive API docs.

Seismo Lab GUI

python seismo_lab.py

SFM REST API

Live device endpoints

Each call dials the device, does its work, and closes the connection. TCP connections are retried once on ProtocolError to handle cold-boot timing.

Caching — frequently-polled endpoints are cached in-process to avoid redundant TCP round-trips:

Method URL Cache
GET /device/info Indefinite; invalidated by POST /device/config
GET /device/events Count-probe fast path (~2s); full download only when new events detected
GET /device/event/{idx}/waveform Permanent per event index
GET /device/monitor/status 30-second TTL
POST /device/connect
POST /device/config Writes compliance config; invalidates cache
POST /device/monitor/start Sends SUB 0x96
POST /device/monitor/stop Sends SUB 0x97

All cached endpoints accept ?force=true to bypass the cache.

Transport query params (supply one set):

Serial:  ?port=COM5&baud=38400
TCP:     ?host=1.2.3.4&tcp_port=12345

DB read endpoints

Query the SQLite database written by ach_server.py. All read-only except PATCH /db/events/{id}/false_trigger.

Method URL Description
GET /db/units All known serials with summary stats
GET /db/events Triggered events (filter by serial, date range, false_trigger)
GET /db/monitor_log Monitoring intervals
GET /db/sessions ACH call-home session history
PATCH /db/events/{id}/false_trigger?value=true Flag / unflag false triggers

minimateplus library

from minimateplus import MiniMateClient
from minimateplus.transport import TcpTransport

# Serial
client = MiniMateClient(port="COM5")

# TCP (cellular modem)
client = MiniMateClient(transport=TcpTransport("1.2.3.4", 12345), timeout=30.0)

with client:
    # Read
    info    = client.connect()               # DeviceInfo — serial, firmware, compliance config
    count   = client.count_events()          # Number of stored events
    keys    = client.list_event_keys()       # Fast browse walk — event keys only, no download
    events  = client.get_events()            # Full download: headers + peaks + metadata
    monitor = client.get_monitor_status()    # Battery, memory, is_monitoring flag
    log     = client.get_monitor_log_entries()  # Monitoring intervals (partial 0x2C records)

    # Write
    client.apply_config(
        sample_rate=1024,
        trigger_level_geo=0.5,
        project="Bridge Inspection 2026",
        client_name="City of Portland",
        operator="B. Harrison",
    )

    # Control
    client.start_monitoring()                # SUB 0x96
    client.stop_monitoring()                 # SUB 0x97
    client.delete_all_events()              # Erase all (SUB 0xA3 → 0x1C → 0x06 → 0xA2)

get_events() runs the full per-event sequence: 1E → 0A → 0C → 5A → 1F. SUB 5A bulk stream provides client, operator, and sensor_location as they existed at record time — not backfilled from the current compliance config.


Database

ach_server.py writes to bridges/captures/seismo_relay.db (SQLite, WAL mode). Three tables, all unit-keyed by serial number:

Table Key Contents
ach_sessions UUID Per-call-home audit record: serial, peer IP, events_downloaded, duration
events UUID, UNIQUE(serial, waveform_key) Triggered events: timestamp, PPV per channel, project/client/operator strings, false_trigger flag
monitor_log UUID, UNIQUE(serial, waveform_key) Monitoring intervals: start/stop time, duration, geo threshold

Deduplication is by (serial, waveform_key) — repeat call-homes or re-runs never produce duplicate rows. Post-erase key reuse is handled automatically via the high-water mark in ach_state.json.


Connecting over cellular (RV50 / RV55)

Field units connect via Sierra Wireless RV50 or RV55 cellular modems.

Required ACEmanager settings

Setting Value Why
Configure Serial Port 38400,8N1 Must match MiniMate baud rate
Flow Control None Hardware FC blocks TX if pins unconnected
Quiet Mode Enable Critical — disabled injects RING/CONNECT onto serial, corrupting the S3 handshake
Data Forwarding Timeout 1 (= 0.1 s) Lower latency
TCP Connect Response Delay 0 Non-zero silently drops the first POLL frame
TCP Idle Timeout 2 (minutes) Prevents premature disconnect
DB9 Serial Echo Disable Echo corrupts the data stream

Protocol quick-reference

Term Value Meaning
DLE 0x10 Data Link Escape
STX 0x02 Start of frame
ETX 0x03 End of frame
ACK 0x41 Frame-start marker sent before every BW frame
DLE stuffing 10 10 on wire Literal 0x10 in payload

Response SUB rule: response_SUB = 0xFF - request_SUB (no exceptions)

Full protocol documentation: docs/instantel_protocol_reference.md


Requirements

pip install pyserial fastapi uvicorn

Python 3.10+. Tkinter is included with the standard Python installer on Windows (check "tcl/tk and IDLE" during install).


Virtual COM ports (bridge capture)

Blastware → COM4 (virtual) ↔ s3_bridge.py ↔ COM5 (physical) → MiniMate Plus

Use com0com or VSPD to create the virtual COM pair on Windows.


Roadmap

  • Full read pipeline — device info, compliance config, event download with true event-time metadata
  • Write commands — push compliance config, trigger thresholds, project strings to device
  • Erase all events — confirmed erase sequence from live MITM capture
  • Monitor control — start/stop monitoring, read battery/memory/status
  • Monitor log entries — decode partial 0x2C records (continuous monitoring intervals)
  • ACH inbound server — accept call-home connections, download events, dedup by key
  • SQLite persistence — events, monitor log, and session history in seismo_relay.db
  • SFM REST API — device control + DB query endpoints, live device cache
  • Terra-view integration — seismo-relay router, unit detail page, VISON-style event listing
  • Vibration summary reports — highest legit PPV per project → Word doc (false trigger filtering first)
  • Compliance config encoder — build raw write payloads from a ComplianceConfig object
  • Modem manager — push RV50/RV55 configs via Sierra Wireless API
S
Description
Program to talk to, listen to, and control various devices such as seismographs.
Readme 13 MiB
Languages
Python 88.5%
HTML 10.7%
Shell 0.7%