Not a distro, not an OS — one executable script that turns the Linux you already run into a marine instrument bridge. It asks what's on board, then installs only the capabilities that setup needs: CAN bus, NMEA 2000, J1939 engine profiles, J1587/J1708, standard NMEA 0183 over RS-422, MTU RS-422, documented proprietary PGNs, a passive smart CAN sniffer, and gauges at the helm.
The script interrogates your boat, not your patience. Answer what's on board and it resolves, installs, and configures only what those answers require.
$ curl -fsSL "https://keelas.sh/?dl=Keelas.sh&raw=1" -o Keelas.sh && chmod +x Keelas.sh && sudo ./Keelas.sh Keelas.sh v0.BETA-pi45.13-rs422-smart-sniffer-kiosk-autoboot — marine bridge deployment Detected: Raspberry Pi 5 · aarch64 · apt · systemd [1/11] Which hardware is physically installed? 1) Waveshare RS485 CAN HAT (12 MHz) 5) Waveshare 2-CH CAN HAT (non-Plus) 2) Waveshare RS485 CAN HAT (8 MHz) 6) PiCAN-M (MCP2515 + NMEA 0183) 3) Waveshare 2-CH CAN FD HAT Rev2.1+ 7) USB SocketCAN (gs_usb) 4) Waveshare 2-CH CAN HAT+ ← Pi 5 8) None — serial / virtual CAN only > 4 → hardware profile exposes 2 CAN channel(s) [2/11] Enable a physical NMEA 2000 network? [Y/n] > Y [3/11] Enable a physical J1939 network? [y/N] > y · bitrate > 250 kbit/s [4/11] Enable NMEA 2000 <-> J1939 conversion? [Y/n] > Y [5/11] Select J1939 engine family/families. 1 Generic · 2 Cummins NIM · 3 Cummins Tier 4 · 4 John Deere · 5 CAT C32/ADEM 6 Scania · 7 Yamaha · 8 Suzuki · 9 FPT · 10 MAN > 5,6 · source:instance map > 0x80:0,0x81:1 · raw proprietary capture [Y/n] > Y [6/11] Documented proprietary NMEA 2000 compatibility output? [y/N] > y [7/11] Legacy SAE J1708/J1587 -> NMEA 2000 conversion? [y/N] > y · port > auto [8/11] RS422 input mode for conversion to NMEA 2000? 1) Standard NMEA 0183 over RS422 2) MTU ECS-5 RS422 3) BOTH 4) None > 3 NMEA 0183 > Waveshare Port A · 4800 8N1 · RMC/GGA/GLL/VTG/heading/depth/wind/temp → N2K MTU ECS-5 > Waveshare Port B · 19200 8N1 · monitoring1 · point/alarm map loaded; framing pending [9/11] Enable passive CAN sniffer / protocol discovery? [y/N] > y 1) Auto classify 2) NMEA 2000 3) J1939 4) NMEA 2000 + J1939 5) Mercury SmartCraft / proprietary CAN 6) Raw CAN > 1 · interfaces > auto receive-only · unknown traffic remains RAW · rotating NDJSON capture enabled [10/11] Install/run Signal K? [Y/n] > Y [11/11] Boot into kiosk gauge dashboard? [Y/n] > Y Resolving dependencies for YOUR answers only: + capability: NMEA2000=y J1939=y convert=y engines=cat_c32,scania propN2K=y CAN-sniffer=y + CAN roles: can0:n2k:250k · can1:j1939:250k + translator: can0 <-> can1 · engine map 0x80:0,0x81:1 · bounded raw PGN capture enabled + prop N2K: Maretron 65284/65286/65287/130823/130840 under verified-data rules + J1587->N2K: engine/transmission/fuel/electrical/navigation data + standard alarm bits + RS422 mode: BOTH · FT4232HL Port A=NMEA 0183 · Port B=MTU ECS-5 + 0183->N2K: 4800 8N1 · position/COG-SOG/heading/depth/wind/sea temperature -> can0 + MTU RS422: FT4232HL Port B @ 19200 8N1 -> can0 (ECS-5 point/alarm map loaded; framing pending) + CAN sniff: can0,can1 · auto classify · receive-only · rotating NDJSON capture + runtime: Node 22.23.2 + Signal K/canboatjs (full installed N2K PGN decode database) Done. Reboot if required. Re-run anytime — it's idempotent.
No golden image to flash, no fork of your OS to trust. Your system stays yours — the script just rigs it.
Eleven questions: hardware first, capabilities second. Pick physical NMEA 2000/J1939 buses, conversion, engine profiles, proprietary output, J1708/J1587, then choose RS-422 mode: standard NMEA 0183, MTU ECS-5, BOTH, or none — followed by the passive CAN sniffer, Signal K and kiosk. Channel-aware so one-CAN hardware is never misrepresented as two physical networks.
A new stdlib-only bridge reads ordinary NMEA 0183 from an RS-422 adapter and publishes direct NMEA 2000 equivalents for position, COG/SOG, vessel heading, water speed, depth, wind and sea temperature. It validates checksums when present and never transmits back onto the 0183 pair.
The Series 2000 Monitoring I/II point, scaling, gear, and alarm maps are built in — raw values decode straight to NMEA 2000 fields. Native support for the Waveshare USB TO 4CH RS485/422 gateway (FT4232HL, Ports A/B). Capture-safe until the E 531 652 framing spec arrives: it records, never guesses frame boundaries.
An embedded stdlib-Python translator bridges verified engine, transmission, fuel, trip, battery and static-identification data in both directions — fast-packet and J1939 TP included. Selectable engine profiles add manufacturer-specific inventory and bounded raw discovery. Unverified proprietary layouts are captured, never fabricated.
The legacy-bridge daemon is built into the script now: read-only on the 9600 bit/s bus, converting MID/PID data and standard diagnostic alarms into NMEA 2000 status bits. Self-tests run at install; the port is wizard-chosen or auto-detected.
Built-in profiles cover Generic J1939, Cummins NIM/Tier 4, John Deere, Caterpillar C32/ADEM, Scania, Yamaha, Suzuki, FPT and MAN. Optional raw capture records unknown/profile-specific traffic, while the proprietary NMEA 2000 publisher emits only documented Maretron-compatible messages from verified values.
Optional receive-only SocketCAN discovery asks what you're sniffing: Auto, NMEA 2000, SAE J1939, both, Mercury SmartCraft/proprietary CAN, or raw CAN. It classifies known PGNs conservatively, extracts source/destination and manufacturer/profile hints, keeps unknown payloads raw, writes rotating NDJSON captures, and never transmits onto the monitored bus.
Node 22.23.2 fetched from nodejs.org, SHA-256 checked against the official sums, isolated under /opt/keelos — only if your system Node is too old. Preflight proves socketcan + canboatjs load before anything is enabled.
Re-run to add hardware later — it converges instead of clobbering, down to Signal K provider config. Keelas.sh --uninstall removes everything it added and nothing it didn't.
Raspberry Pi OS, Armbian, Ubuntu Server, whatever's already on the card. Detects apt, dnf, or pacman; keelos-can.service raises every interface with per-role bitrates in strict order before the translators and dashboard.
What Keelas.sh supports out of the box. Green-direction rows are real semantic conversions in the current bridge. Capture-only rows are intentionally not decoded until the exact manufacturer byte layout is verified.
j1939_raw_capture: y writes bounded NDJSON records with PGN, source, engine instance, matching profile, and raw payload.DM1 policy: an active J1939 DM1 asserts generic NMEA 2000 Check Engine in PGN 127489. Keelas.sh does not invent SPN-specific NMEA alarm bits when there is no defensible standard equivalent.
| J1939 PGN | Data converted | NMEA 2000 PGN | Direction |
|---|---|---|---|
| 61444 · EEC1 | Engine speed | 127488 · Engine Parameters, Rapid Update | BIDIRECTIONAL |
| 61444 · EEC1 | Actual engine torque | 127489 · Engine Parameters, Dynamic | BIDIRECTIONAL |
| 61443 · EEC2 | Percent engine load | 127489 · Engine Parameters, Dynamic | BIDIRECTIONAL |
| 65270 · IC1 | Turbo / boost pressure | 127488 · Engine Parameters, Rapid Update | BIDIRECTIONAL |
| 65262 · ET1 | Coolant temperature · oil temperature | 127489 · Engine Parameters, Dynamic | BIDIRECTIONAL |
| 65263 · EFL/P1 | Fuel pressure · oil pressure · coolant pressure | 127489 · Engine Parameters, Dynamic | BIDIRECTIONAL |
| 65266 · LFE1 | Instantaneous fuel rate | 127489 · Engine Parameters, Dynamic | BIDIRECTIONAL |
| 65271 · VEP1 | Battery / alternator voltage | 127489 · Engine Dynamic + 127508 · Battery Status | BIDIRECTIONAL |
| 65253 · HOURS | Total engine hours | 127489 · Engine Parameters, Dynamic | BIDIRECTIONAL |
| 65257 | Trip fuel used | 127497 · Trip Parameters, Engine | BIDIRECTIONAL |
| 65242 | Software identification | 127498 · Engine Parameters, Static | J1939 → N2K |
| 65260 | VIN / vehicle identification | 127498 · Engine Parameters, Static | J1939 → N2K |
| 61445 · ETC2 | Current transmission gear | 127493 · Transmission Parameters, Dynamic | BIDIRECTIONAL |
| 65272 · TRF1 | Transmission oil pressure · oil temperature | 127493 · Transmission Parameters, Dynamic | BIDIRECTIONAL |
| 65276 · DD1 | Fuel tank level | 127505 · Fluid Level | BIDIRECTIONAL |
| 65226 · DM1 | Active diagnostic presence | 127489 · generic Check Engine status | J1939 → N2K |
| 65280 · Cummins NIM | Genset load percentage | 127489 · Engine Parameters, Dynamic | J1939 → N2K |
Profiles do not magically decode undocumented manufacturer data. They tell the bridge which PGNs belong to the selected engine family and which traffic should be retained for safe discovery. Multiple profiles can be enabled together.
Cummins NIM-only installs default to the documented fixed source-to-instance map 234→0 · 158→1 · 179→2 · 203→3 unless you override it. Other profiles accept explicit mappings such as 0x80:0,0x81:1.
| PGN | Known family | Current handling |
|---|---|---|
| 65030 | Cummins NIM generator AC quantities | PROFILE CAPTURE |
| 65031 | Engine exhaust temperature | RAW UNTIL LAYOUT VERIFIED |
| 65172 | Auxiliary coolant | PROFILE CAPTURE |
| 65214 | Rated speed / engine configuration family | PROFILE CAPTURE |
| 65227 | DM2 historical diagnostics | RECOGNIZED · NOT ACTIVE ALARM |
| 65248 · 65252 · 65256 · 65265 · 65279 | Distance · shutdown/indicators · navigation speed · vehicle speed · water-in-fuel families | PROFILE CAPTURE |
| 64775 · 64891 · 64892 · 65110 · 65269 | Cummins Tier 4 / FPT aftertreatment, DEF/SCR and OEM families | PROFILE CAPTURE |
| 65247 · 65376 · 65409 · 65414 · 65415 · 65416 · 65435 · 65464 | Scania profile-specific / proprietary inventory | PROFILE CAPTURE |
| any other unknown PGN | Unclassified traffic on the selected J1939 bus | BOUNDED RAW CAPTURE WHEN ENABLED |
| NMEA 2000 output | Legacy inputs handled | Examples |
|---|---|---|
| 127488 | Engine rapid data | PID 190 RPM · PID 102 / 439 boost |
| 127489 | Engine dynamic + status | Oil/coolant/fuel pressure · temperatures · voltage · fuel rate · hours · load · alarms |
| 127493 | Transmission dynamic + status | Selected/attained gear · oil pressure/temp · diagnostic status |
| 127497 | Trip engine | Trip fuel · average fuel rate |
| 127498 | Engine static | Rated speed · VIN · software ID |
| 127505 · 127508 | Fuel tanks · battery | Primary/secondary fuel · battery voltages/current |
| 127250 · 128259 · 128275 · 129025 | Heading · speed · distance · position | Compass bearing · road speed · trip/total distance · latitude/longitude |
| 130312 · 130314 | Temperature · actual pressure | Inside/outside/exhaust temperature · barometric pressure |
J1587 alarm mappings:
Safety policy: the sniffer opens CAN_RAW sockets for reception and does not transmit discovery frames. Auto classification is conservative. Ambiguous manufacturer traffic stays UNKNOWN/RAW; SmartCraft mode labels candidates without pretending the payload is decoded.
| Sniffer mode | What Keelas.sh does | Useful output |
|---|---|---|
| Auto classify | Uses configured bus roles and known 29-bit PGN families to distinguish NMEA 2000, J1939 and proprietary candidates. | PGN, priority, source, destination, known name, raw payload |
| NMEA 2000 | Uses NMEA 2000 context and recognizes the built-in N2K PGN inventory, including documented Maretron messages. | N2K PGN + manufacturer hints |
| SAE J1939 | Uses J1939 context and recognizes standard plus selected Cummins, Scania, FPT and engine-profile PGN families. | J1939 PGN + SA/DA + profile hints |
| NMEA 2000 + J1939 | Sniffs both configured SocketCAN interfaces at the same time on dual-bus installations. | One timestamped capture across both buses |
| Mercury SmartCraft | Experimental passive labeling for SmartCraft/proprietary CAN. Payload semantics remain raw until verified. | CAN ID + raw bytes + timing for reverse mapping |
| Raw CAN | No protocol assumption; preserves standard/extended IDs and payloads exactly as received. | CAN ID, DLC, payload, FD/classic flag, interface, timestamp |
Default capture: /var/log/keelos-can-sniffer.ndjson · rotates at 25 MB · journal summary every 30 seconds · configurable with sniffer_max_mb and sniffer_summary.
The serial bridge accepts standard NMEA 0183 talker IDs, validates a checksum when one is present, and publishes only fields with a direct NMEA 2000 equivalent. Default is 4800 baud 8N1; 38400 and supported custom rates can be selected.
| NMEA 0183 sentence | Data converted | NMEA 2000 output |
|---|---|---|
| RMC | Latitude/longitude · COG · SOG | 129025 · 129026 |
| GGA · GLL | Latitude/longitude | 129025 · Position, Rapid Update |
| VTG | Course over ground · speed over ground | 129026 · COG & SOG, Rapid Update |
| HDT · HDM · HDG | True/magnetic heading · deviation/variation when supplied | 127250 · Vessel Heading |
| VHW | Vessel heading · speed through water | 127250 · 128259 |
| DPT · DBT | Depth below transducer · DPT offset when present | 128267 · Water Depth |
| MWV | Apparent or true boat-referenced wind angle/speed | 130306 · Wind Data |
| MTW | Sea water temperature | 130312 · Temperature |
RS-422 mode selector: choose nmea0183, mtu, both, or none. With a Waveshare USB TO 4CH RS485/422 and BOTH selected, automatic channel selection uses Port A for NMEA 0183 and Port B for MTU so the two talkers never contend for one serial channel.
| NMEA 2000 PGN | Message | Keelas.sh behavior |
|---|---|---|
| 65284 | DC Relay Current | Configured only from an explicit verified relay/bank current — ordinary battery current is not substituted. |
| 65286 | Fluid Flow Rate | Automatically derived from standard N2K 127489 fuel-rate data when proprietary output is enabled. |
| 65287 | Trip Volume | Automatically derived from standard N2K 127497 trip-fuel data. |
| 130823 | Temperature, High Range | Automatically published from matching exhaust-temperature data in standard 130312 / 130316. |
| 130840 | Generic Sensor | Exact verified raw/data-format configuration only. No arbitrary sensor payload is invented. |
Catalog-known proprietary families 126720, 128720, 130817–130822, 130824–130826, 130828–130830, 130832–130837 are known to the publisher, but command/configuration messages are not synthesized. Verified passive/status raw payloads are accepted only through the explicit allow-list.
| NMEA 2000 PGN | Mapped MTU data | Status |
|---|---|---|
| 127488 | Engine speed · charge/boost air pressure | MTU → N2K |
| 127489 | Lube-oil pressure/temp · coolant temp/pressure · fuel pressure · engine hours · engine alarms | MTU → N2K |
| 127493 | Ahead/neutral/astern · gear lube-oil pressure · gear temperature · transmission alarms | MTU → N2K |
| 130316 | Exhaust temperature bank A/B · extended range | MTU → N2K |
Transport status: the ECS-5 point, scaling, gear and alarm maps are present, but live semantic framing remains capture-safe until the MTU E 531 652 RS-422 framing/checksum/encoding specification is supplied. The recommended adapter path is the Waveshare USB TO 4CH RS485/422 using RS-422-capable Ports A/B.
The Pi 4/5-hardened build is live for testing. Under active development — dry-run it first, keep backups, and don't make it the only instrumentation between you and the water.
curl -fsSL "https://keelas.sh/?dl=Keelas.sh&raw=1" -o Keelas.sh && chmod +x Keelas.sh && sudo ./Keelas.sh --dry-run
echo "33b8fafa429dd86235a970641888e83895de84de07db7fc5ec0d2eb436314275 Keelas.sh" | sha256sum -c
Beta means Beta: the script asks before touching anything, records every change to a manifest, and --uninstall reverses it. Start with --dry-run, verify the checksum, read the script — it's one file on purpose.
What changed, when, and what it means at the helm. Every release below stays downloadable and checksummed.
/var/log/keelos-can-sniffer.ndjson with timestamp, interface, CAN ID, DLC, raw payload and classification. Default rotation is 25 MB with periodic top-PGN/source/protocol summaries.nmea0183, mtu, both, or none. On the Waveshare USB TO 4CH RS485/422, BOTH mode automatically separates standard NMEA 0183 onto Port A and MTU onto Port B when both channels are left on auto. Generic adapters require explicit different device paths when both are active.Keelas.sh. Download, curl, checksum, dry-run, normal install, and uninstall examples on the site all use the same filename users receive./dev/serial/by-id paths, and a generic-adapter mode remains for anything else.--detect now reports discovered Waveshare 4CH RS-422 channels; --list-mappings emits a JSON report covering the gateway, both Monitoring profiles' block/point counts, the mapped NMEA 2000 PGNs, and the framing status (E 531 652 still required — raw byte boundaries remain unguessed).mtu_rs422_adapter (waveshare4ch/generic) and mtu_rs422_channel (auto/A/B) with strict validation.127488.engine_speed).--detect now lists RS-422 candidate adapters; runtime auto-detection retries at service start and excludes ports already claimed by J1708.chromium via apt candidate lookup (current Pi OS), falling back to chromium-browser on older images. Answer files gained seven mtu_rs422_* keys with strict validation.SHA-256 936aa9078c63528f49b55b2cf0b095dbede77d7e882803eff4d7d925a6906b41
0.MAYBE → 0.BETA across the script and site — same confidence level, more honest label./usr/local/lib/keelos/j1587_n2k.py. Read-only on the 9600 bit/s bus; translates engine, transmission, fuel, and electrical MID/PID data plus standard diagnostic alarms (PID 5/6/44/45/71/97/194 with FMI) into PGN 127489/127493 status bits. Self-tests (py_compile, --self-test, --list-mappings) run at install; the port is chosen in the wizard or auto-detected./opt/keelos — only when the system Node is below 22. Signal K preflight now proves native socketcan + @canboat/canboatjs actually load before anything is enabled; NMEA 2000 decoding uses the canboatjs PGN database.keelos-can.service raises every interface with per-role bitrates (and vcans) in strict order — before the translators, before the dashboard. Answer files gained the new keys; legacy proto: files still load.SHA-256 a338c304e254972e69121295dd93eab96b7dbb08b69e785af1eacda1be858c08
--hat-guide covering the Waveshare CAN boards by PCB name, chip marking, and oscillator.kplex dropped in favor of gpsd + Signal K.--dry-run / --uninstall / --detect, flock on the tiller.Everything below is what the current Keelas.sh build actually configures — same options and caveats as its built-in --hat-guide. Hardened for Raspberry Pi 4/5; anything else runs with a warning.
| CAN / serial interface | What the script configures | Status | |
|---|---|---|---|
| • | Waveshare RS485 CAN HAT (12 MHz) | MCP2515 · SPI0 · GPIO25 · can0 + UART | Auto-config |
| • | Waveshare RS485 CAN HAT (legacy 8 MHz) | MCP2515 · 8 MHz osc · can0 + UART | Auto-config |
| • | Waveshare 2-CH CAN FD HAT Rev2.1+ | MCP2518FD ×2 · Mode A · can0 + can1 | Auto-config Pi 5: best-effort — not on Waveshare's list |
| • | Waveshare 2-CH CAN HAT+ ← Pi 5 pick | MCP2515 ×2 · 16 MHz · SPI1 CS1/CS2 · can0 + can1 | Auto-config |
| • | Waveshare 2-CH CAN HAT (non-Plus) | MCP2515 ×2 · 16 MHz · can0 + can1 | Auto-config Documented through Pi 4B only |
| • | PiCAN-M | MCP2515 · 16 MHz · can0 · pulls in the 0183 stack | Auto-config |
| • | USB SocketCAN adapter | gs_usb / candleLight · no overlay needed · can0 · can also be dedicated to passive SmartCraft/raw sniffing | Auto-config |
| • | No CAN hardware | Serial translation can still feed a private vcan / Signal K path; a physical NMEA 2000 output requires a CAN interface. | Auto-config |
| • | Waveshare USB TO 4CH RS485/422 (FT4232HL) | NMEA 0183 / MTU RS-422 gateway · Ports A/B only for RS-422 · BOTH mode can dedicate A=0183, B=MTU · stable by-id paths | Auto-config |
| • | J1587/J1708 legacy diesel | Bundled read-only translator → NMEA 2000 (data + diagnostic alarms) · port auto-detect | Auto-config |
| • | Standard NMEA 0183 via RS-422 | RMC/GGA/GLL/VTG · heading · water speed · depth · wind · sea temperature → NMEA 2000 PGNs | Auto-config |
| • | MTU Series 2000 (ECS-5) via RS-422 | Monitoring I/II point + alarm maps → NMEA 2000 · Waveshare 4CH gateway (Ports A/B) or generic RS-422 adapter · mandatory baud | Auto-config Capture-safe: E 531 652 serial framing pending |
| • | NMEA 2000 ⇄ J1939 conversion | Bidirectional translator for matched PGNs · vcan auto-created when only one physical bus exists | Auto-config |
| • | Passive CAN protocol sniffer | NMEA 2000 · J1939 · both · SmartCraft candidate · raw CAN · multi-interface capture · no CAN transmission | Auto-config |
| • | Pi Zero 2 W, Orange Pi, other SBCs | Script runs but warns: hardened for Pi 4/5 only | Warns |
Status legend: AUTO-CONFIG = the wizard writes overlays, network config, translators, and services end-to-end · WARNS = proceeds at your own risk. Tip from the script itself: read the exact PCB name, chip marking (MCP2515 vs MCP2518FD), and oscillator before choosing — not the connector shape.