diff --git a/descriptor.go b/descriptor.go index b891008..ccc9f09 100644 --- a/descriptor.go +++ b/descriptor.go @@ -15,6 +15,7 @@ type Descriptor struct { I2CDriver func() I2CDriver LEDDriver func() LEDDriver SPIDriver func() SPIDriver + W1Driver func() W1Driver } // The Describer type is a Descriptor provider. diff --git a/host/rpi/onewirebus.go b/host/rpi/onewirebus.go new file mode 100644 index 0000000..b0426e1 --- /dev/null +++ b/host/rpi/onewirebus.go @@ -0,0 +1,188 @@ +// I²C support. + +package rpi + +import ( + "fmt" + "os" + "sync" + + "github.com/golang/glog" + "github.com/kidoman/embd" + "io/ioutil" +) + +type w1Bus struct { + l byte + busMap map[string]embd.W1Device + Mu sync.Mutex + + initialized bool +} + +type w1Device struct { + file *os.File + addr string + initialized bool + bus *w1Bus +} + +func NewW1Bus(l byte) embd.W1Bus { + return &w1Bus{l: l, busMap: make(map[string]embd.W1Device)} +} + +func (b *w1Bus) init() error { + if b.initialized { + return nil + } + + var err error + if _, err = os.Stat("/sys/bus/w1"); os.IsNotExist(err) { + return err + } + + glog.V(2).Infof("onewire: bus %v initialized", b.l) + + b.initialized = true + + return nil +} + +func (d *w1Device) init() error { + if d.initialized { + return nil + } + + var err error + if d.file, err = os.OpenFile(fmt.Sprintf("/sys/bus/w1/devices/%s/rw", d.addr), os.O_RDWR, os.ModeExclusive); err != nil { + return err + } + + glog.V(2).Infof("onewire: device %s initialized", d.addr) + + d.initialized = true + + return nil +} + +func (d *w1Device) ReadByte() (byte, error) { + d.bus.Mu.Lock() + defer d.bus.Mu.Unlock() + + if err := d.init(); err != nil { + return 0, err + } + + bytes := make([]byte, 1) + n, _ := d.file.Read(bytes) + + if n != 1 { + return 0, fmt.Errorf("onewire: Unexpected number (%v) of bytes read in ReadByte", n) + } + + return bytes[0], nil +} + +func (d *w1Device) WriteByte(value byte) error { + d.bus.Mu.Lock() + defer d.bus.Mu.Unlock() + + if err := d.init(); err != nil { + return err + } + + n, err := d.file.Write([]byte{value}) + + if n != 1 { + err = fmt.Errorf("onewire: Unexpected number (%v) of bytes written in WriteByte", n) + } + + return err +} + +func (d *w1Device) WriteBytes(value []byte) error { + d.bus.Mu.Lock() + defer d.bus.Mu.Unlock() + + if err := d.init(); err != nil { + return err + } + + for i := range value { + n, err := d.file.Write([]byte{value[i]}) + + if n != 1 { + return fmt.Errorf("onewire: Unexpected number (%v) of bytes written in WriteBytes", n) + } + if err != nil { + return err + } + } + + return nil +} + +func (d *w1Device) ReadBytes(number int) (value []byte, err error) { + d.bus.Mu.Lock() + defer d.bus.Mu.Unlock() + + if err := d.init(); err != nil { + return nil, err + } + + bytes := make([]byte, number) + n, _ := d.file.Read(bytes) + + if n != number { + return nil, fmt.Errorf("onewire: Unexpected number (%v) of bytes read in ReadBytes", n) + } + + return bytes, nil +} + +func (b *w1Bus) ListDevices() (devices []string, err error) { + dir, err := ioutil.ReadDir("/sys/bus/w1/devices/") + if err != nil { + return nil, err + } + devs := make([]string, len(dir)) + + for index, element := range dir { + devs[index] = element.Name() + } + + return devs, nil +} + +func (b *w1Bus) Open(address string) (device embd.W1Device, err error) { + b.Mu.Lock() + defer b.Mu.Unlock() + + if d, ok := b.busMap[address]; ok { + return d, nil + } + + d := &w1Device{addr: address, bus: b} + b.busMap[address] = d + return d, nil +} + +func (b *w1Bus) Close() error { + b.Mu.Lock() + defer b.Mu.Unlock() + + for _, b := range b.busMap { + b.Close() + } + + return nil +} + +func (d *w1Device) Close() error { + + if !d.initialized { + return nil + } + + return d.file.Close() +} diff --git a/host/rpi/rpi.go b/host/rpi/rpi.go index 9279ca8..dc34c45 100644 --- a/host/rpi/rpi.go +++ b/host/rpi/rpi.go @@ -5,6 +5,8 @@ GPIO (digital (rw)) I²C LED + W1 - make sure that w1-gpio kernel module is loaded. If you wish to use it with sensors directly (e.g. DS18B20 etc) + make sure to disable respective kernel modules (e.g. w1-therm) in /etc/modprobe.d/blacklist.conf. */ package rpi @@ -97,6 +99,9 @@ func init() { SPIDriver: func() embd.SPIDriver { return embd.NewSPIDriver(spiDeviceMinor, generic.NewSPIBus, nil) }, + W1Driver: func() embd.W1Driver { + return embd.NewW1Driver(NewW1Bus) + }, } }) } diff --git a/onewire.go b/onewire.go new file mode 100644 index 0000000..9907a78 --- /dev/null +++ b/onewire.go @@ -0,0 +1,78 @@ +//OneWire support. + +package embd + +// W1Bus interface is used to interact with the OneWire bus. +type W1Bus interface { + + // List devices on the bus + ListDevices() (devices []string, err error) + + // Open a device + Open(address string) (device W1Device, err error) + + // Close releases the resources associated with the bus. + Close() error +} + +// W1Device interface is user to interact with the OneWire device. +type W1Device interface { + // ReadByte reads a byte from the device. + ReadByte() (value byte, err error) + // ReadByte number of bytes from the device. + ReadBytes(number int) (value []byte, err error) + // WriteByte writes a byte to the device. + WriteByte(value byte) error + // WriteBytes writes a slice bytes to the device. + WriteBytes(value []byte) error + + // Close releases the resources associated with the device. + Close() error +} + +// W1Driver interface interacts with the host descriptors to allow us +// control of OneWire communication. +type W1Driver interface { + Bus(l byte) W1Bus + + // Close releases the resources associated with the driver. + Close() error +} + +var w1DriverInitialized bool +var w1DriverInstance W1Driver + +// InitW1 initializes the W1 driver. +func InitW1() error { + if w1DriverInitialized { + return nil + } + + desc, err := DescribeHost() + if err != nil { + return err + } + + if desc.W1Driver == nil { + return ErrFeatureNotSupported + } + + w1DriverInstance = desc.W1Driver() + w1DriverInitialized = true + + return nil +} + +// CloseW1 releases resources associated with the OneWire driver. +func CloseW1() error { + return w1DriverInstance.Close() +} + +// NewW1Bus returns a W1Bus. +func NewW1Bus(l byte) W1Bus { + if err := InitW1(); err != nil { + panic(err) + } + + return w1DriverInstance.Bus(l) +} diff --git a/onewiredriver.go b/onewiredriver.go new file mode 100644 index 0000000..38ed5e8 --- /dev/null +++ b/onewiredriver.go @@ -0,0 +1,44 @@ +// Generic OneWire driver. + +package embd + +import "sync" + +type w1BusFactory func(byte) W1Bus + +type w1Driver struct { + busMap map[byte]W1Bus + busMapLock sync.Mutex + + ibf w1BusFactory +} + +// NewW1Driver returns a W1Driver interface which allows control +// over the OneWire subsystem. +func NewW1Driver(ibf w1BusFactory) W1Driver { + return &w1Driver{ + busMap: make(map[byte]W1Bus), + ibf: ibf, + } +} + +func (i *w1Driver) Bus(l byte) W1Bus { + i.busMapLock.Lock() + defer i.busMapLock.Unlock() + + if b, ok := i.busMap[l]; ok { + return b + } + + b := i.ibf(l) + i.busMap[l] = b + return b +} + +func (i *w1Driver) Close() error { + for _, b := range i.busMap { + b.Close() + } + + return nil +} diff --git a/samples/onewire.go b/samples/onewire.go new file mode 100644 index 0000000..f6a62b6 --- /dev/null +++ b/samples/onewire.go @@ -0,0 +1,72 @@ +// +build ignore + +package main + +import ( + "fmt" + + "github.com/kidoman/embd" + _ "github.com/kidoman/embd/host/all" +) + +func main() { + if err := embd.InitW1(); err != nil { + panic(err) + } + defer embd.CloseW1() + + w1 := embd.NewW1Bus(0) + + devs, err := w1.ListDevices() + + if err != nil { + panic(err) + } + + for _, dev := range devs { + fmt.Printf("OneWire device: %s\n", dev) + } + + w1d, err := w1.Open("28-011572120bff") + + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", w1d) + + err = w1d.WriteByte(0x44) + + if err != nil { + panic(err) + } + + for ret, err := w1d.ReadByte(); ret == 0 && err != nil; {} + + if err != nil { + panic(err) + } + + err = w1d.WriteByte(0xBE) + + if err != nil { + panic(err) + } + + res, err := w1d.ReadBytes(9) + + if err != nil { + panic(err) + } + + fmt.Print("res: ") + for _, val := range res { + fmt.Printf("0x%02X ", val) + } + fmt.Println() + + var temp float64 = float64(float64(res[1]) * 256. + float64(res[0])) / 16. + fmt.Printf("%f\n", temp) + + fmt.Println("Done") +}