Seri Protokoller
TEKNİK REHBER SERİ PROTOKOLLER NMEA 2000 2026

NMEA 2000
Deniz Elektroniği CAN Standardı

GPS, pusula, makine verileri — IEC 61162-3 temelli NMEA 2000 ağını Linux ve Raspberry Pi ile izle, CANBOAT ile analiz et, OpenCPN'e aktar.

00 NMEA 2000 nedir: deniz elektroniği standardı

NMEA 2000 (N2K), deniz elektroniği cihazlarının tek bir kablo sistemi üzerinde haberleşmesini sağlayan standarttır. IEC 61162-3 olarak da bilinir ve SAE J1939 temeli üzerine inşa edilmiştir.

Genel özellikler

Temel standartIEC 61162-3 / NMEA 2000 (NMEA tarafından yönetilir). J1939'dan türetilmiş; 250 kbit/s, 29-bit CAN ID, aynı adres claiming mekanizması.
Cihaz ekosistemiChartplotter (Garmin, Simrad, Raymarine), GPS alıcısı, VHF telsiz, radar, AIS transponder, motor monitörü, rüzgar sensörü, fener kontrolü — hepsi tek N2K bus'ında.
SertifikasyonNMEA 2000 sertifikası için ürünler NMEA test laboratuvarından geçmelidir. Sertifikalı ürünler "NMEA 2000 Certified" logosu taşır. Ağ geçitleri için "NMEA 2000 Certified" gerekmez; PGN decode araçları için lisans gerekmez.
Bus gücüN2K backbone 12V DC güç taşır. Cihazlar backbone'dan beslenir; ayrı güç kablosu gerekmez. Azami bus akımı 3A (LEN birimi ile sınırlandırılır).

NMEA 0183 ile fark

ÖzellikNMEA 0183NMEA 2000
TopolojiPoint-to-point RS-422Multi-drop bus (CAN)
Hız4800 / 38400 baud250 kbit/s
Cihaz sayısı1 verici, çok alıcı50+ cihaz (LEN sınırı)
FormatASCII metin ($GPRMC,...)Binary PGN
Güç hattıYokBus'ta 12V
Çift yönHayırEvet
LEN — Load Equivalency Number

Her N2K cihazının belirtilen bir LEN değeri vardır. LEN, cihazın bus'tan çektiği akımın göstergesidir (1 LEN = 1mA nominal). Tek bir backbone segmentindeki toplam LEN değeri 50'yi aşmamalıdır.

01 Fiziksel katman: kablo, terminatör, güç

NMEA 2000, DeviceNet kablo standardını (IEC 62026-3) kullanır. 5 iletkenli özel kablo, terminatörler ve T-konnektörler belirli boyutlarda üretilir.

Kablo renk kodu (DeviceNet)

  İletken   Renk     İşlev
  ────────────────────────────────
  1         Kırmızı  +12V (Bus güç)
  2         Siyah    GND
  3         Beyaz    CAN_H
  4         Mavi     CAN_L
  5         Dray     CAN Shield (toprak)
    

Kablo boyutları

Kablo TipiGüç iletkeniCAN iletkeniMaks. uzunluk
Mini (trunk)0.5 mm²0.35 mm²Backbone 100m, Drop 6m
Micro (drop)0.35 mm²0.13 mm²Sadece drop, maks. 1m
Mid (trunk)1.0 mm²0.35 mm²Backbone 100m, Drop 6m

Topoloji ve uzunluk limitleri

  [Terminatör] ──── Backbone ──── [T] ──── Backbone ──── [Terminatör]
                                   │
                                 Drop (maks. 6m)
                                   │
                              Cihaz (chartplotter, GPS vb.)

  Backbone maks.: 100m (toplam)
  Drop maks.:     6m (Mini/Mid kablo)
  Drop maks.:     1m (Micro kablo)
  Cihaz sayısı:   50+ LEN birimi (pratik: 20-30 cihaz)
    

Terminatörler

Backbone'un her iki ucuna 120Ω terminatör takılmalıdır. Aktif terminatörler (120Ω + dahili güç) bazı sistemlerde backbone güç dağıtımını düzenler.

UYARI

İki terminatör yoksa veya yanlış konumdaysa bus düzgün çalışmaz. Terminatör durumunu kontrol etmek için CANBOAT veya Actisense NMEA Reader gibi araçlarla "Bus diagnostics" bakın. Multimetreyle CAN_H — CAN_L arasındaki DC direnç ~60Ω olmalıdır.

02 PGN yapısı ve Fast Packet protokolü

NMEA 2000, J1939'dan türetilmiş PGN yapısını kullanır. 8 byte'tan büyük veriler için özel Fast Packet protokolü tanımlanmıştır; bu J1939 TP'dan farklıdır.

29-bit CAN ID yapısı (J1939 ile aynı)

  Bit 28-26: Priority (0-7)
  Bit 25:    Reserved
  Bit 24:    Data Page (DP)
  Bit 23-16: PDU Format (PF)
  Bit 15-8:  PDU Specific veya Group Extension (PS/GE)
  Bit 7-0:   Source Address (SA)

  PGN = DP(1) | PF(8) | PS/GE(8) — PDU2 için
  PDU1 (PF < 0xF0): hedefli, PS = Destination Address
  PDU2 (PF ≥ 0xF0): broadcast, PS = Group Extension
    

Fast Packet protokolü

8 byte'tan büyük N2K mesajları (örneğin 32 byte GNSS pozisyon verisi) Fast Packet ile gönderilir. Fast Packet, J1939 TP'dan farklı, N2K'ya özgü segmentasyon yöntemidir:

  Frame 1 (First Frame):
  [Seq+Frame#][Total bytes][data bytes 0-5]  = 6 byte veri
  Byte 0: bits 7-5 = Sequence Counter (0-7)
          bits 4-0 = Frame Counter (0 = first frame)

  Frame 2-N (Subsequent Frames):
  [Seq+Frame#][data bytes 0-6]  = 7 byte veri
  Byte 0: bits 7-5 = same Sequence Counter
          bits 4-0 = Frame Counter (1, 2, 3...)

  Sequence Counter: 0-7 arası döngüsel, aynı PGN'nin farklı mesajlarını ayırt eder
    
fast_packet_parse.py
from typing import Optional

class FastPacketAssembler:
    """NMEA 2000 Fast Packet frame'lerini birleştirir"""

    def __init__(self):
        self.buffers = {}   # (pgn, sa, seq) → {'total': N, 'data': bytearray}

    def feed(self, pgn: int, sa: int, raw: bytes) -> Optional[bytes]:
        header   = raw[0]
        seq_cnt  = (header >> 5) & 0x07
        frame_no = header & 0x1F
        key = (pgn, sa, seq_cnt)

        if frame_no == 0:  # First frame
            total = raw[1]
            data  = bytearray(raw[2:])
            self.buffers[key] = {'total': total, 'data': data}
            return None
        else:               # Subsequent frame
            if key not in self.buffers:
                return None  # first frame kaçırıldı
            buf = self.buffers[key]
            buf['data'].extend(raw[1:])

            if len(buf['data']) >= buf['total']:
                complete = bytes(buf['data'][:buf['total']])
                del self.buffers[key]
                return complete
            return None

03 Önemli NMEA 2000 PGN'leri

NMEA 2000, deniz uygulamaları için yüzlerce PGN tanımlar. En yaygın kullanılanlar konum, yönelim ve makine verileridir.

PGNİsimVeriPeriyot
127250Vessel HeadingYön (radyan), referans (Mag/True), sapma100 ms
127251Rate of TurnDönüş hızı (rad/s)100 ms
127245RudderDümen açısı100 ms
127488Engine Parameters RapidRPM, boost pressure, tilt100 ms
127489Engine Parameters DynamicYağ basıncı, su sıcaklığı, alternatör voltajı500 ms
128259SpeedTekne hızı (su / yer)100 ms
128267Water DepthSu derinliği, offset, menzil1000 ms
129025Position Rapid UpdateEnlem, boylam (1/1e7 derece)100 ms
129026COG SOG Rapid UpdateRota (COG) ve zemin hızı (SOG)100 ms
129029GNSS Position DataTam GPS verisi (zaman, konum, yükseklik, DOP)1000 ms
130306Wind DataRüzgar hızı, açısı (app/true)100 ms
130311Environmental ParametersSu sıcaklığı, hava sıcaklığı, basınç500 ms
129794AIS Class A Static DataMMSI, IMO, tekne adı, boyutEvent
129038AIS Class A PositionMMSI, konum, COG, SOG, rotEvent

PGN 127250 (Vessel Heading) parse

pgn127250_parse.py
import struct, math

def parse_vessel_heading(data: bytes) -> dict:
    """PGN 127250 — 8 byte"""
    if len(data) < 8:
        return {}

    sid        = data[0]
    heading_raw = struct.unpack_from('<H', data, 1)[0]
    deviation  = struct.unpack_from('<h', data, 3)[0]  # signed
    variation  = struct.unpack_from('<h', data, 5)[0]  # signed
    reference  = (data[7] >> 6) & 0x03  # 0=mag, 1=true

    def rad_to_deg(raw, scale=1e-4):
        if raw == 0xFFFF or raw == 0x7FFF:
            return None
        return math.degrees(raw * scale)

    return {
        'sid': sid,
        'heading_deg': rad_to_deg(heading_raw),
        'deviation_deg': rad_to_deg(deviation) if deviation != -32768 else None,
        'variation_deg': rad_to_deg(variation) if variation != -32768 else None,
        'reference': ['Magnetic', 'True', 'Error', 'Null'][reference],
    }

# Test
sample = bytes([0x01, 0x2E, 0x04, 0xFF, 0x7F, 0xFF, 0x7F, 0x40])
result = parse_vessel_heading(sample)
print(ff"Yön: {result['heading_deg']:.1f}° {result['reference']}")

PGN 129025 (Position Rapid Update) parse

pgn129025_parse.py
import struct

def parse_position_rapid(data: bytes) -> dict:
    """PGN 129025 — 8 byte: lat + lon"""
    if len(data) < 8:
        return {}
    lat_raw = struct.unpack_from('<i', data, 0)[0]  # signed 32-bit
    lon_raw = struct.unpack_from('<i', data, 4)[0]

    # Çözünürlük: 1e-7 derece / bit
    lat = lat_raw * 1e-7
    lon = lon_raw * 1e-7

    return {'lat': lat, 'lon': lon}

# 41.0082° N, 28.9784° E (İstanbul)
lat_raw = int(41.0082 * 1e7)
lon_raw = int(28.9784 * 1e7)
sample = struct.pack('<ii', lat_raw, lon_raw)
pos = parse_position_rapid(sample)
print(ff"Konum: {pos['lat']:.4f}°N, {pos['lon']:.4f}°E")

04 ISO 11783 address claiming

NMEA 2000 adres yönetimi, J1939/81 ile birebir uyumludur. Aynı NAME ve address claiming mekanizması kullanılır.

N2K NAME alanı — deniz uygulamasına göre

  Industry Group: 4 = Marine
  Vehicle System (N2K'da "Device Class"):
    0  = Not specified
    10 = System tools (PC, gateway)
    20 = Helm Navigation and Control
    25 = Sensor Communication Interface
    35 = Instrumentation / General Systems
    40 = Electrical Distribution
    50 = Propulsion Systems
    60 = Navigation Systems
    70 = Communication Systems
    80 = Autopilot Systems
    

Adres claiming Python örneği (N2K için)

n2k_address_claim.py
import struct, socket

def build_n2k_name(
    manufacturer_code: int,   # 11-bit (0x1ED = Simrad, 0x069 = Garmin...)
    identity_number: int,     # 21-bit (cihaz seri no)
    device_instance: int = 0,
    device_function: int = 60,  # 60 = Navigation (GPS)
    device_class: int = 60,     # 60 = Navigation Systems
    industry_group: int = 4,    # 4 = Marine
    arbitrary: bool = True
) -> int:
    name = (
        (int(arbitrary) << 63) |
        ((industry_group & 0x07) << 60) |
        ((device_class & 0x7F) << 49) |
        ((device_function & 0xFF) << 41) |
        ((device_instance & 0x07) << 38) |
        ((manufacturer_code & 0x7FF) << 21) |
        (identity_number & 0x1FFFFF)
    )
    return name

# Raspberry Pi gateway için NAME
name = build_n2k_name(
    manufacturer_code=0x001,  # özel/test
    identity_number=0x00001,
    device_function=10,       # System Tools
    device_class=10,
    industry_group=4          # Marine
)
print(ff"N2K NAME: 0x{name:016X}")
print(ff"Bytes: {struct.pack('<Q', name).hex()}")

05 CANBOAT araçları

CANBOAT (https://github.com/canboat/canboat), NMEA 2000 verilerini decode etmek için açık kaynak araç setidir. Hem binary PGN'leri analiz eder hem de JSON çıktı üretir.

CANBOAT kurulumu

bash — canboat derleme
# Bağımlılıklar
sudo apt install build-essential git

# Kaynak kodu indir ve derle
git clone https://github.com/canboat/canboat.git
cd canboat
make
sudo make install

# Kurulu araçlar:
# analyzer — PGN decode ve JSON çıktı
# n2kd     — N2K daemon (TCP ile paylaşım)
# candump2analyzer — candump log → analyzer pipeline
# actisense-serial — Actisense NGT-1 USB adaptörü desteği

analyzer ile PGN decode

bash — analyzer kullanımı
# candump çıktısını analyzer'a pipe et
candump can0 | analyzer

# JSON formatında çıktı
candump can0 | analyzer -json
# Çıktı örneği:
# {"timestamp":"2026-04-12T10:30:00.123Z","prio":2,
#  "src":5,"dst":255,"pgn":129025,
#  "description":"Position Rapid Update",
#  "fields":{"Latitude":41.0082,"Longitude":28.9784}}

# Belirli PGN'i filtrele (bash ile)
candump can0 | analyzer -json | grep '"pgn":129025'

# candump log dosyasından analiz
candump2analyzer < candump_log.txt | analyzer -json

# Tüm bilinen PGN listesi
analyzer -explain

n2kd daemon

bash — n2kd TCP sunucu
# n2kd: N2K verilerini TCP üzerinden paylaş
# can0 → TCP 2597 (analyzer JSON) + TCP 2598 (raw)
candump can0 | analyzer -json | n2kd -s 2597

# İstemci bağlantısı (başka cihazdan)
nc 192.168.1.100 2597
# JSON stream başlar

06 Linux ile NMEA 2000 okuma

Linux SocketCAN ile N2K bus'una bağlanmak için gerekli tek şey uyumlu CAN adaptörü ve CAN kernel modülleridir. Ardından candump + CANBOAT ile veri okunabilir.

Donanım adaptörleri

Actisense NGT-1En yaygın N2K USB adaptörü. Linux'ta actisense-serial (CANBOAT içinde) ile kullanılır. N2K konnektörden USB'e köprü.
Yacht Devices YDNU-02N2K USB Gateway — Linux'ta serial port olarak görünür, NMEA 0183 + N2K binary destekler.
OpenSky CANN2K backbone'a bağlanabilen DIY SPI-CAN (MCP2515) veya USB-CAN adaptörü. SocketCAN ile doğrudan kullanılır.
Canable / PEAK USBGenel amaçlı USB-CAN adaptörleri. N2K kablo sistemine standart CAN araçlarıyla bağlanmak için kullanılabilir; bare-wire bağlantı gerekir.

SocketCAN ile N2K veri okuma

bash — N2K başlangıç
# N2K 250 kbit/s
sudo ip link set can0 type can bitrate 250000 restart-ms 200
sudo ip link set can0 up

# Ham frame'leri izle (29-bit EFF → -e flag)
candump can0

# Örnek N2K çıktı:
# (1716890123.456) can0  09F80105   [8]  2E 04 FF 7F FF 7F 40 FF
#  → 0x09F80105 = Priority=2, PGN=0x1F801=129025(pos), SA=0x05

# CANBOAT pipeline ile decode
candump can0 | analyzer -json | python3 -c "
import sys, json
for line in sys.stdin:
    try:
        msg = json.loads(line)
        if msg.get('pgn') in [129025, 127250, 128259]:
            print(json.dumps(msg['fields'], indent=2))
    except: pass
"

Python ile doğrudan N2K okuma

n2k_reader.py
import socket, struct

# J1939 socket ile N2K okuma
s = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
s.bind(('can0',))

def decode_pgn_from_eff(can_id: int) -> tuple:
    """29-bit CAN ID'den PGN ve SA çıkar"""
    can_id &= 0x1FFFFFFF   # EFF flag temizle
    priority = (can_id >> 26) & 0x07
    dp  = (can_id >> 24) & 0x01
    pf  = (can_id >> 16) & 0xFF
    ps  = (can_id >>  8) & 0xFF
    sa  = (can_id >>  0) & 0xFF
    if pf < 0xF0:
        pgn = (dp << 16) | (pf << 8)
    else:
        pgn = (dp << 16) | (pf << 8) | ps
    return pgn, sa, priority

INTERESTING_PGNS = {129025, 127250, 128259, 127488, 130306}

while True:
    frame = s.recv(16)
    can_id, dlc = struct.unpack_from('<IB', frame, 0)
    data = frame[8:8+dlc]

    pgn, sa, prio = decode_pgn_from_eff(can_id)

    if pgn in INTERESTING_PGNS:
        print(ff"PGN={pgn:6d} SA={sa:#04x} prio={prio} data={data.hex()}")

07 OpenCPN ve Signal K entegrasyonu

Signal K, deniz verileri için açık kaynak JSON tabanlı veri modelidir. N2K → Signal K köprüsü, OpenCPN gibi chart plotter yazılımlarına veri akışı sağlar.

Signal K Server kurulumu

bash — Signal K Server (Node.js)
# Node.js gereklidir
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
sudo apt install -y nodejs

# Signal K Server kurulumu
sudo npm install -g @signalk/server

# Başlat
signalk-server

# Web arayüzü: http://raspberrypi:3000
# Admin: http://raspberrypi:3000/@signalk/server-admin-ui

Signal K Server N2K yapılandırması

Signal K Server admin arayüzünden CAN data bağlantısı yapılandırılır:

bash — Signal K N2K pipeline
# Server data bağlantısı için settings.json'a eklenir:
# Pipedeprovider: candump → analyzer -json → signal k

# Alternatif: canboat-to-signalk paketi
sudo npm install -g @canboat/canboatjs

# candump çıktısını SignalK'ya pipe et (manuel)
candump can0 | analyzer -json | \
  node /usr/local/lib/node_modules/@canboat/canboatjs/tools/fromPgn.js | \
  nc localhost 3000

Signal K API ile veri okuma

signalk_client.py
import requests, json

SIGNALK_URL = "http://localhost:3000/signalk/v1/api/vessels/self"

def get_vessel_data():
    resp = requests.get(SIGNALK_URL, timeout=2)
    data = resp.json()

    # Konum
    nav = data.get('navigation', {})
    pos = nav.get('position', {}).get('value', {})
    lat = pos.get('latitude')
    lon = pos.get('longitude')

    # Yön
    heading = nav.get('headingMagnetic', {}).get('value')
    if heading:
        import math
        heading = math.degrees(heading)

    # Hız
    sog = nav.get('speedOverGround', {}).get('value')
    if sog:
        sog_kn = sog * 1.944   # m/s → knot

    print(ff"Konum: {lat:.4f}°N, {lon:.4f}°E")
    print(ff"Yön: {heading:.1f}°  SOG: {sog_kn:.1f} kn")

get_vessel_data()

OpenCPN konfigürasyonu

OpenCPN, Signal K veya NMEA 0183 TCP/UDP stream ile beslenir. Signal K server kuruluysa OpenCPN'de Signal K plugin ile bağlantı kurulabilir.

bash — OpenCPN Signal K bağlantısı
# OpenCPN kurulumu
sudo apt install opencpn

# OpenCPN içinden: Options → Connections → Add Signal K
# Host: localhost, Port: 3000
# Veya NMEA 0183 TCP → Signal K NMEA0183 output aktif et

# NMEA 0183 çıktısı için Signal K'ya NMEA 0183 output eklenir
# Settings → Signal paths → Enable NMEA 0183 output → port 10110
# OpenCPN: Options → Connections → TCP, localhost:10110

08 Pratik: Raspberry Pi tekne veri ağ geçidi

Raspberry Pi 4 + USB-CAN adaptörü (veya SPI-MCP2515) ile teknenin N2K bus'undaki GPS, pusula ve makine verileri WiFi üzerinden tablet veya telefona aktarılabilir.

Sistem mimarisi

  NMEA 2000 Bus
       │
  [Actisense NGT-1 USB]  veya  [MCP2515 SPI → N2K kablo]
       │                              │
  Raspberry Pi 4 ──────────────────────
       │
  candump | analyzer -json
       │
  Signal K Server (TCP 3000)
       │
  WiFi Access Point (hostapd)
       │
  Tablet / Telefon
  (OpenCPN, iNavX, Aqua Map)
    

Tam kurulum scripti

bash — n2k_gateway_setup.sh
#!/bin/bash
# NMEA 2000 → WiFi Gateway kurulum scripti

# 1. CAN interface başlat (Actisense NGT-1 veya MCP2515 sonrası)
sudo ip link set can0 type can bitrate 250000 restart-ms 200
sudo ip link set can0 up
echo "CAN arayüzü açıldı"

# 2. CANBOAT kontrolü
which analyzer || { echo "CANBOAT yok, kuruluyor..."; sudo apt install -y canboat; }

# 3. Signal K başlat (systemd servis olarak)
sudo systemctl start signalk.service
sudo systemctl enable signalk.service

# 4. N2K veri akışını test et
timeout 5 candump can0 | analyzer -json | head -20

echo "Gateway hazır — http://$(hostname -I | awk '{print $1}'):3000"

GPS ve pusula entegrasyonu örneği

n2k_dashboard.py — basit terminali dashboard
import subprocess, json, math, os, time

def clear(): os.system('clear')

data = {'lat': None, 'lon': None,
        'heading': None, 'sog': None, 'depth': None}

proc = subprocess.Popen(
    ['bash', '-c', 'candump can0 | analyzer -json'],
    stdout=subprocess.PIPE, text=True
)

for line in proc.stdout:
    try:
        msg = json.loads(line)
        pgn = msg.get('pgn')
        fields = msg.get('fields', {})

        if pgn == 129025:
            data['lat'] = fields.get('Latitude')
            data['lon'] = fields.get('Longitude')
        elif pgn == 127250:
            h = fields.get('Heading')
            if h: data['heading'] = math.degrees(h)
        elif pgn == 129026:
            sog = fields.get('SOG')
            if sog: data['sog'] = sog * 1.944
        elif pgn == 128267:
            data['depth'] = fields.get('Depth')

        clear()
        print("=== NMEA 2000 Dashboard ===")
        print(ff"Konum: {data['lat']:.4f}°N  {data['lon']:.4f}°E" if data['lat'] else "Konum: ---")
        print(ff"Yön:   {data['heading']:.1f}°" if data['heading'] else "Yön:   ---")
        print(ff"SOG:   {data['sog']:.1f} kn" if data['sog'] else "SOG:   ---")
        print(ff"Derinlik: {data['depth']:.1f} m" if data['depth'] else "Derinlik: ---")
    except (json.JSONDecodeError, KeyError):
        continue

SeaTalk ng ile uyumluluk notu

Raymarine cihazları SeaTalk ng (STng) kullanır. STng elektriksel olarak NMEA 2000 ile uyumludur (aynı CAN fiziksel katman, aynı DeviceNet kablo); ancak özel mavi renk konnektörler ve farklı PGN uzantıları içerir.

ÖzellikNMEA 2000SeaTalk ng (STng)
Elektriksel katmanDeviceNet (Mini)Aynı (DeviceNet uyumlu)
Bitrate250 kbit/s250 kbit/s
Standart PGN'lerTam destekDestekler + özel PGN'ler
Konnektör rengiGriMavi (Raymarine özel)
Linux uyumuTamTam (standart PGN'ler için)
ÖZET

SeaTalk ng kablolarını NMEA 2000 ağına bağlamak için Raymarine A06045 gibi adaptörler kullanılır. Veya konnektör üretim standartları biliniyorsa elle montaj yapılabilir. Linux ve CANBOAT tarafında herhangi bir özel konfigürasyon gerekmez — standart CAN 250 kbit/s üzerinde çalışır.