5.6 KiB
🔌 CAN Go
CAN toolkit for Go programmers.
can-go makes use of the Linux SocketCAN abstraction for CAN communication. (See the SocketCAN documentation for more details).
Modifications to the Original Repo
Original repo: https://github.com/jshiv/can-go/tree/623b1140d54a845026249a5bbbaf23f44c614173.
Comment out the value descriptor generation code within can-go/internal/generate/file.go, lines 128-141. This code creates compile errors due to duplicate values within the .dbc file provided.
DBC Go Code Library Generation
- Place
.dbcfile intoorig/folder. - Run
scripts/main.go:
go run scripts/main.go
This will translate all Chinese variable values within the file to English. Any non-alphanumeric characters are also removed. NOTE The default read file is labelled orig/121-N60AB_ADASBUS_Matrix_CANFD_V2.3.dbc and the default write file is labelled dbc/n60.dbc.
- There will still be leftover Chinese characters in the
.dbcfile, I chose to just remove them. - Generate Go code from cleaned
.dbcfile:
can-go> go run cmd/cantool/main.go generate data/dbc dbc
Examples
Decoding CAN messages
Decoding CAN messages from byte arrays can be done using can.Payload
func main() {
// DBC file
var dbcFile = []byte(`
VERSION ""
NS_ :
BS_:
BU_: DBG DRIVER IO MOTOR SENSOR
BO_ 1530 DisconnectState: 14 MOTOR
SG_ LockCountRearRight : 91|20@0+ (1,0) [0|1048575] "" IO
SG_ DisconnectStateRearRight : 95|4@0+ (1,0) [0|5] "" IO
SG_ CurrentRearRight : 79|16@0+ (1,0) [0|65535] "" IO
SG_ DisconnectStateRearRightTarget : 64|1@0+ (1,0) [0|1] "" IO
SG_ TargetSpeedRearRight : 63|15@0+ (0.125,-2048) [-2048|2047.875] "rad/s" IO
SG_ LockCountRearLeft : 35|20@0+ (1,0) [0|1048575] "" IO
SG_ DisconnectStateRearLeft : 39|4@0+ (1,0) [0|5] "" IO
SG_ CurrentRearLeft : 23|16@0+ (1,0) [0|65535] "" IO
SG_ DisconnectStateRearLeftTarget : 8|1@0+ (1,0) [0|1] "" IO
SG_ TargetSpeedRearLeft : 7|15@0+ (0.125,-2048) [-2048|2047.875] "rad/s" IO
VAL_ 1530 DisconnectStateRearRight 0 "Undefined" 1 "Locked" 2 "Unlocked" 3 "Locking" 4 "Unlocking" 5 "Faulted" ;
VAL_ 1530 DisconnectStateRearLeft 0 "Undefined" 1 "Locked" 2 "Unlocked" 3 "Locking" 4 "Unlocking" 5 "Faulted" ;
`)
// Create payload from hex string
byteStringHex := "8000000420061880000005200600"
p, _ := can.PayloadFromHex(byteStringHex)
// Load example dbc file
c, _ := generate.Compile("test.dbc", dbcFile)
db := *c.Database
// Decode message frame ID 1530
message, _ := db.Message(uint32(1530))
decodedSignals := message.Decode(&p)
for _, signal := range decodedSignals {
fmt.Printf("Signal: %s, Value: %f, Description: %s\n", signal.Signal.Name, signal.Value, signal.Description)
}
}
Signal: TargetSpeedRearLeft, Value: 0.000000, Description:
Signal: DisconnectStateRearLeftTarget, Value: 0.000000, Description:
Signal: CurrentRearLeft, Value: 4.000000, Description:
Signal: LockCountRearLeft, Value: 1560.000000, Description:
Signal: DisconnectStateRearLeft, Value: 2.000000, Description: Unlocked
Signal: TargetSpeedRearRight, Value: 0.000000, Description:
Signal: DisconnectStateRearRightTarget, Value: 0.000000, Description:
Signal: CurrentRearRight, Value: 5.000000, Description:
Signal: LockCountRearRight, Value: 1536.000000, Description:
Signal: DisconnectStateRearRight, Value: 2.000000, Description: Unlocked
Receiving CAN frames
Receiving CAN frames from a socketcan interface.
func main() {
// Error handling omitted to keep example simple
conn, _ := socketcan.DialContext(context.Background(), "can", "can0")
recv := socketcan.NewReceiver(conn)
for recv.Receive() {
frame := recv.Frame()
fmt.Println(frame.String())
}
}
Sending CAN frames/messages
Sending CAN frames to a socketcan interface.
func main() {
// Error handling omitted to keep example simple
conn, _ := socketcan.DialContext(context.Background(), "can", "can0")
frame := can.Frame{}
tx := socketcan.NewTransmitter(conn)
_ = tx.TransmitFrame(context.Background(), frame)
}
Generating Go code from a DBC file
It is possible to generate Go code from a .dbc file.
$ go run github.com/Fisker-Inc/project-ai-can-go/cmd/cantool generate <dbc file root folder> <output folder>
In order to generate Go code that makes sense, we currently perform some validations when parsing the DBC file so there may need to be some changes on the DBC file to make it work
After generating Go code we can marshal a message to a frame:
// import etruckcan "github.com/myproject/myrepo/gen"
auxMsg := etruckcan.NewAuxiliary().SetHeadLights(etruckcan.Auxiliary_HeadLights_LowBeam)
frame := auxMsg.Frame()
Or unmarshal a frame to a message:
// import etruckcan "github.com/myproject/myrepo/gen"
// Error handling omitted for simplicity
_ := recv.Receive()
frame := recv.Frame()
var auxMsg *etruckcan.Auxiliary
_ = auxMsg.UnmarshalFrame(frame)