# :electric_plug: CAN Go [![PkgGoDev][pkg-badge]][pkg] [![GoReportCard][report-badge]][report] [![Codecov][codecov-badge]][codecov] [pkg-badge]: https://pkg.go.dev/badge/github.com/Fisker-Inc/project-ai-can-go [pkg]: https://pkg.go.dev/github.com/Fisker-Inc/project-ai-can-go [report-badge]: https://goreportcard.com/badge/github.com/Fisker-Inc/project-ai-can-go [report]: https://goreportcard.com/report/github.com/Fisker-Inc/project-ai-can-go [codecov-badge]: https://codecov.io/gh/einride/can-go/branch/master/graph/badge.svg [codecov]: https://codecov.io/gh/einride/can-go CAN toolkit for Go programmers. can-go makes use of the Linux SocketCAN abstraction for CAN communication. (See the [SocketCAN][socketcan] documentation for more details). [socketcan]: https://www.kernel.org/doc/Documentation/networking/can.txt ## 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 1. Place `.dbc` file into `orig/` folder. 2. 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`. 3. There will still be leftover Chinese characters in the `.dbc` file, I chose to just remove them. 4. Generate Go code from cleaned `.dbc` file: ``` 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` ```go 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. ```go 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. ```go 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 ``` 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: ```go // 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: ```go // import etruckcan "github.com/myproject/myrepo/gen" // Error handling omitted for simplicity _ := recv.Receive() frame := recv.Frame() var auxMsg *etruckcan.Auxiliary _ = auxMsg.UnmarshalFrame(frame) ```