├── .gitignore ├── constants_darwin.go ├── constants_windows.go ├── constants_linux.go ├── go.mod ├── go.sum ├── socket_linux.go ├── readwritecloser_darwin.go ├── readwritecloser_windows.go ├── socket_darwin.go ├── socket_windows.go ├── filter.go ├── readwritecloser_linux.go ├── handler.go ├── Makefile ├── wait_test.go ├── test_readwritecloser.go ├── frame_test.go ├── can.go ├── LICENSE ├── wait.go ├── bus_test.go ├── readwritecloser.go ├── frame.go ├── cmd └── candump.go ├── README.md └── bus.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build -------------------------------------------------------------------------------- /constants_darwin.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | // TODO(brutella) implement 4 | const AF_CAN = -1 5 | -------------------------------------------------------------------------------- /constants_windows.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | // TODO(brutella) implement 4 | const AF_CAN = -1 5 | -------------------------------------------------------------------------------- /constants_linux.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import "syscall" 4 | 5 | const AF_CAN = syscall.AF_CAN 6 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/brutella/can 2 | 3 | require golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM= 2 | golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 3 | -------------------------------------------------------------------------------- /socket_linux.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | func NewSockaddr(proto uint16, Ifindex int) syscall.Sockaddr { 8 | return &syscall.SockaddrLinklayer{ 9 | Protocol: proto, 10 | Ifindex: Ifindex, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /readwritecloser_darwin.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | func NewReadWriteCloserForInterface(i *net.Interface) (ReadWriteCloser, error) { 9 | return nil, fmt.Errorf("Binding to can interface no supported on Darwin") 10 | } 11 | -------------------------------------------------------------------------------- /readwritecloser_windows.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | func NewReadWriteCloserForInterface(i *net.Interface) (ReadWriteCloser, error) { 9 | return nil, fmt.Errorf("Binding to can interface no supported on Windows") 10 | } 11 | -------------------------------------------------------------------------------- /socket_darwin.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import "syscall" 4 | 5 | // NewSockaddr returns a socket address based on the protocol and interface index. 6 | // TODO(brutella) This method has no implementation. 7 | func NewSockaddr(proto uint16, Ifindex int) syscall.Sockaddr { 8 | return &syscall.SockaddrUnix{} 9 | } 10 | -------------------------------------------------------------------------------- /socket_windows.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import "syscall" 4 | 5 | // NewSockaddr returns a socket address based on the protocol and interface index. 6 | // TODO(brutella) This method has no implementation. 7 | func NewSockaddr(proto uint16, Ifindex int) syscall.Sockaddr { 8 | return &syscall.SockaddrUnix{} 9 | } 10 | -------------------------------------------------------------------------------- /filter.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | type filter struct { 4 | id uint32 5 | handler Handler 6 | } 7 | 8 | func newFilter(id uint32, handler Handler) Handler { 9 | return &filter{ 10 | id: id, 11 | handler: handler, 12 | } 13 | } 14 | 15 | func (f *filter) Handle(frame Frame) { 16 | if frame.ID == f.id { 17 | f.handler.Handle(frame) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /readwritecloser_linux.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "fmt" 5 | "golang.org/x/sys/unix" 6 | "net" 7 | "os" 8 | "syscall" 9 | ) 10 | 11 | func NewReadWriteCloserForInterface(i *net.Interface) (ReadWriteCloser, error) { 12 | s, _ := syscall.Socket(syscall.AF_CAN, syscall.SOCK_RAW, unix.CAN_RAW) 13 | addr := &unix.SockaddrCAN{Ifindex: i.Index} 14 | if err := unix.Bind(s, addr); err != nil { 15 | return nil, err 16 | } 17 | 18 | f := os.NewFile(uintptr(s), fmt.Sprintf("fd %d", s)) 19 | 20 | return &readWriteCloser{f}, nil 21 | } 22 | -------------------------------------------------------------------------------- /handler.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | // The Handler interfaces defines a method to receive a frame. 4 | type Handler interface { 5 | Handle(frame Frame) 6 | } 7 | 8 | // HandlerFunc defines the function type to handle a frame. 9 | type HandlerFunc func(frame Frame) 10 | 11 | type handler struct { 12 | fn HandlerFunc 13 | } 14 | 15 | // NewHandler returns a new handler which calls fn when a frame is received. 16 | func NewHandler(fn HandlerFunc) Handler { 17 | return &handler{fn} 18 | } 19 | 20 | func (h *handler) Handle(frame Frame) { 21 | h.fn(frame) 22 | } 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GOCMD=go 2 | GOBUILD=$(GOCMD) build 3 | GOCLEAN=$(GOCMD) clean 4 | GOTEST=$(GOCMD) test 5 | GOGET=$(GOCMD) get 6 | 7 | VERSION=$(shell git describe --exact-match --tags 2>/dev/null) 8 | BUILD_DIR=build 9 | PACKAGE_RPI=candump-$(VERSION)_linux_armhf 10 | 11 | # unset GOPATH to us Go modules 12 | unexport GOPATH 13 | 14 | test: 15 | $(GOTEST) -v ./... 16 | 17 | clean: 18 | $(GOCLEAN) 19 | rm -rf $(BUILD_DIR) 20 | 21 | package-rpi: build-rpi 22 | tar -cvzf $(PACKAGE_RPI).tar.gz -C $(BUILD_DIR) $(PACKAGE_RPI) 23 | 24 | build-rpi: 25 | GOOS=linux GOARCH=arm GOARM=6 $(GOBUILD) -o $(BUILD_DIR)/$(PACKAGE_RPI)/usr/bin/candump -i cmd/candump.go -------------------------------------------------------------------------------- /wait_test.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestWait(t *testing.T) { 9 | rwc := NewEchoReadWriteCloser() 10 | bus := NewBus(rwc) 11 | 12 | handler := newTestHandler() 13 | bus.Subscribe(handler) 14 | 15 | go func() { 16 | <-time.After(time.Millisecond * 50) 17 | bus.publish(testFrame) 18 | }() 19 | 20 | resp := <-Wait(bus, 0x5FAF, time.Millisecond*500) 21 | 22 | if x := resp.Err; x != nil { 23 | t.Fatal(x) 24 | } 25 | } 26 | 27 | func TestTimeoutErr(t *testing.T) { 28 | rwc := NewEchoReadWriteCloser() 29 | bus := NewBus(rwc) 30 | 31 | handler := newTestHandler() 32 | bus.Subscribe(handler) 33 | 34 | resp := <-Wait(bus, 0x5FAF, time.Millisecond*50) 35 | 36 | if x := resp.Err; x == nil { 37 | t.Fatal(x) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test_readwritecloser.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "time" 7 | ) 8 | 9 | type echoReadWriteCloser struct { 10 | buf bytes.Buffer 11 | closed bool 12 | } 13 | 14 | // NewEchoReadWriteCloser returns a ReadWriteCloser which echoes received bytes. 15 | func NewEchoReadWriteCloser() ReadWriteCloser { 16 | return NewReadWriteCloser(&echoReadWriteCloser{}) 17 | } 18 | 19 | func (rw *echoReadWriteCloser) Read(b []byte) (n int, err error) { 20 | for { 21 | if rw.buf.Len() > 0 { 22 | return rw.buf.Read(b) 23 | } 24 | 25 | if rw.closed == true { 26 | break 27 | } 28 | 29 | <-time.After(time.Millisecond * 1) 30 | } 31 | 32 | return 0, io.EOF 33 | } 34 | 35 | func (rw *echoReadWriteCloser) Write(b []byte) (n int, err error) { 36 | return rw.buf.Write(b) 37 | } 38 | 39 | func (rw *echoReadWriteCloser) Close() error { 40 | rw.closed = true 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /frame_test.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "log" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestFrame(t *testing.T) { 10 | f := Frame{ 11 | ID: 0x701, 12 | Length: 1, 13 | Flags: 0, 14 | Res0: 0, 15 | Res1: 0, 16 | Data: [8]uint8{0x05}, 17 | } 18 | b, err := Marshal(f) 19 | 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | if is, want := len(b), 16; is != want { 25 | log.Fatalf("is=%v want=%v", is, want) 26 | } 27 | 28 | if is, want := b, []uint8{0x7, 0x1, 0x1, 0x0, 0x0, 0x0, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; reflect.DeepEqual(is, want) { 29 | log.Fatalf("is=%v want=%v", is, want) 30 | } 31 | 32 | if err := Unmarshal(b, &f); err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | if is, want := f.ID, uint32(0x701); is != want { 37 | log.Fatalf("is=%v want=%v", is, want) 38 | } 39 | 40 | if is, want := f.Data, []uint8{0x05, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; reflect.DeepEqual(is, want) { 41 | log.Fatalf("is=%v want=%v", is, want) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /can.go: -------------------------------------------------------------------------------- 1 | // Package can provides an implemention of a CAN bus to send and receive CAN frames. 2 | package can 3 | 4 | const ( 5 | // MaskIDSff is used to extract the valid 11-bit CAN identifier bits from the frame ID of a standard frame format. 6 | MaskIDSff = 0x000007FF 7 | // MaskIDEff is used to extract the valid 29-bit CAN identifier bits from the frame ID of an extended frame format. 8 | MaskIDEff = 0x1FFFFFFF 9 | // MaskErr is used to extract the the error flag (0 = data frame, 1 = error message) from the frame ID. 10 | MaskErr = 0x20000000 11 | // MaskRtr is used to extract the rtr flag (1 = rtr frame) from the frame ID 12 | MaskRtr = 0x40000000 13 | // MaskEff is used to extract the eff flag (0 = standard frame, 1 = extended frame) from the frame ID 14 | MaskEff = 0x80000000 15 | ) 16 | 17 | const ( 18 | // MaxFrameDataLength defines the max length of a CAN data frame defined in ISO 11898-1. 19 | MaxFrameDataLength = 8 20 | // MaxExtFrameDataLength defines the max length of an CAN extended data frame defined in ISO ISO 11898-7. 21 | MaxExtFrameDataLength = 64 22 | ) 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Matthias Hochgatterer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /wait.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // A WaitResponse encapsulates the response of waiting for a frame. 9 | type WaitResponse struct { 10 | Frame Frame 11 | Err error 12 | } 13 | 14 | type waiter struct { 15 | id uint32 16 | wait chan WaitResponse 17 | bus *Bus 18 | filter Handler 19 | } 20 | 21 | // Wait returns a channel, which receives a frame or an error, if the 22 | // frame with the expected id didn't arrive on time. 23 | func Wait(bus *Bus, id uint32, timeout time.Duration) <-chan WaitResponse { 24 | waiter := waiter{ 25 | id: id, 26 | wait: make(chan WaitResponse), 27 | bus: bus, 28 | } 29 | 30 | ch := make(chan WaitResponse) 31 | 32 | go func() { 33 | select { 34 | case resp := <-waiter.wait: 35 | ch <- resp 36 | case <-time.After(timeout): 37 | err := fmt.Errorf("Timeout error waiting for %X", id) 38 | ch <- WaitResponse{Frame{}, err} 39 | } 40 | }() 41 | 42 | waiter.filter = newFilter(id, &waiter) 43 | bus.Subscribe(waiter.filter) 44 | 45 | return ch 46 | } 47 | 48 | func (w *waiter) Handle(frame Frame) { 49 | w.bus.Unsubscribe(w.filter) 50 | w.wait <- WaitResponse{frame, nil} 51 | } 52 | -------------------------------------------------------------------------------- /bus_test.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | var testFrame = Frame{ 10 | ID: 0x5FAF, 11 | Length: 0x8, 12 | Flags: 0x0, 13 | Res0: 0x0, 14 | Res1: 0x0, 15 | Data: [MaxFrameDataLength]uint8{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, 16 | } 17 | 18 | type testHandler struct { 19 | frame Frame 20 | } 21 | 22 | func newTestHandler() *testHandler { 23 | return &testHandler{} 24 | } 25 | 26 | func (h *testHandler) Handle(frame Frame) { 27 | h.frame = frame 28 | } 29 | 30 | func TestPublish(t *testing.T) { 31 | rwc := NewEchoReadWriteCloser() 32 | bus := NewBus(rwc) 33 | 34 | handler := newTestHandler() 35 | bus.Subscribe(handler) 36 | 37 | go bus.publishNextFrame() 38 | 39 | rwc.WriteFrame(testFrame) 40 | 41 | <-time.After(time.Millisecond * 50) 42 | 43 | if is, want := handler.frame, testFrame; reflect.DeepEqual(is, want) == false { 44 | t.Fatalf("is=% X want=% X", is, want) 45 | } 46 | } 47 | 48 | func TestSubscribe(t *testing.T) { 49 | rwc := NewEchoReadWriteCloser() 50 | bus := NewBus(rwc) 51 | 52 | handler := newTestHandler() 53 | bus.Subscribe(handler) 54 | bus.publish(testFrame) 55 | 56 | if is, want := handler.frame, testFrame; reflect.DeepEqual(is, want) == false { 57 | t.Fatalf("is=% X want=% X", is, want) 58 | } 59 | } 60 | 61 | func TestUnsubscribe(t *testing.T) { 62 | rwc := NewEchoReadWriteCloser() 63 | bus := NewBus(rwc) 64 | 65 | handler := newTestHandler() 66 | bus.Subscribe(handler) 67 | 68 | if x := bus.contains(handler); x != true { 69 | t.Fatal(x) 70 | } 71 | 72 | bus.Unsubscribe(handler) 73 | 74 | if x := bus.contains(handler); x != false { 75 | t.Fatal(x) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /readwritecloser.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // The Reader interface extends the `io.Reader` interface by method 8 | // to read a frame. 9 | type Reader interface { 10 | io.Reader 11 | ReadFrame(*Frame) error 12 | } 13 | 14 | // The Writer interface extends the `io.Writer` interface by method 15 | // to write a frame. 16 | type Writer interface { 17 | io.Writer 18 | WriteFrame(Frame) error 19 | } 20 | 21 | // The ReadWriteCloser interface combines the Reader and Writer and 22 | // `io.Closer` interface. 23 | type ReadWriteCloser interface { 24 | Reader 25 | Writer 26 | 27 | io.Closer 28 | } 29 | 30 | type readWriteCloser struct { 31 | rwc io.ReadWriteCloser 32 | } 33 | 34 | // NewReadWriteCloser returns a ReadWriteCloser for an `io.ReadWriteCloser`. 35 | func NewReadWriteCloser(rwc io.ReadWriteCloser) ReadWriteCloser { 36 | return &readWriteCloser{rwc} 37 | } 38 | 39 | func (rwc *readWriteCloser) ReadFrame(frame *Frame) error { 40 | b := make([]byte, 256) // TODO(brutella) optimize size 41 | n, err := rwc.Read(b) 42 | 43 | if err != nil { 44 | return err 45 | } 46 | 47 | err = Unmarshal(b[:n], frame) 48 | 49 | return err 50 | } 51 | 52 | func (rwc *readWriteCloser) WriteFrame(frame Frame) error { 53 | b, err := Marshal(frame) 54 | 55 | if err != nil { 56 | return err 57 | } 58 | 59 | _, err = rwc.Write(b) 60 | 61 | return err 62 | } 63 | 64 | func (rwc *readWriteCloser) Read(b []byte) (n int, err error) { 65 | return rwc.rwc.Read(b) 66 | } 67 | 68 | func (rwc *readWriteCloser) Write(b []byte) (n int, err error) { 69 | return rwc.rwc.Write(b) 70 | } 71 | 72 | func (rwc *readWriteCloser) Close() error { 73 | return rwc.rwc.Close() 74 | } 75 | -------------------------------------------------------------------------------- /frame.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | ) 7 | 8 | // Frame represents a standard CAN data frame 9 | type Frame struct { 10 | // bit 0-28: CAN identifier (11/29 bit) 11 | // bit 29: error message flag (ERR) 12 | // bit 30: remote transmision request (RTR) 13 | // bit 31: extended frame format (EFF) 14 | ID uint32 15 | Length uint8 16 | Flags uint8 17 | Res0 uint8 18 | Res1 uint8 19 | Data [MaxFrameDataLength]uint8 20 | } 21 | 22 | // Marshal returns the byte encoding of frm. 23 | func Marshal(frm Frame) (b []byte, err error) { 24 | wr := errWriter{ 25 | buf: bytes.NewBuffer([]byte{}), 26 | } 27 | wr.write(&frm.ID) 28 | wr.write(&frm.Length) 29 | wr.write(&frm.Flags) 30 | wr.write(&frm.Res0) 31 | wr.write(&frm.Res1) 32 | wr.write(&frm.Data) 33 | 34 | return wr.buf.Bytes(), wr.err 35 | } 36 | 37 | // Unmarshal parses the bytes b and stores the result in the value 38 | // pointed to by frm. 39 | func Unmarshal(b []byte, frm *Frame) (err error) { 40 | cr := &errReader{ 41 | buf: bytes.NewBuffer(b), 42 | } 43 | cr.read(&frm.ID) 44 | cr.read(&frm.Length) 45 | cr.read(&frm.Flags) 46 | cr.read(&frm.Res0) 47 | cr.read(&frm.Res1) 48 | cr.read(&frm.Data) 49 | 50 | return cr.err 51 | } 52 | 53 | type errReader struct { 54 | buf *bytes.Buffer 55 | err error 56 | } 57 | 58 | func (r *errReader) read(v interface{}) { 59 | if r.err == nil { 60 | r.err = binary.Read(r.buf, binary.LittleEndian, v) 61 | } 62 | } 63 | 64 | type errWriter struct { 65 | buf *bytes.Buffer 66 | err error 67 | } 68 | 69 | func (wr *errWriter) write(v interface{}) { 70 | if wr.err == nil { 71 | wr.err = binary.Write(wr.buf, binary.LittleEndian, v) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /cmd/candump.go: -------------------------------------------------------------------------------- 1 | // This program logs can frames to the console similar to candump from can-utils[1]. 2 | // 3 | // [1]: https://github.com/linux-can/can-utils 4 | package main 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "github.com/brutella/can" 10 | "log" 11 | "net" 12 | "os" 13 | "os/signal" 14 | ) 15 | 16 | var i = flag.String("if", "", "network interface name") 17 | 18 | func main() { 19 | flag.Parse() 20 | if len(*i) == 0 { 21 | flag.Usage() 22 | os.Exit(1) 23 | } 24 | 25 | iface, err := net.InterfaceByName(*i) 26 | 27 | if err != nil { 28 | log.Fatalf("Could not find network interface %s (%v)", *i, err) 29 | } 30 | 31 | conn, err := can.NewReadWriteCloserForInterface(iface) 32 | 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | 37 | bus := can.NewBus(conn) 38 | bus.SubscribeFunc(logCANFrame) 39 | 40 | c := make(chan os.Signal) 41 | signal.Notify(c, os.Interrupt) 42 | signal.Notify(c, os.Kill) 43 | 44 | go func() { 45 | select { 46 | case <-c: 47 | bus.Disconnect() 48 | os.Exit(1) 49 | } 50 | }() 51 | 52 | bus.ConnectAndPublish() 53 | } 54 | 55 | // logCANFrame logs a frame with the same format as candump from can-utils. 56 | func logCANFrame(frm can.Frame) { 57 | data := trimSuffix(frm.Data[:], 0x00) 58 | length := fmt.Sprintf("[%x]", frm.Length) 59 | log.Printf("%-3s %-4x %-3s % -24X '%s'\n", *i, frm.ID, length, data, printableString(data[:])) 60 | } 61 | 62 | // trim returns a subslice of s by slicing off all trailing b bytes. 63 | func trimSuffix(s []byte, b byte) []byte { 64 | for i := len(s) - 1; i >= 0; i-- { 65 | if s[i] != b { 66 | return s[:i+1] 67 | } 68 | } 69 | 70 | return []byte{} 71 | } 72 | 73 | // printableString creates a string from s and replaces non-printable bytes (i.e. 0-32, 127) 74 | // with '.' – similar how candump from can-utils does it. 75 | func printableString(s []byte) string { 76 | var ascii []byte 77 | for _, b := range s { 78 | if b < 32 || b > 126 { 79 | b = byte('.') 80 | 81 | } 82 | ascii = append(ascii, b) 83 | } 84 | 85 | return string(ascii) 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # can 2 | 3 | *can* provides an interface to a [CAN bus](https://www.kernel.org/doc/Documentation/networking/can.txt) to read and write frames. The library is based on the [SocketCAN](https://github.com/torvalds/linux/blob/097f70b3c4d84ffccca15195bdfde3a37c0a7c0f/include/uapi/linux/can.h) network stack on Linux. 4 | 5 | # Hardware 6 | 7 | I'm using a [Raspberry Pi 2 Model B](https://www.raspberrypi.org/products/raspberry-pi-2-model-b/) and [PiCAN2 CAN-Bus board for Raspberry Pi 2](http://skpang.co.uk/catalog/pican2-canbus-board-for-raspberry-pi-2-p-1475.html) to connect to a CAN bus. 8 | 9 | # Software 10 | 11 | The Raspberry Pi runs [Raspbian](https://www.raspberrypi.org/downloads/raspbian/). 12 | 13 | # Configuration 14 | 15 | Update `/boot/config.txt` with 16 | 17 | dtparam=spi=on 18 | dtoverlay=mcp2515-can0-overlay,oscillator=16000000,interrupt=25 19 | dtoverlay=spi-bcm2835-overlay 20 | 21 | and reboot. 22 | 23 | After phyiscally connecting to the CAN bus, you have to set up the can network interface for a specific bitrate, i.e. 50 kB 24 | 25 | sudo ip link set can0 up type can bitrate 50000 26 | 27 | Running `ifconfig` should now include the `can0` interface. 28 | 29 | #### Test your configuration 30 | 31 | You should test if you actually receive data from the CAN bus. You can either use the `candump` tool from the [can-utils](https://github.com/linux-can/can-utils) or a simple reimplementation under `cmd/candump.go`. 32 | 33 | Either way you will see something like this 34 | 35 | > go run $GOSRC/github.com/brutella/can/cmd/candump.go -if can0 36 | 37 | can0 100 [6] 20 83 0C 00 67 29 ' ...g)' 38 | can0 701 [1] 05 '.' 39 | 40 | ## Usage 41 | 42 | #### Setup the CAN bus 43 | 44 | ```go 45 | bus, _ := can.NewBusForInterfaceWithName("can0") 46 | bus.ConnectAndPublish() 47 | ``` 48 | 49 | #### Send a CAN frame 50 | 51 | ```go 52 | frm := can.Frame{ 53 | ID: 0x701, 54 | Length: 1, 55 | Flags: 0, 56 | Res0: 0, 57 | Res1: 0, 58 | Data: [8]uint8{0x05}, 59 | } 60 | 61 | bus.Publish(frm) 62 | ``` 63 | 64 | #### Receive a CAN frame 65 | 66 | ```go 67 | bus.SubscribeFunc(handleCANFrame) 68 | 69 | func handleCANFrame(frm can.Frame) { 70 | ... 71 | } 72 | ``` 73 | 74 | There is more to learn from the [documentation](http://godoc.org/github.com/brutella/can). 75 | 76 | # Contact 77 | 78 | Matthias Hochgatterer 79 | 80 | Github: [https://github.com/brutella](https://github.com/brutella/) 81 | 82 | Twitter: [https://twitter.com/brutella](https://twitter.com/brutella) 83 | 84 | 85 | # License 86 | 87 | *can* is available under the MIT license. See the LICENSE file for more info. -------------------------------------------------------------------------------- /bus.go: -------------------------------------------------------------------------------- 1 | package can 2 | 3 | import ( 4 | "io" 5 | "net" 6 | ) 7 | 8 | // Bus represents the CAN bus. 9 | // Handlers can subscribe to receive frames. 10 | // Frame are sent using the *Publish* method. 11 | type Bus struct { 12 | rwc ReadWriteCloser 13 | 14 | handler []Handler 15 | } 16 | 17 | // NewBusForInterfaceWithName returns a bus from the network interface with name ifaceName. 18 | func NewBusForInterfaceWithName(ifaceName string) (*Bus, error) { 19 | iface, err := net.InterfaceByName(ifaceName) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | conn, err := NewReadWriteCloserForInterface(iface) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | return NewBus(conn), nil 30 | } 31 | 32 | // NewBus returns a new CAN bus. 33 | func NewBus(rwc ReadWriteCloser) *Bus { 34 | return &Bus{ 35 | rwc: rwc, 36 | handler: make([]Handler, 0), 37 | } 38 | } 39 | 40 | // ConnectAndPublish starts handling CAN frames to publish them to handlers. 41 | func (b *Bus) ConnectAndPublish() error { 42 | for { 43 | err := b.publishNextFrame() 44 | if err != nil { 45 | return err 46 | } 47 | } 48 | 49 | return nil 50 | } 51 | 52 | // Disconnect stops handling CAN frames. 53 | func (b *Bus) Disconnect() error { 54 | return b.rwc.Close() 55 | } 56 | 57 | // Subscribe adds a handler to the bus. 58 | func (b *Bus) Subscribe(handler Handler) { 59 | b.handler = append(b.handler, handler) 60 | } 61 | 62 | // SubscribeFunc adds a function as handler. 63 | func (b *Bus) SubscribeFunc(fn HandlerFunc) { 64 | handler := NewHandler(fn) 65 | b.Subscribe(handler) 66 | } 67 | 68 | // Unsubscribe removes a handler. 69 | func (b *Bus) Unsubscribe(handler Handler) { 70 | for i, h := range b.handler { 71 | if h == handler { 72 | b.handler = append(b.handler[:i], b.handler[i+1:]...) 73 | return 74 | } 75 | } 76 | } 77 | 78 | // Publish publishes a frame on the bus. 79 | // 80 | // Frames publishes with the Publish methods are not received by handlers. 81 | func (b *Bus) Publish(frame Frame) error { 82 | return b.rwc.WriteFrame(frame) 83 | } 84 | 85 | func (b *Bus) contains(handler Handler) bool { 86 | for _, h := range b.handler { 87 | if h == handler { 88 | return true 89 | } 90 | } 91 | 92 | return false 93 | } 94 | 95 | func (b *Bus) publishNextFrame() error { 96 | frame := Frame{} 97 | err := b.rwc.ReadFrame(&frame) 98 | if err != nil { 99 | b.rwc.Close() 100 | 101 | if err != io.EOF { // EOF is not an error, it happens when calling rwc.Close() 102 | return err 103 | } 104 | 105 | return nil 106 | } 107 | 108 | b.publish(frame) 109 | 110 | return nil 111 | } 112 | 113 | func (b *Bus) publish(frame Frame) { 114 | for _, h := range b.handler { 115 | h.Handle(frame) 116 | } 117 | } 118 | --------------------------------------------------------------------------------