seismo-relay v0.12.5
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 + Download + 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, delete_all_events, push_config, get_call_home_config, …)
│ ├── framing.py ← Frame builders, DLE codec, S3FrameParser
│ ├── models.py ← DeviceInfo, Event, ComplianceConfig, MonitorLogEntry, CallHomeConfig, …
│ └── blastware_file.py ← Write events to Blastware-compatible .AB0 files
│
├── sfm/ ← SFM REST API server (FastAPI, port 8200)
│ ├── server.py ← Live device endpoints + DB query endpoints + caching
│ ├── database.py ← SeismoDb — SQLite persistence (events, monitor_log, ach_sessions, sessions table)
│ └── sfm_webapp.html ← Embedded web UI with Call Home config tab
│
├── 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 with raw capture checkboxes
│ └── 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.
In-memory caching — frequently-polled endpoints avoid redundant TCP round-trips
via a thread-safe _LiveCache (plain Python dict + threading.Lock):
| Method | URL | Cache Strategy |
|---|---|---|
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; invalidated by monitor start/stop |
GET |
/device/call_home |
Fresh read from device (not cached) |
POST |
/device/connect |
— |
POST |
/device/config |
Writes compliance config; invalidates info + events cache |
POST |
/device/config/project |
Patches project/client/operator/sensor_location strings |
POST |
/device/monitor/start |
Sends SUB 0x96; immediately evicts status cache |
POST |
/device/monitor/stop |
Sends SUB 0x97; immediately evicts status cache |
POST |
/device/call_home |
Reads, patches specified fields, writes back to device |
Cache bypass — All cached endpoints accept ?force=true to skip the cache and
force a fresh read from the device.
Cache stats — GET /cache/stats returns hit/miss counts and TTL info; DELETE /cache/device
clears the device cache immediately.
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)
ach_cfg = client.get_call_home_config() # Auto Call Home settings (SUB 0x2C)
# Write
client.apply_config(
sample_rate=1024,
recording_mode="Continuous", # Single Shot / Continuous / Histogram / Histogram+Continuous
histogram_interval_sec=15, # 2, 5, 15, 60, 300, 900
trigger_level_geo=0.5,
geo_range="Normal", # Normal (10.000 in/s) / Sensitive (1.25 in/s)
project="Bridge Inspection 2026",
client_name="City of Portland",
operator="B. Harrison",
)
client.set_call_home_config(
auto_call_home_enabled=True,
after_event_recorded=True,
at_specified_times=True,
time1_hour=18, time1_min=30, # 6:30 PM
time2_hour=6, time2_min=0, # 6:00 AM
)
# 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) using the
SeismoDb persistence layer. Four tables, all unit-keyed by serial number:
| Table | Key | Contents |
|---|---|---|
ach_sessions |
UUID | Per-call-home audit record: serial, timestamp, peer IP, events_downloaded, monitor_entries, duration_seconds |
events |
UUID, UNIQUE(serial, waveform_key) | Triggered events: timestamp, Tran/Vert/Long/VectorSum/Mic PPV, project/client/operator/sensor_location strings, sample_rate, record_type, false_trigger flag |
monitor_log |
UUID, UNIQUE(serial, waveform_key) | Monitoring intervals: serial, waveform_key, start_time, stop_time, duration_seconds, geo_threshold_ips |
events.false_trigger |
Boolean flag | PATCH endpoint to mark/unmark false triggers for review |
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. Key-based state tracking allows correct
handling of device erasures (external or post-download).
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
Compliance Config Features (v0.12.2–v0.12.3)
The REST API and web UI expose full control over device compliance settings:
- Recording Mode (Single Shot / Continuous / Histogram / Histogram+Continuous)
- Sample Rate (1024 / 2048 / 4096 sps)
- Record Time (float, seconds)
- Histogram Interval (2s, 5s, 15s, 1m, 5m, 15m) — when recording mode includes histogram
- Geo Trigger Levels (float, in/s per channel)
- Geo Maximum Range (Normal 10.000 in/s / Sensitive 1.250 in/s per channel)
- Project / Client / Operator / Sensor Location (ASCII strings)
Auto Call Home config:
- Auto Call Home Enable (bool)
- Dial String (read-only; 40-byte ASCII)
- Trigger on Event (bool)
- Scheduled Call-Ins (two time slots with HH:MM each)
- Retry Settings (count, delay, connection timeout, warm-up time)
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.
Key Features (v0.10–v0.12)
Device support (v0.12.5):
- Full read/write/erase pipelines
- Compliance config (recording mode, sample rate, histogram interval, geo sensitivity, project strings)
- Auto Call Home config (read/write ACH settings, dial string, time slots, retries)
- Monitor control (start/stop, status polling, battery/memory)
- Monitor log entries (continuous monitoring intervals without full waveform download)
Data persistence (v0.11):
- SQLite database (
seismo_relay.db) with 4 tables: ach_sessions, events, monitor_log, plus false_trigger flag - Deduplication by waveform key (handles re-runs and repeat call-homes)
- Post-erase key-reuse detection (tracks high-water mark)
- Session state (
ach_state.json) with downloaded keys and max key
REST API (v0.12.1):
- Live device endpoints with in-memory caching (
_LiveCache) - Cache statistics (
/cache/stats) and manual invalidation (/cache/device) - DB query endpoints (units, events, monitor_log, sessions, false_trigger PATCH)
- Call Home config read/write endpoints
- Blastware file download endpoint (
/device/event/{index}/blastware_file)
File output (v0.7+):
- Blastware-compatible
.AB0file generation (waveform + metadata) - Multi-channel waveform decode from SUB 5A bulk stream
- Second-resolution timestamp encoding in Blastware filename
Capture tools (v0.12.5):
- Serial-to-TCP bridge with raw BW/S3 capture (s3_bridge.py, defaults to auto-capture)
- GUI bridge with raw capture checkboxes (gui_bridge.py)
- ACH inbound server with bidirectional capture (ach_server.py saves raw_tx + raw_rx)
- Transparent TCP MITM proxy for live BW session capture (ach_mitm.py)
Analysis tools:
- s3_analyzer.py — session parser, frame differ, Claude export
- gui_analyzer.py — standalone analyzer GUI
- frame_db.py — SQLite frame database for capture analysis
seismo_lab.py GUI (v0.12.5):
- Bridge tab — Serial/TCP mode selector with raw capture options
- Analyzer tab — BW/S3 capture playback and differencing
- Download tab — Live wire-byte capture during event download (new v0.12.5)
- Console tab — Logging and diagnostics
Roadmap (Future)
- 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
ComplianceConfigobject - Modem manager — push RV50/RV55 configs via Sierra Wireless API
- Histogram mode recording support (5A stream analysis for mode 0x03)
- Call Home dial_string write support (requires DLE escaping for embedded control characters)