Skip to main content

fastnet2ip

Reads raw B&G Fastnet data from a serial port, decodes it with pyfastnet, and broadcasts it over UDP in your choice of format — NMEA 0183 or NMEA 2000 (over IP) — for Signal K, OpenCPN, chart plotters, and other navigation software on your network.

Output Flag Default port Use with
NMEA 0183 --output nmea0183 (default) 2002 Chart plotters, OpenCPN, most navigation software
NMEA 2000 --output nmea2000 2000 Actisense, Yacht Devices, Signal K server (via UDP)

Runs on Raspberry Pi, macOS, or Linux. Requires Python 3.10+.

Quick start

Install the CLI in its own isolated environment with pipx:

pipx install fastnet2ip

Run it against your Fastnet serial port:

fastnet2ip --serial /dev/ttyUSB0 --output nmea0183 --live-data
fastnet2ip --serial /dev/ttyUSB0 --output nmea2000 --live-data

No instruments to hand? Replay one of the bundled captures from the source repo (tests/data/):

fastnet2ip --file example1_fastnet_data.txt --output nmea0183 --live-data

--live-data prints a live channel table to the console once per second:

Example console output

The app can also be invoked as a module: python3 -m fastnet2ip ... — equivalent to the fastnet2ip command.

To upgrade later: pipx upgrade fastnet2ip.

Alternative installs (pip venv / from source)
# pip into a virtual environment
python3 -m venv ~/fastnet2ip-venv
source ~/fastnet2ip-venv/bin/activate
pip install fastnet2ip

# from source (development)
git clone https://github.com/ghotihook/fastnet2ip
cd fastnet2ip
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[test]"

The Fastnet toolkit

Three projects stack together — pick the one that matches where you want the data to end up:

Project What it does Use it when
pyfastnet Decoder library. Turns raw Fastnet bytes into Signal K paths in SI units. You're writing your own Python and want the decoded data.
fastnet2ip (this app) Serial → network. Broadcasts decoded data over UDP as NMEA 0183 or NMEA 2000 (over IP). Feeding Signal K, OpenCPN, or a plotter over WiFi / Ethernet.
fastnet2n2k Serial → physical NMEA 2000 bus. Transmits PGNs onto a CAN backbone via SocketCAN. Wiring into a real NMEA 2000 network / chartplotter.
                          ┌─ fastnet2ip   → UDP (NMEA 0183 / NMEA 2000 over IP) → Signal K, OpenCPN, plotters
B&G Fastnet bus ─(serial)─→ pyfastnet ─┤
                          └─ fastnet2n2k → SocketCAN (NMEA 2000 PGNs)           → CAN backbone, chartplotter

This app builds on pyfastnet: pyfastnet handles the protocol layer (frame sync, checksums, decoding raw bytes into named channels); fastnet2ip handles everything above that — reading the serial port, a live data store, mapping channels to output sentences/frames, rate limiting, and UDP broadcast. If you need the data on a physical NMEA 2000 CAN backbone rather than over the network, use fastnet2n2k instead.

Running as a systemd service

For an always-on bridge (e.g. a Raspberry Pi), run fastnet2ip under systemd so it starts on boot and restarts on failure.

Install it globally, not per-user. A plain pipx install goes to a user's ~/.local/bin, which a root-run service can't rely on. Use pipx install --global so the command lands in /usr/local/bin.

1. Install globally with pipx

sudo apt install pipx                        # once, if you don't have it
sudo pipx install --global fastnet2ip

This gives you /usr/local/bin/fastnet2ip, the path the unit below uses. (--global needs pipx ≥ 1.5.)

2. Create the unit file

Paste the following into /etc/systemd/system/fastnet2ip.service (e.g. sudo nano /etc/systemd/system/fastnet2ip.service). The same template ships as fastnet2ip.service in the source repository.

[Unit]
Description=fastnet2ip Service
####################################### CHANGE THIS IF PORT CHANGES
After=dev-ttyUSB0.device
BindsTo=dev-ttyUSB0.device

[Service]
Type=simple
User=root
# Uncomment ONE ExecStart line for the output mode you want:
# NMEA 2000 output:
ExecStart=/usr/local/bin/fastnet2ip --output nmea2000 --serial /dev/ttyUSB0 --udp-port 2000 --n2k-format ydwg --n2k-src 201 --n2k-pri 4 --ignore-gps --log-level INFO
# NMEA 0183 output:
#ExecStart=/usr/local/bin/fastnet2ip --output nmea0183 --serial /dev/ttyUSB0 --udp-port 2002 --log-level INFO
Restart=always
RestartSec=10

# === RESOURCE LIMITS ===
OOMScoreAdjust=-700
OOMPolicy=continue
MemoryMax=128M
MemoryHigh=96M
TimeoutStopSec=30

# === LOGGING ===
StandardOutput=journal
StandardError=journal
SyslogIdentifier=fastnet2ip
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target

Before enabling, edit it to match your setup:

  • After= / BindsTo= — your serial device, in systemd's escaped form (/dev/ttyUSB0dev-ttyUSB0.device)
  • the active ExecStart= — pick one line (NMEA 0183 or NMEA 2000) and set the correct --serial port

3. Enable and start it

sudo systemctl daemon-reload
sudo systemctl enable fastnet2ip
sudo systemctl start fastnet2ip
sudo journalctl -u fastnet2ip -f      # follow the logs

To upgrade later: sudo pipx upgrade --global fastnet2ip && sudo systemctl restart fastnet2ip.

Command-line options

Shared

Argument Default Description
--output FORMAT nmea0183 nmea0183 or nmea2000
--serial PORT Serial port (e.g. /dev/ttyUSB0)
--file PATH Path to a recorded hex file
--log-level LEVEL INFO DEBUG, INFO, WARNING, ERROR
--live-data off Print live channel table to console once per second
--ignore-gps off Suppress GPS channels — see below
--ignore-heading off Suppress heading channels — see below
--host ADDR 255.255.255.255 UDP destination host
--udp-port N 2002 / 2000 UDP port (default depends on output mode)

NMEA 2000 (--output nmea2000)

Argument Default Description
--n2k-src N 201 N2K source address 0–253 (accepts hex: 0xC9)
--n2k-pri N 4 Message priority 0 (highest) – 7 (lowest)
--n2k-format FMT ydwg Wire format: ydwg or pcdin (see below)

Avoiding feedback loops: --ignore-gps and --ignore-heading

Most B&G systems receive GPS (and sometimes heading) from an external source and pass it through onto the Fastnet bus alongside the instrument data. If you also connect that same source directly to your network, re-broadcasting it from this bridge creates a feedback loop — downstream software sees the same data arriving twice, which can cause jumps, conflicts, or incorrect averaging.

Use --ignore-gps and/or --ignore-heading when that source is already on your network. Both flags work with --output nmea0183 and --output nmea2000. If the bridge is the only source of that data on your network, omit them.

--ignore-gps suppresses:

Fastnet channel NMEA 0183 NMEA 2000
LatLon GLL PGN 129025
Speed Over Ground VTG PGN 129026
Course Over Ground (True) VTG PGN 129026
Course Over Ground (Mag) VTG PGN 129026

--ignore-heading suppresses:

Fastnet channel NMEA 0183 NMEA 2000
Heading HDM / HDT PGN 127250
Heading (Raw) PGN 65281

Hardware

Fastnet uses two-wire differential transmission. RS-485 adapters work well; the CAN Hat option includes 120 Ω termination which is recommended.

Tested hardware

Wiring

Fastnet wire RS-485
White Data +
Green Data -

Serial settings: 28,800 baud, 8 data bits, odd parity, 2 stop bits

Raspberry Pi built-in UART (/dev/ttyAMA*): the first open of a Pi's built-in PL011 UART after a boot can leave the hardware at 9600 even though 28,800 was requested — 28,800 is non-standard, so it takes the kernel's BOTHER path, which has a known first-open quirk (bytes arrive but nothing decodes; a second run "fixes" it). fastnet2ip works around it automatically when opening the port. USB RS-485 adapters (e.g. the DTECH dongle) are not affected. Full diagnosis: docs/uart_first_open_baud_fix.md.

Output reference

NMEA 0183

Sentence Content
VHW Boatspeed; heading in True or Magnetic field per instrument configuration
DBT Depth below transducer
RSA Rudder angle
HDM Magnetic heading (emitted when instrument is configured for magnetic reference)
HDT True heading (emitted when instrument is configured for true reference)
MWD True wind direction and speed; direction True or Magnetic per instrument configuration
MWV True wind angle/speed (ref T)
MWV Apparent wind angle/speed (ref R)
MDA Air temp, sea temp, barometric pressure
VTG COG and SOG
VPW Velocity made good
VDR Tidal set and drift; set direction True or Magnetic per instrument configuration
GLL Latitude/Longitude

True vs Magnetic: The Fastnet data stream carries no magnetic variation or deviation. Whether a channel is labelled True or Magnetic reflects the reference configured in the B&G instrument — not a computed conversion. VHW, HDM/HDT, MWD, and VDR each output whichever reference the instrument is set to.

XDR transducers:

XDR name Content
BATTV Battery voltage
ROLL Heel angle (degrees)
PITCH Fore/aft trim (degrees)
RAW_WIND_A Apparent wind angle raw sensor value
RAW_WIND_S Apparent wind speed raw sensor value
RAW_BSP Boatspeed raw sensor value

NMEA 2000

PGN Name
127245 Rudder
127250 Vessel Heading
127251 Rate of Turn
127257 Attitude (heel + trim)
127508 Battery Status
128000 Leeway
128259 Boat Speed
128267 Water Depth
128275 Distance Log
129025 Position
129026 COG & SOG
129283 Cross Track Error
129291 Set & Drift
130306 Wind Data (apparent, true boat-ref, true ground-ref)
130312 Temperature (sea + air)
130314 Pressure
65280 Proprietary: raw wind speed, wind angle
65281 Proprietary: raw heading
65282 Proprietary: raw boatspeed

Wire formats (--n2k-format):

  • ydwg — Yacht Devices RAW UDP: HH:MM:SS.mmm R XXXXXXXX DD DD DD...
  • pcdin — PCDIN sentences for Signal K server UDP input: $PCDIN,PPPPPP,TTTTTTTT,SS,DDDD...*CC

How it works

Serial port / hex file
        │
        ▼
  pyfastnet FrameBuffer     ← byte sync, checksum, channel decoding
        │
        ▼
  Live data store           ← latest value per channel
        │
        ▼
  Output handler            ← nmea0183 or nmea2000, selected by --output
        │
        ▼
  UDP broadcast

A message is sent on every channel update — a repeated value is still live data worth putting on the wire — rate-capped at 20 Hz per channel so a fast path can't flood UDP. There is no dedupe and no periodic re-broadcast: the bridge reflects what the instruments provide, and when a source goes quiet its output stops (consumers time it out themselves). This matches the sibling fastnet2n2k bridge's cadence.

To add a new output format, implement OutputHandler in fastnet2ip/handlers/ and add it to _HANDLERS in fastnet2ip/__main__.py.

Bench testing tools

Two helper scripts for capturing and replaying Fastnet data live in the tools/ directory of the source repository (they are not installed by pip/pipx — clone the repo to use them):

# record raw Fastnet data to file
python3 tools/record_fn.py --port /dev/ttyUSB0 --output my_capture.txt

# play a recording back to a serial port
python3 tools/playback_fn.py --port /dev/ttyUSB1 --input my_capture.txt

# play back at 4x wire speed
python3 tools/playback_fn.py --port /dev/ttyUSB1 --input my_capture.txt --speed 4

Playback is paced at the wire speed implied by the baud rate and framing (28800 baud, 8O2 = 12 bits/byte = 2400 bytes/s), so a capture takes about as long to replay as it did to record while the bus was active. Recordings carry no timestamps, so idle gaps on the original bus are not reproduced — a replay is therefore shorter than the original recording session, and is not a faithful reconstruction of real-world message timing. --speed scales the rate; values above 1.0 are capped by the hardware on a real serial port, but do take effect on a virtual port (pty/socat).

Recordings can also be replayed through the main app with fastnet2ip --file my_capture.txt. Note this uses a different, fixed pacing (256-byte chunks every 50 ms ≈ 5120 B/s) and is not wire-speed accurate.

Development

git clone https://github.com/ghotihook/fastnet2ip
cd fastnet2ip
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[test]"
pytest

Build distributable artifacts:

pip install build
python -m build        # writes sdist + wheel to dist/

Releasing to PyPI

The version lives in one place — __version__ in fastnet2ip/__init__.py — which pyproject.toml reads automatically.

  1. Bump the version in fastnet2ip/__init__.py per semver (patch = bug fix, minor = new backward-compatible flag, major = breaking change).
  2. Rebuild clean and validate (stale dist/ files would otherwise be uploaded):
    rm -rf dist build *.egg-info
    python -m build
    twine check dist/*
    
  3. Tag and upload:
    git commit -am "Release X.Y.Z"
    git tag vX.Y.Z
    git push origin main --tags
    twine upload dist/*          # needs a PyPI account + API token in ~/.pypirc
    
  4. Users upgrade with pipx upgrade fastnet2ip (or pip install --upgrade fastnet2ip).

PyPI never lets you re-upload an existing version — even a deleted or "yanked" one. If a release has a bug, bump to the next patch version and re-publish; you can never overwrite a number that's already live.

Acknowledgments

  • trlafleur — collected significant background research
  • Oppedijk — protocol background
  • timmathews — substantial C++ implementation

License

MIT — see LICENSE.

Download files

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

Source Distribution

fastnet2ip-3.2.0.tar.gz (33.4 kB view details)

Uploaded Source

Built Distribution

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

fastnet2ip-3.2.0-py3-none-any.whl (25.1 kB view details)

Uploaded Python 3

File details

Details for the file fastnet2ip-3.2.0.tar.gz.

File metadata

  • Download URL: fastnet2ip-3.2.0.tar.gz
  • Upload date:
  • Size: 33.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for fastnet2ip-3.2.0.tar.gz
Algorithm Hash digest
SHA256 dfe0f2c31665f941d67bddb4749a634fc25f2afa8f23603ddcbf446188c4189a
MD5 7cc2ae2fdb2bd54d1ff1a403de754039
BLAKE2b-256 c06995a4859e98109315b2ce0f2758296c5c7831bbdd2f8dd0e692a693fc3c55

See more details on using hashes here.

File details

Details for the file fastnet2ip-3.2.0-py3-none-any.whl.

File metadata

  • Download URL: fastnet2ip-3.2.0-py3-none-any.whl
  • Upload date:
  • Size: 25.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for fastnet2ip-3.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ad381cfa1a80f6f354de778f20416e300050842bcb6f00ee43c7a80256db1ee0
MD5 6f71caae3f012404050d033b144d3f47
BLAKE2b-256 7c7982eb1c6cf2c873faf922c47244467c7b9dfacc4c25ebda5ff0a58e728b62

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

3.2.0 This release

2 files

3.1.0

2 files

3.0.0

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

1.0.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