Skip to main content

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 retries once a second — appropriate for a boat bus that may power-cycle. Retrying never blocks the gateway: the TCP port stays open and clients can connect (and just see no data) while can0 is missing, including at boot when n2k2ip starts before the interface is configured. The outage is logged once, not once per retry.
  • Stalled clients are dropped. A client with data queued that accepts no bytes for CLIENT_STALL_SEC (30 s) is disconnected. This catches the half-open case — Wi-Fi vanishes and the peer never sends a FIN — which bare TCP only notices after many minutes of retransmits. SO_KEEPALIVE is set as a second line of defence.
  • Client limit. At most MAX_CLIENTS (32) connections at once; further ones are refused with a log line rather than being allowed to exhaust file descriptors.
  • 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 _reopen_can if you need to restrict PGNs.
  • No access control. The port is served on all interfaces with no authentication — anyone who can reach it can read your bus. Fine on a private boat network; firewall it if that network is shared.

License

MIT

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.1.tar.gz (10.4 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.1-py3-none-any.whl (10.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: n2k2ip-0.1.1.tar.gz
  • Upload date:
  • Size: 10.4 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.1.tar.gz
Algorithm Hash digest
SHA256 efdda34b836db321f7984716b0ad9981fd3a7968f0b041d675916a92805eaaaf
MD5 432a8bc2cdbebe78594df9a661295fce
BLAKE2b-256 d84ad3c93bae473cee86b6c0b20bb51172d58b07cf18f95d65c2c4d36d039790

See more details on using hashes here.

File details

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

File metadata

  • Download URL: n2k2ip-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 10.8 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0c53aac57546f46a63fc661bf6a9d0833a35fc2f90613e1697a23188579eebd2
MD5 78c4cb1174e1e5a3379ef3450f27e6a5
BLAKE2b-256 ecf2bcf85c3a618ff117a52e9fbb97e5b47a2e2c69bae6434113fb67279056b5

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page