can

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 12, 2026 License: BSD-3-Clause Imports: 8 Imported by: 6

README

Go CAN Networking Package

Package can aims to provide a generic way to access CAN networks on various platforms.

It has been created in 2013 to enable tools written in Go to access PCAN USB adapters on both Linux and Windows, replacing earlier C-based work from a decade before that focused on can4linux.
Starting 2022, CAN FD and SocketCAN support was added gradually.

There is still no v1, due to its age the package has undergone multiple refactorings. Error handling is not optimal, it should align more to what SocketCAN defines.

Support for specific types of adapters is implemented as Drivers in subdirectory drv:

Driver Adapters Platforms CAN 2.0 FD
pcan PCAN-USB Linux, Windows
pcan PCAN-USB FD Linux*, Windows ☑ (Windows)
socketcan any adapter supported by SocketCAN Linux
rpc remote CAN adapters

* the FD mode of PCAN-USB FD may be used on Linux via the socketcan driver, but not yet via the pcan character-device driver.

Windows Support includes the arm64 architecture.

SocketCAN

The socketcan driver makes use of a utility socketcan-link, which is part of the repository and expected to be run with elevated privileges, specifically CAP_NET_ADMIN (to allow CAN interface configuration without root). See drv/socketcan for details.

cd drv/socketcan/cmd/socketcan-link
go build -o /path/to/bin/socketcan-link -trimpath -ldflags '-s -w'
sudo setcap cap_net_admin=ep /path/to/bin/socketcan-link

Device / Interface configuration

Package can provides a Plan 9 ctl file inspired text based configuration string that allows to specify the can device (adapter, interface) and to configure the bit timings (see ParseConfig documentation for details).

This config string can be as simple as ",500k", which will tell can.Open() to look for the first available CAN adapter and configure it using a nominal bitrate of 500 kbit/s. The comma separates the empty device string from the parameter "500k".
To look specifically for a PCAN adapter, use "pcan,500k", which will use the first available PCAN device. "pcan:usb2,500k" narrows the configuration to the second PCAN USB adapter. Similar, "socketcan" will use any available SocketCAN adapter, while "socketcan:can0" explicitely selects the can0 network interface, and "socketcan:@spi0.1" would select the network interface linked to SPI device 0.1.

On default, a sample point of 87.5% is assumed. To specify a different sample point, use for instance: 500k@.7 for 70%. A data bittiming for CAN FD mode can be specified using the db: prefix:

500k@.8,db:1M@.7

This will set the nominal bittiming to 500 kbit/s at 80%, and the data bittiming to 1 Mbit/s with a sample point set to 70%.

Sync jump width is set to the size of the phase segment 2 on default, but can be set to a specific value (in tq or as fraction) if needed, like 4 tq: 500k@.8s4 or 500k@.8:s4, or 10 percent: 500k@.8:s.1

Instead of a bitrate a bit timing specification may be used, like:

*25:34-35-10

which means 1 tq = 25ns, propSeg = 34 tq, phaseSeg1 = 35 tq, phaseSeg2 = 10 tq. This will result in a bitrate of 500 kbit/s. The * character signals the multiplicative nature of the tq value; there is also / to specify a clock prescaler, like /2.

FD mode is selected automatically if a data bitrate is specified and the adapter supports FD mode. It can be enforced by specifying fd, like in ,1M,fd.

Basic Usage

package main

import (
	"github.com/knieriem/can"
	"github.com/knieriem/can/drv/socketcan"
)

func main() {
	can.RegisterDriver(socketcan.Driver)

	dev, err := can.Open("")
	if err != nil {
		// ...
	}

	var m can.Msg

	// Write a standard frame containing one byte, 0x42, with identifier 0xABC.
	m.Id = 0xABC
	data := m.Data()[:1]
	data[0] = 0x42
	m.SetData(data)
	err = dev.WriteMsg(&m)
	if err != nil {
		// ...
	}
}

cmd/can Utility

Command can provides functionality like calculating bit timings:

./can bt -dev candlelightfd 500k

or writing a CAN frame, similar but less complete compared to what can-utils' cansend provides.

Tested Adapters

  • PCAN-USB, PCAN-USB FD (socketcan, pcan; Linux, Windows)
  • MCP2518FD (socketcan; Linux on RPi)
  • candleLight FD (USB/STM32G0B1, socketcan; Linux)

Use Cases

Industrial
This package has been used for CAN 2.0 tooling, e.g. as part of a Modbus-over-CAN firmware downloader, or as development tool. In these cases, the PCAN-USB adapters are used on Windows and Linux.

Automotive
The package is also used to communicate with LED drivers like ST's LLDL16EN on a CAN FD light bus. For this application, SocketCAN is used, with a MCP2518FD on linux/arm64.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrFDNotSupported = Error("FD mode not supported")
View Source
var ErrInvalidMsgLen = errors.New("invalid message length")
View Source
var ErrMsgCapExceeded = errors.New("message capacity too small")
View Source
var ErrTxQueueFull = Error("tx queue full")

ErrTxQueueFull is returned when a Msg could not be added to the devices' transmit queue. On Linux, it is returned in case of ENOBUFS. Normally this error is caused by a wiring problem, or if no CAN node is present on the bus.

View Source
var ValidFDSizes = []int{12, 16, 20, 24, 36, 48, 64}

Functions

func FormatBitrate added in v0.3.0

func FormatBitrate(b uint32) string

func RegisterDriver

func RegisterDriver(drv Driver)

func VerifyDataLenFD added in v0.3.0

func VerifyDataLenFD(n int) (next int, needsFD bool, err error)

Types

type BitTimingConfig added in v0.3.0

type BitTimingConfig struct {
	Bitrate     uint32
	SamplePoint timing.SamplePoint

	timing.BitTiming

	Tq time.Duration
}

func (*BitTimingConfig) Resolve added in v0.3.0

func (btc *BitTimingConfig) Resolve(dest *BitTimingConfig, clock uint32, cstr *timing.Constraints) error

Resolve interprets a BitTimingConfig.

If a bitrate and (optionally) a sample point are specified, it calculates a timing.BitTiming, taking fOsc and dev into account.

If, instead, the BitTiming field is provided, it validates and, if necessary, fills in the Tq or Prescaler fields.

The result is stored into dest if dest is non-nil; otherwise, the receiver btc is modified in-place.

func (*BitTimingConfig) String added in v0.3.0

func (c *BitTimingConfig) String() string

type Config added in v0.3.0

type Config struct {
	Nominal BitTimingConfig
	Data    Optional[BitTimingConfig]

	Termination Optional[bool]
	FDMode      Optional[bool]

	MsgFilter []MsgFilter
}

func ParseConfig added in v0.3.0

func ParseConfig(specs ...string) (*Config, error)

ParseConfSpecs parses CAN adapter configuration specifications. The strings may contain space separated parameter settings.

The syntax of configuration strings has been designed with Plan 9's _ctl_ file commands in mind (see https://plan9.io/magic/man2html/3/uart for an example). The basic structure is:

key ":" value

The colon may be omitted; in this case, the start of the value will be the position of the first decimal digit.

A value may be a plain integer, a bool or a more complex string.

Boolean values are represented by integer values 1 and 0, which map to true and false. In case of true, the value may be omitted -- the key used alone stands for the value being "true".

If a parameter is omitted altogether, the default settings of adapters will be used, if not otherwise specified.

Defined parameters:

b - nominal bit timing, optionally with a sample point, and SJW

	A value can be a bit timing expression:

	  bit-timing-expr = ( bitrate | bittiming ) [[ ":" ] sjw]

	          bitrate = number [ "k" | "M" ] [ "@" sample-point ]

	        bittiming = ( "*" tq | "/" prescaler ) ":" seg-expr

	         seg-expr = prop-seg "-" ps1 "-" ps2

	              sjw = "s" [ number | "." fraction ]

	     sample-point = "." fraction

	Examples: 500k@.875, b1M@.75 refering to 500 kbit/s or 1 Mbit/s,
	with sample points at 87.5% resp. 75%.
	In case of nominal bitrates, as an exception, the "b" key may be
	omitted. So, stating "500k" will be recognized as b:500k.

db - data bit timing, optionally with a sample point, and SJW

	A data bit timing value is a bit timing expr. See the definition
	of "b" (nominal bit timing) for details.

fd - CAN FD mode

	A boolean parameter deciding whether the CAN adapter should be run
	in CAN 2.0 mode or FD mode.

f - CAN message filter

	A value has the form:  id ":" mask,
	where id and mask either consist of three characters (standard
	frame) or up to eight characters (extended frame), as in:
		f:123:7ff or f:123_4567:1fff_ffff

	A short form can be used where only the id is specified and
	":" mask part is omitted. Within the id part, "-" may be used
	for a nibble that may contain any value from 0 to 0xF, as in:
		f:67-
	This would enable the receipt of messages with standard frame
	CAN IDs from 0x670 to 0x67F.

	Multiple filters may be specified. The effect of a filter may
	be inverted by using a prefix "!" in front of the id part,
	like in "f!12-" (the example would avoid the reception of
	standard frames in the range 120 to 12F).

T - enable/disable termination resistor

	This is a boolean parameter.

func (*Config) Format added in v0.3.0

func (c *Config) Format(sep string) string

func (*Config) ResolveBitTiming added in v0.3.0

func (conf *Config) ResolveBitTiming(ctl *timing.Controller) error

ResolveBittiming calls Resolve on the nominal and, if requested and supported, the data BitTimingConfig fields, updating the Config in-place. The function returns any error received from any of the Resolve calls.

func (*Config) ResolveFDMode added in v0.3.0

func (conf *Config) ResolveFDMode(fdCapable bool) (isFD bool, err error)

ResolveFDMode determines whether a Config requests FD mode, factoring in the hardware's FD capability. It does not modify the Config.

FD mode is requested if either the FDMode option is enabled or data bit timing is specified. If the request is "soft" (FDMode.Soft or Data.Soft is true) and fdCapable is false, the function returns isFD==false without an error. If the request is strict and fdCapable is false, it returns ErrFDNotSupported.

type DataBufPool added in v0.3.0

type DataBufPool interface {
	Get(minSize int) DataBuffer
}

type DataBuffer added in v0.3.0

type DataBuffer interface {
	// Data returns a byte slice with len set to the current data
	// portion, and cap set to the size of the underlying buffer.
	Data() []byte

	// Set updates the length of the current data if the underlying
	// buffer is the same. Otherwise it will use copy() to import
	// the specified bytes.
	Set([]byte)

	// Put returns the buffer back to its internally referenced pool.
	// Depending on whether a pool is associated, this may be a no-op.
	Put()

	// Reset sets the current slice to an empty slice.
	Reset()
}

type Device

type Device interface {
	Read([]Msg) (n int, err error)

	// Writes a message into the driver transmit buffer.
	// The ownership of the message will not be taken.
	WriteMsg(*Msg) error

	// As an alternative to WriteMsg, Write can be used
	// if more than one message should be handed over
	// to the driver at once (if the driver is able to do that).
	Write([]Msg) (n int, err error)

	ID() string
	Info() *DeviceInfo

	Close() error
}

The Device interface gives access to a CAN Device. Read and Write calls will block if no messages are available to be read or if the transmit buffer of the driver is full.

func Open

func Open(deviceSpec string, opts ...Option) (dev Device, err error)

Open tries to open a CAN device matching the device specification. The deviceSpec has the syntax

[ driverName [ ":" deviceName ] { "," ctlString } ]

The syntax suggests that "" is a valid input: It will try to open any available CAN adapter with driver dependent default settings. The comma separated ctl strings will be processed by ParseConfig. On success, a Device instance will be returned, else an error.

type DeviceInfo added in v0.3.0

type DeviceInfo struct {
	ID string

	Model  string
	Device string
	Driver string

	SystemDriver        string
	SystemDriverVersion string

	APIVersion string
	Firmware   string
	SerialNum  string
}

func Scan

func Scan() (list []DeviceInfo)

func (*DeviceInfo) Format added in v0.3.0

func (di *DeviceInfo) Format(idSep, itemSep, end string) string

func (*DeviceInfo) String added in v0.3.0

func (di *DeviceInfo) String() string

type Driver

type Driver interface {
	Name() string
	//	Version() string
	Open(env *Env, name string, conf *Config) (Device, error)
	Scan() []DeviceInfo
}
var UnsupportedDriver Driver = unsupported{}

type Env added in v0.3.0

type Env struct {
	BufPool DataBufPool
}

type Error

type Error string

func (Error) Error

func (e Error) Error() string

type Flags

type Flags int

Message Flags.

const (
	// message type
	ExtFrame Flags = 1 << iota
	RTRMsg
	StatusMsg

	// FD specific flags
	FDSwitchBitrate
	ForceFD

	// if StatusMsg is set:
	MissingAck
	ErrorActive
	ErrorWarning
	ErrorPassive
	BusOff
	DataOverrun
	ReceiveBufferOverflow
)

func (Flags) ExtFrame

func (f Flags) ExtFrame() bool

Reports whether the message contains an 29 bit wide, extended indentifier, or a standard 11 bit wide identifier.

func (Flags) IsStatus

func (f Flags) IsStatus() bool

Reports wether the message is a status message, not a data message. In the first case, Msg fields Id, Len and Data should not be interpreted.

func (Flags) Test

func (f Flags) Test(t Flags) bool

type Msg

type Msg struct {
	Id uint32 // The CAN message identifier
	Flags

	Rx struct {
		Time Time // Timestamp
	}
	// contains filtered or unexported fields
}

Definition of a CAN Message.

func (*Msg) Attach added in v0.3.0

func (m *Msg) Attach(b DataBuffer)

Attach is similar to SetData, but instead of a byte slice, a DataBuffer must be provided. This helps to avoid an internal allocation in case the data contains more than eight bytes.

func (*Msg) Data

func (m *Msg) Data() []byte

Data returns the current Payload of the message. If no payload buffer has been set by calling SetData before, Data returns a byte slice with the standard payload length 8.

func (*Msg) FromExpr added in v0.3.0

func (m *Msg) FromExpr(expr string) error

FromExpr parses a CAN message expression string and stores the result into m, which may be a pre-initialized value. The format is similar to the format used by cansend from can-utils.

CAN ID and data, separated by '#' or ':', must be specified in hexadecimal format. An FD frame can be forced using a double separator, followed by a CAN flags hex nibble; supported FD flags: BRS = 0b0001.

The string may not contain white-space, but '.' can be used to separate data bytes.

func (*Msg) Import added in v0.3.0

func (m *Msg) Import(b []byte, pool DataBufPool) error

Import copies b into the message's backing store, either the standard payload array (if ≤ 8 bytes), or a user provided buffer previously set using SetData, if available. Else it will try to get a sufficient buffer from the pool, link it to the message, and copy the contents of b there. If the pool argument is nil, ErrMsgCapExceeded will be replied.

func (*Msg) Release added in v0.3.0

func (m *Msg) Release()

func (*Msg) Reset added in v0.3.0

func (m *Msg) Reset()

Reset sets the message back to the initial state.

func (*Msg) SetData added in v0.3.0

func (m *Msg) SetData(b []byte)

SetData updates the payload of the message.

type MsgFilter added in v0.3.0

type MsgFilter struct {
	ID       uint32
	IDMask   uint32
	ExtFrame bool
	Invert   bool
}

func (*MsgFilter) Range added in v0.3.0

func (f *MsgFilter) Range() (from, to uint32, ok bool)

Range returns two values defining a range that corresponds a single region defined by ID and IDMask fields. In this case ok will be set to true. If ID and IDMask would create multiple / many regions, ok is set to false. Range may be useful for drivers that implement filtering based on ID ranges.

type Option added in v0.3.0

type Option func(*openProps)

func WithConfig added in v0.3.0

func WithConfig(conf *Config) Option

type Optional added in v0.3.0

type Optional[T any] struct {
	Valid bool
	Soft  bool
	Value T
}

func (*Optional[T]) Set added in v0.3.0

func (o *Optional[T]) Set(value T)

type PlainData added in v0.3.0

type PlainData []byte

func (PlainData) Data added in v0.3.0

func (pd PlainData) Data() []byte

func (PlainData) Put added in v0.3.0

func (PlainData) Put()

func (*PlainData) Reset added in v0.3.0

func (pd *PlainData) Reset()

func (*PlainData) Set added in v0.3.0

func (pd *PlainData) Set(b []byte)

type Time

type Time int64

func Now

func Now() Time

func UnixTimevals

func UnixTimevals(s, µs int32) Time

func (Time) Time

func (t Time) Time() time.Time

func (Time) UnixTimevals

func (t Time) UnixTimevals() (s, µs int32)

type Unversioned

type Unversioned struct{}

func (Unversioned) Info added in v0.3.0

func (Unversioned) Info() *DeviceInfo

Directories

Path Synopsis
cmd module
drv
A helper package for the various CAN driver interface packages.
A helper package for the various CAN driver interface packages.
all
Convenience package that registers all known drivers.
Convenience package that registers all known drivers.
canrpc
Package canrpc implements net/rpc client and server objects.
Package canrpc implements net/rpc client and server objects.
socketcan module
dev

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL