Swift CLI tools to explore sailboat data sources, in strict concurrency mode.
The package ships two products:
BoatToolsKit— library, multiplatform. All the business logic: NMEA / Signal K / Victron VRM clients, parsers, Bonjour discovery, on-device sensors on every platform (read directly on Apple, pushed in by the host app elsewhere), offline sunrise / solar noon / sunset, and lookups against public services — WMO for GMDSS forecasts, Wikimedia for vessel photographs.boattools— executable, ArgumentParser-based CLI on top of the library. Nine subcommands:connect,devices,pilot,windlass,file,vrm,discover,gmdss,simulate. The device-sensor fallback is aBoatToolsKitfeature only — it is not exposed by the CLI.
A third piece lives in the nested Bridge/ package:
libBoatToolsBridge, a dynamic library exposing BoatToolsKit through a
plain C ABI (boattools_bridge_*) — NMEA parsing, streaming connections of
every kind the app offers (TCP, UDP, a URL for Signal K over ws/http,
Victron VRM, a recorded log replayed from disk, and a parametrable synthetic
passage — all polled), a connection test that waits for the first data, a
host-pushed device-sensor feed (position, heading, barometric pressure — for
Android, Windows and Linux hosts that read their own hardware and push it in),
AIS target details, Wikimedia vessel photographs, GMDSS forecasts, sun times, the NMEA 2000
device inventory with an ISO Request roll call, and autopilot / windlass
commands — so non-Swift hosts (C# via P/Invoke, Python via
ctypes…) reuse the same decoding, transports and commands instead of
reimplementing them. Build it with swift build -c release from Bridge/
(on Windows, pass the CCurl include/lib flags as for the CLI); every
returned string is a caller-owned UTF-8 buffer released with
boattools_bridge_string_free.
Several handles can be open at once and polled independently — that is how a host builds a multiplexer, one connection per source. Signal K and VRM feeds carry no NMEA 2000 session, so they answer no device inventory and accept no pilot commands; TCP, the simulator and a replayed log do.
Download pre-built binaries for macOS, Windows and Linux from the GitHub releases page — it carries both the latest stable release and a rolling pre-release.
The macOS package is signed and notarised by Apple, so it installs without
any Gatekeeper warning, dropping boattools into /usr/local/bin. It is fully
self-contained — no extra runtime to install.
Install a Swift toolchain with swiftly, the official installer:
curl -O https://download.swift.org/swiftly/linux/swiftly-$(uname -m).tar.gz
tar zxf swiftly-$(uname -m).tar.gz && ./swiftly initwinget install --id Swift.Toolchain -eThe discover feature relies on the Avahi compatibility library for Bonjour/mDNS. It is not required if you do not plan to use the discover feature.
sudo apt-get install -y libavahi-compat-libdnssd1The Windows application relies on the Microsoft.VCRedist package. It is generally already installed on most PCs. Running the command below is harmless if it is already present.
winget install --id Microsoft.VCRedist.2015+.x64 -eThe pre-built Linux and Windows binaries link the Swift runtime (they are not self-contained), so the target machine installs the Swift runtime, macOS has no such requirement.
make build # debug build
make release # optimised build
make run # build and run boattools
make test # run the test suite
Build through make rather than swift build directly: the version target
regenerates the embedded version string (Sources/BoatTools/Version.generated.swift,
git-ignored) from git describe, so boattools --version stays accurate. A bare
swift build still works, but the version is only refreshed when you run make.
All socket-level code lives behind four
platform-neutral protocols (TCPTransport, UDPTransport, HTTPTransport,
WebSocketTransport) selected at compile time through NetworkStack:
| Apple / Linux | Windows | |
|---|---|---|
| TCP | SwiftNIO (ClientBootstrap) |
libcurl (CONNECT_ONLY=1, curl_easy_send/recv) |
| UDP (broadcast + multicast) | SwiftNIO (DatagramBootstrap) |
Winsock (recvfrom, IP_ADD_MEMBERSHIP) |
| HTTP(S) | AsyncHTTPClient | libcurl easy interface (Schannel TLS) |
| WebSocket | WebSocketKit | libcurl (CONNECT_ONLY=2, curl_ws_send/recv) |
The Windows build links libcurl statically through the CCurl
system-library target, so boattools.exe ships no libcurl/zlib DLLs (the Swift
runtime DLLs remain a separate dependency — see Runtime
requirements). Install libcurl with vcpkg
using the static triplet (-md = dynamic C runtime, matching the Swift
runtime); the websockets feature is required (WebSocket support is
enabled by default in curl ≥ 8.11 but vcpkg still gates it behind a feature
flag):
vcpkg install curl[websockets]:x64-windows-static-md
Then point the compiler and linker at the vcpkg tree, e.g.:
swift build -Xcc -I%VCPKG_ROOT%\installed\x64-windows-static-md\include ^
-Xlinker /LIBPATH:%VCPKG_ROOT%\installed\x64-windows-static-md\lib
(That is exactly what the Windows CI workflow does — see
.github/workflows/windows.yml, which also
verifies that the produced executable imports no curl/zlib DLL.)
- All public types are Sendable (
BoatMetric,NMEAFrame,JSONValue, configs, errors). - Signal K snapshots are typed
JSONValue(aSendableenum) rather than[String: Any]. SignalKClient:final class Sendable, mutable token managed by an internalactor TokenStore. Authentication via bearer token or username / password.VictronVRMClient:final class Sendable, no mutable state.NMEATransport:struct Sendable. State (LineAggregator, FrameDispatcher, the assemblers) is confined to the single task that consumes the byte stream — no manual lock and no@unchecked Sendable.NMEASession:final class @unchecked Sendable— the outbound path (the device directory, the resolved wire format, the fast-packet sequence counter) is reachable from both the consuming task and any caller ofsend(_:), so every mutable member is confined behind anNSLockinstead of task confinement; justified in a comment at the declaration site, per the project's@unchecked Sendableconvention.- Explicit lifecycle: async
shutdown(), no magic deinit. - Upcoming features enabled:
ExistentialAny,InternalImportsByDefault. - Diagnostic frames: every transport emits
.invalidChecksum(rawLine:)for bad-XOR NMEA sentences and.unknown(rawLine:)for unparseable lines / non-conforming Signal K JSON. The CLI prints them in red / orange when stdout is a TTY.
The reusable surface of BoatToolsKit, grouped logically. Decoders, fast-packet
assemblers and other parsing internals are kept internal — consumers feed raw
frames to the clients and the metric store and let them dispatch.
Models — value types emitted by clients and the metric store.
BoatMetric— canonical numeric metric (name, value, unit, timestamp).NMEAFrame— tagged-union of decoded frame variants (.nmea0183,.nmea2000,.metric,.aisTarget,.gsvReport,.invalidChecksum,.unknown).AISTarget— decoded AIS position / static report.SatelliteInfo— single satellite from a GSV / sats-in-view report.Country— MID-derived flag for an AIS MMSI.JSONValue—SendableJSON tree returned by Signal K REST snapshots.FileFrame— frame + optional embedded timestamp, emitted by file replay.ReplayPacing— how a recorded log is replayed: honour the file's own timestamps, or emit at a fixed number of lines per second.VesselPhoto— a freely-licensed vessel photograph from Wikimedia Commons: image URL, Commons page, vessel name,author,licence, and acreditline ready to render. Its licences require attribution — show the credit wherever the image is shown.BoatCloudError— transport / parsing failure.CommandError— an autopilot / windlass order could not be sent:.noAutopilot,.unsupportedAutopilot(_:)(an identified brand with no implemented dialect),.unsupportedCommand(_:dialect:)(a dialect gap, e.g. no absolute locked heading).- NMEA enums:
TalkerId,MessageId,AisMessageType,NavigationStatus,ManeuverIndicator,ShipType,NavigationalAidType.
Metric store — observable aggregation of resolved metrics. See METRIC_STORE.md.
BoatMetricStore—@Observable @MainActorstore; feed itNMEAFrameorBoatMetricvalues viafeed(_:),feedSignalK(_:),feedMetric(_:),feedMetrics(_:), or the async-stream piping helperspipe(_:)/pipeSignalK(_:)/pipeMetrics(_:). AIS exposesaisTargets,ownShip(own MMSI excluded from targets) andisStale(_:).labels/setLabels(_:)carry human display names for metric prefixes (e.g.battery.0→ "Lynx 24").clear()/clearAIS()reset published state when switching source or disconnecting —clear()also resets the pending one-second window so stale frames cannot resurface on the next tick.ConnectionMultiplexer—@Observable @MainActor; feeds several live sources into oneBoatMetricStoreat once for a "listen to everything" mode, owning one task per source, tracking each source's liveness (SourceState:.connecting/.ended/.failed) and tearing them all down together.TimedSample,RingBuffer<T>,TieredHistory,PressureHistory— history primitives backing the store'swindTWS,sog,pressure, etc.
Clients — talk to live data sources.
SignalKClient— REST snapshots (snapshot(...)), WebSocket live stream (liveStream(...)), and raw NDJSON delta streams over TCP / UDP (tcpStream(...),udpStream(...)), with token- or password-based auth (login(...)).VictronVRMClient— VRM Portal HTTP API:installations(),diagnostics(siteId:), andmetrics(siteId:)mapped onto canonical metric names nested under per-device prefixes (battery.0.,solar.1.,tank.,vebus.,system.).labels(...)fetches the installation's custom device names;frameStream(...)polls continuously, or takes a single snapshot when the interval is zero.DiagnosticRecordexposesdevice,instanceand aunitstripped of its printf format.GMDSSForecastService— official GMDSS high-seas text forecasts from the WMO WWMIWS service:forecast(metarea:)for a whole METAREA (1–21), orforecast(latitude:longitude:)which resolves the position to its METAREA and keeps the matching directional sub-bulletin. The transport is injectable (URLSessionby default);GMDSSForecast/GMDSSBulletincarry the title, issue time, sub-area label and body text. The position resolution and sub-bulletin split are also exposed standalone —metarea(latitude:longitude:)andbulletins(_:coveringLatitude:longitude:in:)— for callers holding an already-fetched (e.g. logged) bulletin set.VesselPhotoService— a freely-licensed vessel photograph by IMO number, via two public Wikimedia APIs: Wikidata (the ship item carrying that IMO inP458, and itsP18image) then Commons (the thumbnail at the requested width, plus the photographer and licence).photo(imo:width:)returns aVesselPhoto— image URL, Commons page, vessel name,author,licenceand a ready-to-rendercredit— ornil, which is the common case: only vessels notable enough for Wikidata and pictured on Commons have one (large merchant and passenger ships often, pleasure craft essentially never). A miss is never an error. Lookups are cached in memory, misses included, so revisiting a target costs nothing. The CC licences require attribution: showcreditwherever the image is shown.NMEASimulator— generates a synthetic NMEA 2000 passage as anNMEAFramestream (frameStream(route:speedKnots:timeMultiplier:loop:)): position, COG/SOG, heading, wind, depth and AIS, along aSimulatorRoute(SimulatorRoute.presets, e.g..monacoToMaddalena).historyBackfill(...)seeds the store with a plausible past so charts are not empty on connect.session(route:speedKnots:timeMultiplier:updateInterval:loop:)returns a fullNMEASessioninstead: the same passage plus a simulated NMEA 2000 network — a Raymarine Evolution autopilot and two windlasses — that answers the ISO Request roll call, broadcasts its status and obeys the commands sent back on the session, so a pilot/windlass remote control can be exercised with no boat attached.- Each client also offers
staticstream factories (SignalKClient.liveStream(config:)/.tcpStream(...)/.udpStream(...),VictronVRMClient.frameStream(accessToken:siteId:...)) that manage the underlying transport internally, so callers can pipe them straight into the store without touching the networking stack.
Transport — NMEA over TCP / UDP / file.
NMEATransport— opens a TCP or UDP socket, demultiplexes lines, runs the multipart / fast-packet / GSV assemblers, emitsNMEAFramevalues.session(config:)returns anNMEASessioninstead of a bare stream when the outbound path is wanted too.NMEASession— a live connection: the inboundframesstream plus, on TCP, transmission onto the network.send(_:)encodes anOutboundMessagein the connection's wire format;isTransmitCapabletells whether the connection can speak at all;devices()/device(at:)expose the connection's own device directory;interrogateDevices(destination:)broadcasts the ISO Request roll call.OutboundMessage— a protocol-neutral message to transmit (an autopilot command, a windlass order, an ISO request):.nmea0183(body:)gains its$and checksum,.nmea2000(pgn:destination:priority:data:)is encoded per gateway envelope — RAW frames with fast-packet fragmentation, iKonvert!PDGY, SeaSmart$PCDIN. Receive-only formats (Signal K, Canboat PLAIN, UDP listeners) refuse to transmit.ConnectionMultiplexer.send(_:toDeviceAt:)— routes an outbound message in "listen to everything" mode: to the session(s) that heard the target source address when known, otherwise to every transmit-capable session.NMEA2000Commands— command builders for the devices a sailor drives.autopilot(in:)identifies the pilot (ISO class 40 / function 150) and selects its dialect from the manufacturer code (AutopilotBrand);messages(for:brand:destination:)encodes anAutopilotCommand(standby / engage / wind-vane / track / ±N° / locked heading). Implemented dialects: Raymarine Evolution (126208 writes of 65379 and 65360, SeaTalk 126720 keystrokes), Navico — Simrad NAC-2/NAC-3, B&G — (Simnet AP command, PGN 130850, canboat layout) and Garmin Reactor (proprietary 126720, community reverse-engineering, alpha); Furuno and unknown brands are named but refused.seatalkSentences(for:)carries the same orders as Seatalk 1 keystrokes ($STALK,86,11,…) for Raymarine pilots behind an NMEA 0183 converter —NMEASession.send(_: AutopilotCommand)picks them automatically on an 0183 connection.message(for:windlassID:destination:)builds the standard windlass order (a 126208 command of PGN 128776) —WindlassCommand: up / down / off; NMEA 2000 only (0183 has no windlass sentence, Signal K no standard control path).SignalKClient.put(path:value:)/.autopilot(_:)— commands through a Signal K server: thesteering.autopilotPUT paths of the server's autopilot API (state, target heading, adjust), relayed to the pilot by the server's own plugin.NMEATransportMode,NMEAInputFormat— configuration enums.NMEA2000DeviceDirectory/NMEA2000Device— inventory of the devices on the NMEA 2000 network, accumulated from the device-information PGNs (60928 address claims, 126996 product information, 126998 configuration information, 126464 PGN lists, 126993 heartbeats): manufacturer, model, serial, versions, class / function, instances, load equivalency, PGN lists, last seen.interrogationLines(destination:)yields the ISO Requests (YD RAW transmit format) that make every device announce itself.ConnectionOwnershipManager— AppGroup-backed primary / secondary election so several processes (e.g. main app + widget) can share one upstream connection.
Device sensors — the phone/laptop's own GPS, compass and barometer, as a fallback when the boat's network has nothing. Supported on every platform, through two entry points that emit the same canonical metrics (lat, lon, SOG, COG, HDG.*, magneticVariation, pressure.atmospheric), so downstream code never knows which one fed it:
| Platform | Entry point | What reads the hardware |
|---|---|---|
| iOS, iPadOS, macOS, watchOS, visionOS | DeviceSensors |
BoatToolsKit itself — CoreLocation + CoreMotion |
| Android | ExternalSensorFeed |
the host app — Android location, compass, barometer |
| Windows | ExternalSensorFeed |
the host app — Windows.Devices location, compass, barometer |
| Linux | ExternalSensorFeed |
the host app — gpsd (position, and heading when a compass is behind it) |
DeviceSensors— Apple platforms, where Swift reads the sensors directly.DeviceSensorsConfig— sensor-selection and accuracy knobs (DeviceSensors).DeviceFallback(withDeviceFallback.Config) — Apple platforms; watches the store and startsDeviceSensorsonly while a given metric (position, heading, pressure) is missing or stale, automatically standing down when network data returns.ExternalSensorFeed— every other platform: Swift cannot reach their sensor APIs, so the host reads its own GPS/compass/barometer and pushes readings in —pushLocation(latitude:longitude:altitudeMetres:speedMetresPerSecond:courseDegrees:timestamp:),pushHeading(magneticDegrees:trueDegrees:timestamp:)(also derivesmagneticVariationwhen both headings are given),pushPressure(hectopascals:timestamp:)— turning them into the exact same canonical metricsDeviceSensorsemits, so downstream code stays platform-blind. Thread-safe (push from any thread);stream()yields the canonicalBoatMetrics. This is what the bridge'sboattools_bridge_open_device_feed/_push_location/_push_heading/_push_pressurewrap for non-Swift hosts (e.g. ThoosaUno's C#DeviceSensorsServiceon Android, Windows and Linux).
Curve histories — MetricHistories smooths and retains the charted metrics (TWS, TWD, AWS, AWA, SOG, COG, depth, water temperature: 5 s over 1 h and 1 min over 6 h, angles by circular mean; pressure: 30 min over 48 h). BoatMetricStore keeps one, and the bridge opens others for hosts that merge their own connections (boattools_bridge_history_open / _add / _series / _close).
Sun — offline, no network, the same minutes on every platform.
SunTimes— sunrise, solar noon and sunset of a local day at a position (NOAA solar calculator algorithm, about a minute's accuracy up to ±72° latitude);Horizonselects the official events or civil / nautical / astronomical dawn and dusk;polarreports a day when the Sun neither rises nor sets.SunTimes.upcoming(latitude:longitude:after:)gives the next sunrise, solar noon and sunset asSunEvents, soonest first — what the apps' Sun pill shows. The bridge exposes them asboattools_bridge_sun_timesandboattools_bridge_sun_upcoming.
Parsing — most parsers are internal. The one exposed type:
NMEA0183Parser— stateless sentence parser used by the CLI to filter decoded vs unknown sentence types. NMEA 2000, AIS, SeaSmart, Canboat, iKonvert and YD RAW decoders are reached indirectly throughNMEATransport. Full decoder coverage inDECODERS.md.
How decoded values are normalised, prioritised across talkers, deduplicated and persisted in the metric store. Full document: METRIC_STORE.md.
Which NMEA 0183 sentences, NMEA 2000 PGNs and Signal K paths BoatToolsKit decodes, indexed by canonical metric. Full reference: DECODERS.md.
- BoatTools — Decoder Coverage
- NMEA 0183
- NMEA 2000
- Signal K
- Index by canonical metric
- Transmitted frames (outbound) — the pilot / windlass / roll-call frames the library sends
boattools connect — all transports: TCP, UDP broadcast/multicast, Signal K web
boattools devices — inventory the devices present on the NMEA 2000 network
boattools pilot — send an order to the autopilot (brand dialect auto-selected)
boattools windlass — drive the anchor windlass (standard NMEA 2000 order)
boattools file — read and parse a local log file
boattools vrm — Victron VRM cloud
boattools discover — LAN discovery via Bonjour/mDNS
boattools gmdss — official GMDSS high-seas forecasts (WMO WWMIWS)
boattools simulate — replay a synthetic NMEA 2000 passage
boattools --version — print the version string
connect
- NMEA over TCP — connect to a gateway / MFD
- NMEA over TCP — with explicit duration
- Signal K NDJSON over TCP
- NMEA over UDP — broadcast
- NMEA over UDP — multicast
- Signal K WebSocket stream — auto-detected from
ws:// - Signal K WebSocket stream — forced from an
http://URL - Signal K snapshot (HTTP GET, one-shot)
- Signal K snapshot polled every 5s for one minute
- Signal K snapshot polled every 30s forever (Ctrl-C to stop)
- Signal K via Victron's authenticated relay
- Capture a live session to a log file
devices
file
- Read a local log file — dump as fast as possible
- Read a log file at a fixed rate
- Replay a log file at the original recorded pace
vrm
- Victron VRM — list installations attached to my user
- Victron VRM — diagnostics of a single site (one-shot)
- Victron VRM — monitor a site every 60s for 10 minutes
- Victron VRM — monitor a site every 5 min forever
discover
- LAN discovery via Bonjour — interactive menu, then connect
- LAN discovery — list only, no prompt (scripting / piping)
- LAN discovery — longer scan window
gmdss
simulate
# Classic syntax
./boattools connect --host 10.0.0.50 --port 10110
# URL syntax
./boattools connect --url tcp://10.0.0.50:10110./boattools connect --host 10.0.0.50 --port 10110 --duration 10
./boattools connect --url tcp://10.0.0.50:10110 --duration 10./boattools connect --host 10.0.0.50 --port 8375 --format signalk
./boattools connect --url tcp://10.0.0.50:8375 --format signalk# Classic syntax
./boattools connect --port 10110
# URL syntax
./boattools connect --url udp://:10110# Classic syntax
./boattools connect --port 10110 --multicast 239.0.0.1
# URL syntax
./boattools connect --url udp://239.0.0.1:10110./boattools connect --url ws://10.0.0.50:3000./boattools connect --url http://10.0.0.50:3000 --live./boattools connect --url http://10.0.0.50:3000./boattools connect --url http://10.0.0.50:3000 --watch 5 --duration 60./boattools connect --url http://10.0.0.50:3000 --watch 30./boattools connect --url https://654321-signalk.proxyrelay9.victronenergy.com/ --username test --password test# Capture a WebSocket stream for 1 hour
./boattools connect --url ws://10.0.0.50:3000 --log ~/session.log --duration 3600
# Capture NMEA over TCP, no time limit (Ctrl-C to stop)
./boattools connect --host 10.0.0.50 --port 10110 --log ~/nmea.logRaw lines are written as received — NMEA sentences, Signal K NDJSON deltas, YD RAW hex.
The file can be replayed immediately with file.
Bare YD RAW frames carry no timestamp, so on capture they are written with a
<HH:mm:ss.SSS> R prefix — this lets file --realtime replay the capture at the
original pace. Other formats already embed a timestamp (or carry their own
framing) and are written verbatim.
# TCP RAW gateway — broadcasts an ISO Request first, so every device answers
./boattools devices --host 10.0.0.50 --port 1457
# UDP broadcast — purely passive (devices are heard as they announce themselves)
./boattools devices --port 2000 --format ydraw --duration 30Collects the device-information PGNs — 60928 address claims, 126996 product information, 126998 configuration information, 126464 PGN lists, 126993 heartbeats — and prints one block per device: manufacturer, model, serial, software version, class / function, instances, certification, load equivalency, transmitted and received PGNs.
━━ @035 GPS 24xd — Garmin ━━
kind Navigation · Ownship Position (GNSS)
instances device 0 · system 0
unique number 123456
NAME 0xC27891051CA1E240 · self-addressing
product code 9876
software 2.60
serial SN-0042
transmits 126992, 129025, 129026, 129029, 129539, 129540
heartbeat every 60.0 s
last seen 14:07:12
On UDP (receive-only) the command stays passive; use --no-request to force
the same on TCP. Signal K web sources do not relay these PGNs — point the
command at the gateway itself.
# Identify the pilot (ISO class 40 / function 150), speak its dialect
./boattools pilot auto --host 10.0.0.50 --port 1457
./boattools pilot -- -10 --host 10.0.0.50 --port 1457 # 10° to port
./boattools pilot heading 235 --host 10.0.0.50 --port 1457
./boattools pilot standby --host 10.0.0.50 --port 1457
# The standard NMEA 2000 windlass order (a command of PGN 128776)
./boattools windlass up --host 10.0.0.50 --port 1457
./boattools windlass off --host 10.0.0.50 --port 1457The pilot command first broadcasts the ISO Request roll call, waits for the autopilot's address claim, then encodes the order in the brand's dialect.
Two more transports carry the same orders:
# Raymarine behind a Seatalk 1 ⇄ NMEA 0183 converter — $STALK keystrokes
./boattools pilot standby --host 10.0.0.51 --port 10110 --format nmea0183
# Through a Signal K server's autopilot API (server-side plugin required)
./boattools pilot auto --url http://10.0.0.60:3000 --token XYZ| Pilot | Protocol | Frames | standby / auto / wind | track | ±N° | heading D | Status |
|---|---|---|---|---|---|---|---|
| Raymarine Evolution (EV-1/EV-2) | NMEA 2000 | 126208 writes of 65379 / 65360, SeaTalk keystrokes on 126720 | ✓ | ✓ | ✓ | ✓ | community sequences, not yet hardware-validated |
| Raymarine Seatalk 1 (ST1000+, ST4000+, …) | NMEA 0183 via a Seatalk converter | $STALK,86,11,… keystrokes |
✓ | ✓ | ✓ | — | keystroke codes per the Seatalk reference |
| Navico — Simrad NAC-2/NAC-3, B&G, Lowrance | NMEA 2000 | Simnet AP command, PGN 130850 | ✓ | ✓ | ✓ | — | canboat layout, proven on NAC-3 by the Signal K plugin |
| Garmin Reactor | NMEA 2000 | proprietary 126720 | ✓ (no track) | — | ✓ (±15°/±1° steps) | — | alpha — community reverse-engineering |
| Any pilot behind a Signal K server | Signal K | PUT steering.autopilot.* |
✓ | ✓ | ✓ | ✓ | needs the server's autopilot plugin |
| Furuno NavPilot | — | — | — | — | — | — | not supported — no public dialect; identified and refused by name |
| Other / unknown brands | — | — | — | — | — | — | not supported — identified and refused by name |
“—” inside a supported row means the dialect itself cannot express that order (the library refuses with the command and dialect names rather than sending a guess).
| Protocol | Frames | up / down / off | Notes |
|---|---|---|---|
| NMEA 2000 | 126208 command of PGN 128776 (direction control) | ✓ | the standard order — brand-independent (Lewmar, Maxwell, Quick…), addressed to the windlass heard on the bus or broadcast, --windlass selects the unit |
| NMEA 0183 | — | — | not supported — the standard defines no windlass sentence |
| Signal K | — | — | not supported — no standard control path in the specification |
./boattools file /path/to/log.nmea
./boattools file /path/to/signalk.ndjsonAuto-detects the format from the content: NMEA 0183, YD RAW (bare or with a
<timestamp> R/T logging prefix), SeaSmart ($PCDIN), Signal K NDJSON,
Canboat PLAIN CSV, and Digital Yacht iKonvert (!PDGY, Base64 N2K).
Override with --format nmea0183|ydraw|seasmart|signalk|canboat|ikonvert if needed.
# 10 frames per second
./boattools file /path/to/log.nmea --rate 10
# 1 frame per second
./boattools file /path/to/log.nmea --rate 1./boattools file /path/to/signalk.ndjson --realtime
./boattools file /path/to/log.nmea --realtimeTimestamps are extracted from the data itself:
- Signal K NDJSON:
updates[].timestampfield (ISO 8601) - NMEA 0183: date + time from
RMCsentences (DDMMYY + HHMMSS) - Canboat PLAIN: the leading ISO 8601 timestamp column
- YD RAW: the
<time> R/Tlogging-prefix time (time of day only — replay uses the interval between successive lines, so a capture that does not cross midnight replays at the right pace)
Lines with no recognisable timestamp are emitted immediately in sequence.
--realtime and --rate are mutually exclusive.
./boattools vrm --user-id 123456 --token 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff./boattools vrm --user-id 123456 --token 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff --site-id 654321./boattools vrm --user-id 123456 --token 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff --site-id 654321 --watch 60 --duration 600./boattools vrm --user-id 123456 --token 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff --site-id 654321 --watch 300discover browses several service types:
- Standards:
_signalk-http._tcp,_signalk-ws._tcp,_nmea-0183._tcp - Vendor-specific (best-effort, unverified):
_garmin-marine._tcp,_navico-mfd._tcp,_raymarine-net._tcp,_furuno-navnet._tcp
On selection, every endpoint is dispatched via connect --url <scheme://host:port>:
Signal K → ws:// or http://, NMEA / vendor → tcp://. Vendor protocols are
often proprietary binary — you'll likely see orange unknown: lines, but at
least the TCP connection works.
./boattools discover./boattools discover --no-interactive./boattools discover --timeout 10Prints every bulletin issued for the METAREA (1–21). METAREA 3 (the
Mediterranean & Black Sea) carries two: a WEST bulletin from Météo-France and
an EAST one from the Hellenic service.
./boattools gmdss --metarea 3Resolves the position to its METAREA and prints only the matching sub-area bulletin. Off Sicily, that is the western-Mediterranean (Météo-France) text.
./boattools gmdss --lat 37.1 --lon 14.1The data is the official GMDSS high-seas text, fetched from the WMO Worldwide Met-ocean Information and Warning Service (WWMIWS).
Streams a realistic NMEA 2000 passage (position, COG/SOG, heading, wind, depth,
AIS…) from the built-in simulator — handy for exercising downstream tools with
no hardware. --fast speeds up the movement only; the reported SOG stays
realistic.
./boattools simulate --list # available routes
./boattools simulate # Monaco → La Maddalena at 6 kn
./boattools simulate --speed 8 --fast 50 # faster passage, fast-forwarded