Categorygithub.com/aldas/go-nmea-client
repositorypackage
0.0.0-20230718091314-75bc78b9f6f8
Repository: https://github.com/aldas/go-nmea-client.git
Documentation: pkg.go.dev

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# README

go-nmea-client

WORK IN PROGRESS Only public because including private Go library is too much of a hustle for CI


Go library to read NMEA 2000 messages from SocketCAN interfaces or USB devices (Actisense NGT1/W2K-1 etc).

In addition, this repository contains command line application n2k-reader to provide following features:

  • Can read input from:
    • files
    • TCP connections
    • serial devices
  • Can read different input formats:
    • SocketCAN format
    • CanBoat raw format
    • Actisense format:
      • NGT1 Binary,
      • N2K Ascii,
      • N2K Binary,
      • Raw ASCII
      • EBL (log files from W2K-1 device, NB: NGT1 format is different)
  • Can output read raw frames/messages as:
    • JSON,
    • HEX,
    • BASE64,
    • CanBoat format
  • Can assemble Fast-Packet frames into complete Messages
  • Can decode CAN messages to fields with CanBoat PGN database
  • Can output decoded messages fields as:
    • JSON (stdout)
    • CSV file (each PGN has own csv file). columns (fields) can be customized
  • Can send STDIN input to CAN interface/device
  • Can do basic NMEA2000 bus NODE mapping (which devices/nodes exist in bus)
    • Can list known nodes (send !nodes as input)
    • Can request nodes NAMES from STDIN (send !addr-claim as input)

Disclaimer

This repository exists only because of CanBoat authors. They have done a lot of work to acquire knowledge of NMEA2000 protocol and made it free.

NMEA2000 reader

Compile NMEA2000 reader for different achitectures/platforms (AMD64,ARM32v6,ARM32v7,ARM64,MIPS32 (softfloat)).

make n2kreader-all

Create Actisense reader that can be run on MIPS architecture (Teltonika RUT955 router ,CPU: Atheros Wasp, MIPS 74Kc, 550 MHz)

GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags="-s -w" -o n2k-reader-mips cmd/n2kreader/main.go

Help about arguments:

./n2k-reader -help

Example usage:

Run reader suitable for Raspberry Pi Zero with Canboat PGN database (canboat.json). Only decode PGNs 126996,126998 and output decoded messages as JSON.

./n2kreader-reader-arm32v6 -pgns ./canboat.json -filter 126996,126998 -output-format json
  • You can write data to NMEA bus by sending text to STDIN. Example 6,59904,0,255,3,14,f0,01 + \n sends PGN 59904 from src 0 to dst 255 requesting PGN 126996 (0x01, 0xf0, 0x14)
  • !nodes - lists all knowns node NAME and their associated Source values
  • !addr-claim - sends broadcast request for ISO Address Claim

Read device /dev/ttyUSB0 as ngt format, filter out PGNS 59904,60928 and output decoded messages as json:

./n2k-reader-arm32v6 -pgns canboat.json -input-format ngt -device "/dev/ttyUSB0" -filter 59904,60928 -output-format json

Read file as n2k-ascii format and output decoded messages as json format:

./n2k-reader -pgns=canboat/testdata/canboat.json \
   -device="actisense/testdata/actisense_n2kascii_20221028_10s.txt" \
   -is-file=true \
   -output-format=json \
   -input-format=n2k-ascii

Read file as canboat-raw format, filter out PGNS 127245,127250,129026 and append decoded messages as new lines to CSV files with given fields as columns:

./n2k-reader-amd64 -pgns canboat/testdata/canboat.json \
  -device canboat/testdata/canboat_format.txt \
  -np \
  -is-file \
  -input-format canboat-raw \
  -csv-fields "127245:_time_ms,position,directionOrder;127250:_time_ms(100ms),heading;129026:_time_ms,cog,sog"

This is instructs reader to treat device actisense/testdata/actisense_n2kascii_20221028_10s.txt as an ordinary file instead of serial device. All input read from device is decoded as Actisense N2K binary protocol ( Actisense W2K-1 device can output this) and print output in JSON format.

Read Actisense EBL log file as BST-95 format (created by W2K-1 device) and output decoded messages as json format:

./n2k-reader -pgns=canboat/testdata/canboat.json \
   -device="actisense/testdata/actisense_w2k1_bst95.ebl" \
   -is-file=true \
   -output-format=json \
   -input-format=ebl

Library example

func main() {
	f, err := os.Open("canboat.json")
	schema := canboat.CanboatSchema{}
	if err := json.NewDecoder(f).Decode(&schema); err != nil {
		log.Fatal(err)
	}
	decoder := canboat.NewDecoder(schema)

	// reader, err = os.OpenFile("/path/to/some/logged_traffic.bin", os.O_RDONLY, 0)
	reader, err := serial.OpenPort(&serial.Config{
		Name: "/dev/ttyUSB0",
		Baud: 115200,
		// ReadTimeout is duration that Read call is allowed to block. Device has different timeout for situation when
		// there is no activity on bus. Can not be smaller than 100ms
		ReadTimeout: 100 * time.Millisecond,
		Size:        8,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer reader.Close()

	config := actisense.Config{
		ReceiveDataTimeout:      5 * time.Second,
		DebugLogRawMessageBytes: false,
	}
	// device = actisense.NewN2kASCIIDevice(reader, config) // W2K-1 has support for Actisense N2K Ascii format
	device := actisense.NewBinaryDeviceWithConfig(reader, config)
	if err := device.Initialize(); err != nil {
		log.Fatal(err)
	}

	ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer cancel()
	for {
		rawMessage, err := device.ReadRawMessage(ctx)
		if err != nil {
			if err == io.EOF || err == context.Canceled {
				return
			}
			log.Fatal(err)
		}
		b, _ := json.Marshal(rawMessage)
		fmt.Printf("#Raw %s\n", b)

		pgn, err := decoder.Decode(rawMessage)
		if err != nil {
			fmt.Println(err)
			continue
		}

		b, _ = json.Marshal(pgn)
		fmt.Printf("%s\n", b)
	}
}

Research/check following:

  1. https://gist.github.com/jackm/f33d6e3a023bfcc680ec3bfa7076e696