Skip to main content

Bridge an NMEA 2000 (SocketCAN) bus to TCP clients as Yacht Devices RAW (YDRAW)

Project description

n2k2ip

Bridge an NMEA 2000 (SocketCAN) bus onto the network: read raw CAN frames from a SocketCAN interface and stream them to TCP clients as Yacht Devices RAW (YDRAW) text — one line per CAN frame. A software equivalent of a Yacht Devices Wi-Fi gateway (YDWG-02) in RAW mode.

NMEA2000 bus ──(can0)──▶ n2k2ip ──(tcp/1457)──▶ many clients
                                                 (plotters, loggers, OpenCPN, …)

Why it exists / design

The data is live, and stale data is worthless. Everything here serves that:

  • Drop-old, never back up. Each client has a bounded outbound queue. When a client can't keep up (a slow or stalled Wi-Fi link), the oldest whole lines are dropped so it always receives recent frames instead of a growing backlog. Maximum staleness is bounded by the MAX_QUEUE constant (100 lines) in n2k2ip.py.
  • One slow client can't hurt the others. All writes are non-blocking; a stalled or dead client never blocks the CAN reader or the other clients.
  • Low latency by default. TCP_NODELAY is set on every client, so each line hits the wire immediately (no Nagle batching). Over Wi-Fi this matters — measured median latency for this kind of small-message stream is a few ms with NODELAY vs tens of ms without.
  • Lightweight. Single-threaded, event-driven (selectors) — no threads, no busy-poll, no dependencies. Pure Python standard library.

Requirements

  • Linux with SocketCAN (a can0-style interface)
  • Python 3.9+no third-party packages (stdlib only)
  • A synchronised host clock — YDRAW timestamps are taken from the host's UTC clock, so make sure NTP is running and disciplined (e.g. systemd-timesyncd or chrony). On a headless/boat box without a network, fit an RTC or expect the clock — and therefore the timestamps — to be wrong until time is acquired.

Install

On Raspberry Pi OS (and other Debian 12+ systems) the system Python is "externally managed", so use pipx — it drops the n2k2ip command on your PATH in its own isolated environment and sidesteps --break-system-packages:

sudo apt install pipx        # once, if you don't have it
pipx install n2k2ip

There are no third-party dependencies, so plain pip install n2k2ip also works if you'd rather (add --break-system-packages, or install into a virtualenv).

Because n2k2ip is a single stdlib-only file, you can also skip installing entirely and run it straight from a checkout:

python3 n2k2ip.py --channel can0 --port 1457

Quick start

# bring the CAN interface up at the NMEA2000 bitrate
sudo ip link set can0 up type can bitrate 250000

# serve YDRAW on tcp/1457
n2k2ip --channel can0 --port 1457

Connect and watch:

nc <host> 1457
# 09:46:57.556 R 0DF50B16 00 E6 05 00 00 FF 7F FF
# 09:46:57.556 R 00FA8C3E 4C 08 C2 24 7E E8 61 4B
# ...

Usage

n2k2ip [--channel can0] [--port 1457] [--log-level INFO]

(or python3 n2k2ip.py … when running from a checkout)

option default meaning
--channel can0 SocketCAN interface to read
--port 1457 TCP port to serve YDRAW on
--log-level INFO DEBUG, INFO, WARNING, ERROR

Per-client buffer depth is the MAX_QUEUE constant in n2k2ip.py (100 lines). When a client falls behind, the oldest lines are dropped so it never lags more than ~100 frames — roughly 0.2–0.5 s of backlog on a busy bus. Raise it only if you'd rather tolerate longer stalls than skip frames.

Output format (YDRAW)

Each line is one CAN frame:

HH:MM:SS.mmm R <8-hex CAN-ID> <space-separated hex data bytes>

Lines are terminated with \r\n. Direction is always R (received). The timestamp is the host clock in UTC (HH:MM:SS.mmm). The YDRAW spec calls for UTC sourced from the NMEA 2000 bus when available; we don't decode the bus, so we use the host's UTC clock instead — accurate as long as the host clock is synced (e.g. NTP).

Multi-frame (fast-packet) messages are passed through, not reassembled — and that is correct for YDRAW, which is a per-CAN-frame format. Each fast-packet frame is sent as its own line with its sequence/frame-counter byte intact; reassembling them into a logical PGN is the consumer's job (e.g. canboat analyzer, OpenCPN, a YDRAW decoder). Over a lossy link this means a lost frame loses its whole fast-packet PGN — inherent to the format, same as a hardware gateway. For that reason TCP is preferred over UDP/broadcast on Wi-Fi, where reliability and low jitter matter most.

Run as a service (systemd)

Install it globally with pipx, then drop in the shipped unit:

sudo apt install pipx                 # once, if you don't have it
sudo pipx install --global n2k2ip     # -> /usr/local/bin/n2k2ip
sudo cp n2k2ip.service /etc/systemd/system/
# edit the unit if your channel/port differ, or if can0 is brought up elsewhere
sudo systemctl daemon-reload
sudo systemctl enable --now n2k2ip.service
journalctl -u n2k2ip.service -f

Update later with sudo pipx upgrade --global n2k2ip. If you'd rather not pip-install at all, the single stdlib-only file can just be copied and run directly — see the comment header in n2k2ip.service.

Behaviour notes

  • CAN reconnect. If the interface goes down or the controller goes bus-off, the reader logs it and reopens the socket with backoff — appropriate for a boat bus that may power-cycle.
  • Error/remote frames are skipped; only data frames are forwarded.
  • No filtering. Every data frame on the bus is forwarded. Add a SocketCAN filter in _open_can if you need to restrict PGNs.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

n2k2ip-0.1.0.tar.gz (8.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

n2k2ip-0.1.0-py3-none-any.whl (8.7 kB view details)

Uploaded Python 3

File details

Details for the file n2k2ip-0.1.0.tar.gz.

File metadata

  • Download URL: n2k2ip-0.1.0.tar.gz
  • Upload date:
  • Size: 8.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for n2k2ip-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1c0ddb8cdaf28793bd0ab5ac7ec6f832d12cfea6d5ebf302c79c978c95e722b4
MD5 80df1eb511f9f361a4035f34bcd3e880
BLAKE2b-256 18d9044323cb8e393b1b16c10aaba1238d177b7c6765bcf171da843fb1eaff94

See more details on using hashes here.

File details

Details for the file n2k2ip-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: n2k2ip-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for n2k2ip-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8e3e3da53d3afee39b0df95d0cfc8d2c7526ea80fd6ae659d93431ea103bf771
MD5 d9a5f540ba691ca9555fac3398203deb
BLAKE2b-256 676ecb2e8cfc19465614393e7b4a679f7e5409f77daea1a39238aa2deeae5197

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page