github.com/simpleiot/simpleiot@v0.18.3/docs/user/can.md (about) 1 # CAN Bus Client 2 3 The CAN client allows loading a standard CAN database file, recieving CAN data, 4 and translating the CAN data into points via the database. 5 6 ## Usage 7 8 The CAN client can be used as part of the SimpleIoT library or through the web 9 UI. The first step in either case is to create a CAN database in .kbc format. 10 11 ### Create the CAN Database 12 13 Create a file in the folder with the Go code named "test.kcd" containing the 14 following: 15 16 ```xml 17 <NetworkDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://kayak.2codeornot2code.org/1.0" xsi:schemaLocation="Definition.xsd"> 18 <Document name="Some Document Name">some text</Document> 19 <Bus name="sampledatabase"> 20 <Message id="0x123" name="HelloWorld" length="8"> 21 <Notes></Notes> 22 <Signal name="Hello" offset="0" length="8"/> 23 <Signal name="World" offset="8" length="8"/> 24 </Message> 25 <Message id="0x12345678" name="Food" length="8" format="extended"> 26 <Notes></Notes> 27 <Signal name="State" offset="0" length="32"/> 28 <Signal name="Type" offset="32" length="32"/> 29 </Message> 30 </Bus> 31 </NetworkDefinition> 32 ``` 33 34 You can create any CAN database you want by crafting it in Kvaser's free DBC 35 editor and then using the `canmatrix` tool to convert it to KCD format. Note 36 that `canmatrix` does not support all features of the DBC and KCD formats. 37 38 Next, setup the virtual socketCan interface. 39 40 ### Setup Virtual CAN Interface 41 42 Run this in the command line. 43 [Reference](https://www.pragmaticlinux.com/2021/10/how-to-create-a-virtual-can-interface-on-linux/) 44 45 ```bash 46 sudo modprobe vcan 47 sudo ip link add dev vcan0 type vcan 48 sudo ip link set up vcan0 49 ``` 50 51 ### Option #1 - Use In Web UI 52 53 Follow the [instructions](installation.md) to install SimpleIoT, run it, and 54 navigate to the web UI. 55 56 Expand the root node and click the + symbol to add a sub node. Select "CAN Bus" 57 and click "add". 58 59  60 61 Configure the CAN Bus node with a [File subnode](file.md) and upload the `.kcd` 62 file you created. 63 64  65 66 Once the file has been uploaded, you should see the following stats in the CAN 67 bus node: 68 69 Messages in db: 2 Signals in db: 4 70 71 ### Test with Messages 72 73 In a separate terminal: 74 75 ``` 76 cansend vcan0 123#R{8} 77 cansend vcan0 12345678#DEADBEEF 78 ``` 79 80 Ensure that there are no errors logged in the terminal by the application. 81 82 In the Web ui you should see the "Db msgs recieved" field increase to 2. 83 84 ### Option #2 - Use As Library 85 86 Copy this code to a Go file on your Linux machine in a folder by itself. 87 88 ```go 89 package main 90 91 import ( 92 "log" 93 94 "github.com/nats-io/nats.go" 95 "github.com/simpleiot/simpleiot/client" 96 "github.com/simpleiot/simpleiot/data" 97 "github.com/simpleiot/simpleiot/server" 98 ) 99 100 // exNode is decoded data from the client node 101 type exNode struct { 102 ID string `node:"id"` 103 Parent string `node:"parent"` 104 Description string `point:"description"` 105 Port int `point:"port"` 106 Role string `edgepoint:"role"` 107 } 108 109 // exNodeClient contains the logic for this client 110 type exNodeClient struct { 111 nc *nats.Conn 112 config client.SerialDev 113 stop chan struct{} 114 stopped chan struct{} 115 newPoints chan client.NewPoints 116 newEdgePoints chan client.NewPoints 117 chGetConfig chan chan client.SerialDev 118 } 119 120 // newExNodeClient is passed to the NewManager() function call -- when 121 // a new node is detected, the Manager will call this function to construct 122 // a new client. 123 func newExNodeClient(nc *nats.Conn, config client.SerialDev) client.Client { 124 return &exNodeClient{ 125 nc: nc, 126 config: config, 127 stop: make(chan struct{}), 128 newPoints: make(chan client.NewPoints), 129 newEdgePoints: make(chan client.NewPoints), 130 } 131 } 132 133 // Start runs the main logic for this client and blocks until stopped 134 func (tnc *exNodeClient) Run() error { 135 for { 136 select { 137 case <-tnc.stop: 138 close(tnc.stopped) 139 return nil 140 case pts := <-tnc.newPoints: 141 err := data.MergePoints(pts.ID, pts.Points, &tnc.config) 142 if err != nil { 143 log.Println("error merging new points:", err) 144 } 145 log.Printf("New config: %+v\n", tnc.config) 146 case pts := <-tnc.newEdgePoints: 147 err := data.MergeEdgePoints(pts.ID, pts.Parent, pts.Points, &tnc.config) 148 if err != nil { 149 log.Println("error merging new points:", err) 150 } 151 case ch := <-tnc.chGetConfig: 152 ch <- tnc.config 153 } 154 } 155 } 156 157 // Stop sends a signal to the Run function to exit 158 func (tnc *exNodeClient) Stop(err error) { 159 close(tnc.stop) 160 } 161 162 // Points is called by the Manager when new points for this 163 // node are received. 164 func (tnc *exNodeClient) Points(id string, points []data.Point) { 165 tnc.newPoints <- client.NewPoints{id, "", points} 166 } 167 168 // EdgePoints is called by the Manager when new edge points for this 169 // node are received. 170 func (tnc *exNodeClient) EdgePoints(id, parent string, points []data.Point) { 171 tnc.newEdgePoints <- client.NewPoints{id, parent, points} 172 } 173 174 func main() { 175 nc, root, stop, err := server.TestServer() 176 177 if err != nil { 178 log.Println("Error starting test server:", err) 179 } 180 181 defer stop() 182 183 canBusTest := client.CanBus{ 184 ID: "ID-canBus", 185 Parent: root.ID, 186 Description: "vcan0", 187 Device: "vcan0", 188 } 189 190 err = client.SendNodeType(nc, canBusTest, "test") 191 if err != nil { 192 log.Println("Error sending CAN node:", err) 193 } 194 195 // Create a new manager for nodes of type "testNode". The manager looks for new nodes under the 196 // root and if it finds any, it instantiates a new client, and sends point updates to it 197 m := client.NewManager(nc, newExNodeClient) 198 m.Start() 199 200 // Now any updates to the node will trigger Points/EdgePoints callbacks in the above client 201 } 202 ``` 203 204 **Run the following commands**: 205 206 - `go mod init example.com/m` 207 - `go run <file>.go` 208 - Run the `go get` commands suggested by `go run` 209 - `go mod tidy` 210 - `go run <file>.go` 211 212 #### Run it! 213 214 `go run <file.go>` 215 216 Follow instructions from the "Test with Messages" section above. 217 218 ## Future Work 219 220 - Scale and translate messages based on scale and offset parameters in database 221 - Auto connect to CAN bus in case it is brought up after SIOT client is started 222 - Attempt to bring up CAN bus within client, handle case where it is already up 223 - Support multiple CAN database files per node (be selective in which internal 224 db is updated when a name or data point is recieved in the client) 225 - Support sending messages (concept of nodes and send/recieve pulled from 226 databases??) 227 - Support .dbc file format in addition to .kcd 228 - Add the concept of a device to the CAN message points