169 lines
5.6 KiB
Markdown
169 lines
5.6 KiB
Markdown
# :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 <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:
|
|
|
|
```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)
|
|
|
|
```
|