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  ![Creating a CAN Bus node in SimpleIoT](../images/create-canbus-node.png)
    60  
    61  Configure the CAN Bus node with a [File subnode](file.md) and upload the `.kcd`
    62  file you created.
    63  
    64  ![Configure the CAN Bus node with the .kcd file](../images/configure-canbus-node.png)
    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