Skip to main content

Device Discovery

lplex automatically discovers and tracks all devices on the NMEA 2000 bus. It builds a device registry from two PGN types:

  • PGN 60928 (ISO Address Claim): provides the 64-bit CAN NAME, manufacturer code, device class, function, and instance
  • PGN 126996 (Product Information): provides model ID, software version, and serial number

How discovery works

  1. When lplex sees a frame from an unknown source address, it sends an ISO Request (PGN 59904) to that source asking for an address claim
  2. The device responds with PGN 60928, which populates the basic device info
  3. lplex then requests PGN 126996 for product details
  4. The registry updates continuously as new claims and product info arrive

This is transparent. You don't need to configure anything.

NAME-based deduplication

If a device restarts and claims a new source address while keeping the same 64-bit NAME, lplex automatically evicts the old entry. This prevents stale phantom devices from accumulating in the registry, and the old source's values are also cleaned up.

Idle expiry

Devices that haven't sent any frames within the idle timeout (default 5 minutes) are automatically removed. This covers both NAME-bearing devices and stats-only entries. Configure with -device-idle-timeout (or HOCON device.idle-timeout). Set to 0 to disable.

Virtual device

When lplex-server sends CAN frames (via /send or /query), it needs a claimed source address to be a compliant NMEA 2000 participant. Without this, frames are sent from the unclaimed address 254, and some devices will ignore them.

Enable a virtual device to make lplex-server claim an address on the bus:

lplex-server -virtual-device -virtual-device-name 00e0170001000004

Or in HOCON:

virtual-device {
enabled = true
name = "00e0170001000004"
}

The virtual device:

  • Auto-selects a source address (starting at 252, counting down to avoid real hardware)
  • Claims the address via PGN 60928, waiting at least 250ms (minimum holdoff required by the NMEA 2000 / ISO 11783-5 spec) before using the claimed address
  • Resolves conflicts automatically (lower NAME wins; if we lose, we pick a new address)
  • Responds to ISO requests for address claim (PGN 60928) and product info (PGN 126996)
  • Heartbeats periodically, re-broadcasting address claims (default every 60s) and product info (default every 5m) to keep the bus aware of our presence
  • Appears in the device table like any other device, with full product info

The NAME must be a 64-bit hex value. Lower values have higher priority in address conflicts. See ISO 11783-5 for the NAME field encoding.

Heartbeat intervals are configurable via -virtual-device-claim-heartbeat (default 60s) and -virtual-device-product-info-heartbeat (default 5m). See Configuration for all options.

Multi-bus device keying

In multi-bus setups (multiple CAN interfaces), devices are keyed by (bus, source address) rather than source address alone. This means two devices on different buses can share the same source address without conflicting. The bus field appears in device JSON responses to identify which interface each device was discovered on.

Device table fields

FieldSourceDescription
srcCAN headerCurrent source address (0-253)
busCAN interfaceBus name (e.g. "can0"). Present in multi-bus setups.
namePGN 6092864-bit ISO 11783 NAME (hex string)
manufacturerPGN 60928Manufacturer name (resolved from code)
manufacturer_codePGN 60928Raw manufacturer code
device_classPGN 60928Device class number
device_functionPGN 60928Device function number
device_instancePGN 60928Device instance (0-252)
unique_numberPGN 6092821-bit unique number from NAME
product_codePGN 126996Product code
model_idPGN 126996Model name string
software_versionPGN 126996Software version string
model_versionPGN 126996Hardware/model version string
model_serialPGN 126996Serial number string
first_seenlplexRFC 3339 timestamp of first frame
last_seenlplexRFC 3339 timestamp of last frame
packet_countlplexTotal frames received from this device
byte_countlplexTotal bytes received from this device

API

List all devices

curl http://inuc1.local:8089/devices | jq
[
{
"src": 10,
"name": "0x00A1B2C3D4E5F600",
"manufacturer": "Garmin",
"manufacturer_code": 229,
"device_class": 25,
"device_function": 130,
"device_instance": 0,
"unique_number": 123456,
"product_code": 1234,
"model_id": "GPS 19x HVS",
"software_version": "5.60",
"model_version": "1",
"model_serial": "ABC123",
"first_seen": "2026-03-06T10:00:00Z",
"last_seen": "2026-03-06T10:15:32Z",
"packet_count": 45023,
"byte_count": 360184
}
]

Filter by device

Use device-based filters on the /events or /values endpoints:

# All frames from Garmin devices
curl -N "http://inuc1.local:8089/events?manufacturer=Garmin"

# Last values from device instance 0
curl "http://inuc1.local:8089/values?instance=0"

Device table in journals

Journal files include a device table snapshot in each block. This allows journal readers to resolve source addresses to device names without needing a live connection. The device table includes manufacturer, model ID, software version, and product code.

Cloud devices

The cloud server exposes the same device API per instance:

curl https://lplex.dockwise.app/instances/boat-001/devices | jq

Device info is carried over the replication stream. The replica broker maintains its own DeviceRegistry populated from replicated frames.