diff --git a/experimental/devices/ds18b20/ds18b20.go b/experimental/devices/ds18b20/ds18b20.go new file mode 100644 index 0000000..6d63bcb --- /dev/null +++ b/experimental/devices/ds18b20/ds18b20.go @@ -0,0 +1,165 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package ds18b20 interfaces to Dallas Semi / Maxim DS18B20 and MAX31820 +// 1-wire temperature sensors. +// +// Note that both DS18B20 and MAX31820 use family code 0x28. +// +// Both powered sensors and parasitically powered sensors are supported +// as long as the bus driver can provide sufficient power using an active +// pull-up. +// +// The DS18B20 alarm functionality and reading/writing the 2 alarm bytes in +// the EEPROM are not supported. The DS18S20 is also not supported. +// +// Datasheets +// +// https://datasheets.maximintegrated.com/en/ds/DS18B20-PAR.pdf +// +// http://datasheets.maximintegrated.com/en/ds/MAX31820.pdf +package ds18b20 + +import ( + "errors" + "time" + + "github.com/google/periph/devices" + "github.com/google/periph/experimental/conn/onewire" +) + +// New returns an object that communicates over 1-wire to the DS18B20 sensor with the +// specified 64-bit address. +// +// resolutionBits must be in the range 9..12 and determines how many bits of precision +// the readings have. The resolution affects the conversion time: 9bits:94ms, 10bits:188ms, +// 11bits:375ms, 12bits:750ms. +// +// A resolution of 10 bits corresponds to 0.25C and tends to be a good compromise between +// conversion time and the device's inherent accuracy of +/-0.5C. +func New(o onewire.Bus, addr onewire.Address, resolutionBits int) (*Dev, error) { + if resolutionBits < 9 || resolutionBits > 12 { + return nil, errors.New("ds18b20: invalid resolutionBits") + } + + d := &Dev{onewire: onewire.Dev{Bus: o, Addr: addr}, resolution: resolutionBits} + + // Start by reading the scratchpad memory, this will tell us whether we can talk to the + // device correctly and also how it's configured. + spad, err := d.readScratchpad() + if err != nil { + return nil, err + } + + // Change the resolution, if necessary (datasheet p.6). + if int(spad[4]>>5) != resolutionBits-9 { + // Set the value in the configuration register. + d.onewire.Tx([]byte{0x4e, 0, 0, byte((resolutionBits-9)<<5) | 0x1f}, nil) + // Copy the scratchpad to EEPROM to save the values. + d.onewire.TxPower([]byte{0x48}, nil) + // Wait for the write to complete + time.Sleep(10 * time.Millisecond) + } + + return d, nil +} + +// ConvertAll performs a conversion on all DS18B20 devices on the bus. +// +// During the conversion it places the bus in strong pull-up mode to +// power parasitic devices and returns when the conversions have +// completed. This time period is determined by the maximum +// resolution of all devices on the bus and must be provided. +// +// ConvertAll uses time.Sleep to wait for the conversion to finish, +// which takes from 94ms to 752ms. +func ConvertAll(o onewire.Bus, maxResolutionBits int) error { + if maxResolutionBits < 9 || maxResolutionBits > 12 { + return errors.New("ds18b20: invalid maxResolutionBits") + } + if err := o.Tx([]byte{0xcc, 0x44}, nil, onewire.StrongPullup); err != nil { + return err + } + conversionSleep(maxResolutionBits) + return nil +} + +//===== Dev + +// Dev is a handle to a Dallas Semi / Maxim DS18B20 temperature sensor on a 1-wire bus. +type Dev struct { + onewire onewire.Dev // device on 1-wire bus + resolution int // resolution in bits (9..12) +} + +// Temperature performs a conversion and returns the temperature. +func (d *Dev) Temperature() (devices.Celsius, error) { + if err := d.onewire.TxPower([]byte{0x44}, nil); err != nil { + return 0, err + } + conversionSleep(d.resolution) + return d.LastTemp() +} + +// LastTemp reads the temperature resulting from the last conversion from the device. +// It is useful in combination with ConvertAll. +func (d *Dev) LastTemp() (devices.Celsius, error) { + // Read the scratchpad memory. + spad, err := d.readScratchpad() + if err != nil { + return 0, err + } + + // spad[1] is MSB, spad[0] is LSB and has 4 fractional bits. Need to do sign extension + // multiply by 1000 to get devices.Millis, divide by 16 due to 4 fractional bits. + // Datasheet p.4. + c := (devices.Celsius(int8(spad[1]))<<8 + devices.Celsius(spad[0])) * 1000 / 16 + + // The device powers up with a value of 85°C, so if we read that odds are very high + // that either no conversion was performed or that the conversion failed due to lack of + // power. This prevents reading a temp of exactly 85°C, but that seems like the right + // tradeoff. + if c == 85000 { + return 0, busError("ds18b20: has not performed a temperature conversion (insufficient pull-up?)") + } + + return c, nil +} + +// + +// busError implements error and onewire.BusError. +type busError string + +func (e busError) Error() string { return string(e) } +func (e busError) BusError() bool { return true } + +// conversionSleep sleeps for the time a conversion takes, which depends +// on the resolution: +// 9bits:94ms, 10bits:188ms, 11bits:376ms, 12bits:752ms, datasheet p.6. +func conversionSleep(bits int) { + time.Sleep((94 << uint(bits-9)) * time.Millisecond) +} + +// readScratchpad reads the 9 bytes of scratchpad and checks the CRC. +// It returns the 8 bytes of scratchpad data (excluding the CRC byte). +func (d *Dev) readScratchpad() ([]byte, error) { + // Read the scratchpad memory. + var spad [9]byte + if err := d.onewire.Tx([]byte{0xbe}, spad[:]); err != nil { + return nil, err + } + + // Check the scratchpad CRC. + if !onewire.CheckCRC(spad[:]) { + for _, s := range spad { + if s != 0xff { + return nil, busError("ds18b20: incorrect scratchpad CRC") + } + } + return nil, busError("ds18b20: device did not respond") + } + + return spad[:8], nil +} diff --git a/experimental/devices/ds18b20/ds18b20_test.go b/experimental/devices/ds18b20/ds18b20_test.go new file mode 100644 index 0000000..cef656e --- /dev/null +++ b/experimental/devices/ds18b20/ds18b20_test.go @@ -0,0 +1,153 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package ds18b20 + +import ( + "os" + "testing" + "time" + + "github.com/google/periph/devices" + "github.com/google/periph/experimental/conn/onewire" + "github.com/google/periph/experimental/conn/onewire/onewiretest" + "github.com/google/periph/host" +) + +// TestMain lets periph load all drivers and then runs the tests. +func TestMain(m *testing.M) { + host.Init() + os.Exit(m.Run()) +} + +// TestTemperature tests a temperature conversion on a ds18b20 using +// recorded bus transactions. +func TestTemperature(t *testing.T) { + // set-up playback using the recording output. + ops := []onewiretest.IO{ + // Match ROM + Read Scratchpad (init) + {Write: []uint8{0x55, 0x28, 0xac, 0x41, 0xe, 0x7, 0x0, 0x0, 0x74, 0xbe}, + Read: []uint8{0xe0, 0x1, 0x0, 0x0, 0x3f, 0xff, 0x10, 0x10, 0x3f}, Pull: false}, + // Match ROM + Convert + {Write: []uint8{0x55, 0x28, 0xac, 0x41, 0xe, 0x7, 0x0, 0x0, 0x74, 0x44}, + Read: []uint8(nil), Pull: true}, + // Match ROM + Read Scratchpad (read temp) + {Write: []uint8{0x55, 0x28, 0xac, 0x41, 0xe, 0x7, 0x0, 0x0, 0x74, 0xbe}, + Read: []uint8{0xe0, 0x1, 0x0, 0x0, 0x3f, 0xff, 0x10, 0x10, 0x3f}, Pull: false}, + } + var addr onewire.Address = 0x740000070e41ac28 + var temp devices.Celsius = 30000 // 30.000°C + owBus := &onewiretest.Playback{Ops: ops} + // Init the ds18b20. + ds18b20, err := New(owBus, addr, 10) + if err != nil { + t.Fatal(err) + } + // Read the temperature. + t0 := time.Now() + now, err := ds18b20.Temperature() + dt := time.Since(t0) + if err != nil { + t.Fatal(err) + } + // Expect the correct value. + if now != temp { + t.Errorf("expected %s, got %s", temp.String(), now.String()) + } + // Expect it to take >187ms + if dt < 188*time.Millisecond { + t.Errorf("expected conversion to take >187ms, took %s", dt) + } +} + +// TestConvertAll tests a temperature conversion on all ds18b20 using +// recorded bus transactions. +func TestConvertAll(t *testing.T) { + // set-up playback using the recording output. + ops := []onewiretest.IO{ + // Skip ROM + Convert + {Write: []uint8{0xcc, 0x44}, Read: []uint8(nil), Pull: true}, + } + owBus := &onewiretest.Playback{Ops: ops} + // Perform the conversion + t0 := time.Now() + if err := ConvertAll(owBus, 9); err != nil { + t.Fatal(err) + } + // Expect it to take >93ms + if dt := time.Since(t0); dt < 94*time.Millisecond { + t.Errorf("expected conversion to take >93ms, took %s", dt) + } +} + +/* Commented out in order not to import periph/host, need to move to smoke test +// TestRecordTemp tests and records a temperature conversion. It outputs +// the recording if the tests are run with the verbose option. +// +// This test is skipped unless the -record flag is passed to the test executable. +// Use either `go test -args -record` or `ds18b20.test -test.v -record`. +func TestRecordTemp(t *testing.T) { + // Only proceed to init hardware and test if -record flag is passed + if !*record { + t.SkipNow() + } + host.Init() + + i2cBus, err := i2c.New(-1) + if err != nil { + t.Fatal(err) + } + owBus, err := ds248x.New(i2cBus, nil) + if err != nil { + t.Fatal(err) + } + devices, err := owBus.Search(false) + if err != nil { + t.Fatal(err) + } + addrs := "1-wire devices found:" + for _, a := range devices { + addrs += fmt.Sprintf(" %#016x", a) + } + t.Log(addrs) + // See whether there's a ds18b20 on the bus. + var addr onewire.Address + for _, a := range devices { + if a&0xff == 0x28 { + addr = a + break + } + } + if addr == 0 { + t.Fatal("no DS18B20 found") + } + t.Logf("var addr onewire.Address = %#016x", addr) + // Start recording and perform a temperature conversion. + rec := &onewiretest.Record{Bus: owBus} + time.Sleep(50 * time.Millisecond) + ds18b20, err := New(rec, addr, 10) + if err != nil { + t.Fatalf("ds18b20 init: %s", err) + } + temp, err := ds18b20.Temperature() + if err != nil { + t.Fatal(err) + } + // Output what got recorded. + t.Log("var ops = []onewiretest.IO{") + for _, op := range rec.Ops { + t.Logf(" %#v,", op) + } + t.Log("}") + t.Logf("var temp devices.Celsius = %d // %s", temp, temp.String()) +} + +// + +var record *bool + +func init() { + record = flag.Bool("record", false, "record real hardware accesses") +} +*/ diff --git a/experimental/devices/ds248x/dev.go b/experimental/devices/ds248x/dev.go new file mode 100644 index 0000000..fa44ef5 --- /dev/null +++ b/experimental/devices/ds248x/dev.go @@ -0,0 +1,186 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package ds248x + +import ( + "fmt" + "sync" + "time" + + "github.com/google/periph/conn" + "github.com/google/periph/experimental/conn/onewire" +) + +// Dev is a handle to a ds248x device and it implements the onewire.Bus interface. +// +// Dev implements a persistent error model: if a fatal error is encountered it places +// itself into an error state and immediately returns the last error on all subsequent +// calls. A fresh Dev, which reinitializes the hardware, must be created to proceed. +// +// A persistent error is only set when there is a problem with the ds248x device itself +// (or the I²C bus used to access it). Errors on the 1-wire bus do not cause persistent +// errors and implement the onewire.BusError interface to indicate this fact. +type Dev struct { + sync.Mutex // lock for the bus while a transaction is in progress + i2c conn.Conn // i2c device handle for the ds248x + isDS2483 bool // true: ds2483, false: ds2482-100 + confReg byte // value written to configuration register + tReset time.Duration // time to perform a 1-wire reset + tSlot time.Duration // time to perform a 1-bit 1-wire read/write + err error // persistent error, device will no longer operate +} + +// String +func (d *Dev) String() string { + return fmt.Sprintf("ds248x") +} + +// Tx performs a bus transaction, sending and receiving bytes, and +// ending by pulling the bus high either weakly or strongly depending +// on the value of power. +// +// A strong pull-up is typically required to power temperature conversion +// or EEPROM writes. +func (d *Dev) Tx(w, r []byte, power onewire.Pullup) error { + d.Lock() + defer d.Unlock() + + // Issue 1-wire bus reset. + if present, err := d.reset(); err != nil { + return err + } else if !present { + return busError("ds248x: no device present") + } + + // Send bytes onto 1-wire bus. + for i, b := range w { + if power == onewire.StrongPullup && i == len(w)-1 && len(r) == 0 { + // This is the last byte, need to activate strong pull-up. + d.i2cTx([]byte{cmdWriteConfig, d.confReg&0xbf | 0x4}, nil) + } + d.i2cTx([]byte{cmd1WWrite, b}, nil) + d.waitIdle(7 * d.tSlot) + } + + // Read bytes from one-wire bus. + for i, _ := range r { + if power == onewire.StrongPullup && i == len(r)-1 { + // This is the last byte, need to activate strong-pull-up + d.i2cTx([]byte{cmdWriteConfig, d.confReg&0xbf | 0x4}, nil) + } + d.i2cTx([]byte{cmd1WRead}, r[i:i+1]) + d.waitIdle(7 * d.tSlot) + d.i2cTx([]byte{cmdSetReadPtr, regRDR}, r[i:i+1]) + } + + return d.err +} + +// Search performs a "search" cycle on the 1-wire bus and returns the +// addresses of all devices on the bus if alarmOnly is false and of all +// devices in alarm state if alarmOnly is true. +// +// If an error occurs during the search the already-discovered devices are +// returned with the error. +func (d *Dev) Search(alarmOnly bool) ([]onewire.Address, error) { + return onewire.Search(d, alarmOnly) +} + +// SearchTriplet performs a single bit search triplet command on the bus, +// waits for it to complete and returs the outcome. +// +// SearchTriplet should not be used directly, use Search instead. +func (d *Dev) SearchTriplet(direction byte) (onewire.TripletResult, error) { + // Send one-wire triplet command. + var dir byte + if direction != 0 { + dir = 0x80 + } + d.i2cTx([]byte{cmd1WTriplet, dir}, nil) + // Wait and read status register, concoct result from there. + status := d.waitIdle(0 * d.tSlot) // in theory 3*tSlot but it's actually overlapped + tr := onewire.TripletResult{ + GotZero: status&0x20 == 0, + GotOne: status&0x40 == 0, + Taken: status >> 7, + } + return tr, d.err +} + +// + +// reset issues a reset signal on the 1-wire bus and returns true if any device +// responded with a presence pulse. +func (d *Dev) reset() (bool, error) { + // Issue reset. + d.i2cTx([]byte{cmd1WReset}, nil) + + // Wait for reset to complete. + status := d.waitIdle(d.tReset) + if d.err != nil { + return false, d.err + } + // Detect bus short and turn into 1-wire error + if (status & 4) != 0 { + return false, shortedBusError("onewire/ds248x: bus has a short") + } + return (status & 2) != 0, nil +} + +// i2cTx is a helper function to call i2c.Tx and handle the error by persisting it. +func (d *Dev) i2cTx(w, r []byte) { + if d.err != nil { + return + } + d.err = d.i2c.Tx(w, r) +} + +// waitIdle waits for the one wire bus to be idle. +// +// It initially sleeps for the delay and then polls the status register and +// sleeps for a tenth of the delay each time the status register indicates +// that the bus is still busy. The last read status byte is returned. +// +// An overall timeout of 3ms is applied to the whole procedure. waitIdle +// uses the persistent error model and returns 0 if there is an error. +func (d *Dev) waitIdle(delay time.Duration) byte { + if d.err != nil { + return 0 + } + // Overall timeout. + tOut := time.Now().Add(3 * time.Millisecond) + time.Sleep(delay) + for { + // Read status register. + var status [1]byte + d.i2cTx(nil, status[:]) + // If bus idle complete, return status. This also returns if d.err!=nil + // because in that case status[0]==0. + if (status[0] & 1) == 0 { + return status[0] + } + // If we're timing out return error. This is an error with the ds248x, not with + // devices on the 1-wire bus, hence it is persistent. + if time.Now().After(tOut) { + d.err = fmt.Errorf("ds248x: timeout waiting for bus cycle to finish") + return 0 + } + // Try not to hog the kernel thread. + time.Sleep(delay / 10) + } +} + +// shortedBusError implements error and onewire.ShortedBusError. +type shortedBusError string + +func (e shortedBusError) Error() string { return string(e) } +func (e shortedBusError) IsShorted() bool { return true } +func (e shortedBusError) BusError() bool { return true } + +// busError implements error and onewire.BusError. +type busError string + +func (e busError) Error() string { return string(e) } +func (e busError) BusError() bool { return true } diff --git a/experimental/devices/ds248x/ds248x.go b/experimental/devices/ds248x/ds248x.go new file mode 100644 index 0000000..b072edc --- /dev/null +++ b/experimental/devices/ds248x/ds248x.go @@ -0,0 +1,166 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package ds248x controls a Maxim DS2483 or DS2482-100 1-wire interface chip over I²C. +// +// Datasheets +// +// https://www.maximintegrated.com/en/products/digital/one-wire/DS2483.html +// +// https://www.maximintegrated.com/en/products/interface/controllers-expanders/DS2482-100.html +package ds248x + +import ( + "errors" + "fmt" + "time" + + "github.com/google/periph/conn/i2c" +) + +// PupOhm controls the strength of the passive pull-up resistor +// on the 1-wire data line. The default value is 1000Ω. +type PupOhm uint8 + +const ( + R500Ω = 4 // 500Ω passive pull-up resistor + R1000Ω = 6 // 1000Ω passive pull-up resistor +) + +// Opts contains options to pass to the constructor. +type Opts struct { + Addr uint16 // I²C address, default 0x18 + PassivePullup bool // false:use active pull-up, true: disable active pullup + + // The following options are only available on the ds2483 (not ds2482-100). + // The actual value used is the closest possible value (rounded up or down). + ResetLow time.Duration // reset low time, range 440μs..740μs + PresenceDetect time.Duration // presence detect sample time, range 58μs..76μs + Write0Low time.Duration // write zero low time, range 52μs..70μs + Write0Recovery time.Duration // write zero recovery time, range 2750ns..25250ns + PullupRes PupOhm // passive pull-up resistance, true: 500Ω, false: 1kΩ +} + +// New returns an device object that communicates over I²C to the DS2482/DS2483 controller. +func New(i i2c.Bus, opts *Opts) (*Dev, error) { + addr := uint16(0x18) + if opts != nil { + switch opts.Addr { + case 0x18, 0x19, 0x20, 0x21: + addr = opts.Addr + case 0x00: + default: + return nil, errors.New("ds248x: given address not supported by device") + } + } + d := &Dev{i2c: &i2c.Dev{Bus: i, Addr: addr}} + if err := d.makeDev(opts); err != nil { + return nil, err + } + return d, nil +} + +// + +// defaults holds default values for optional parameters. +var defaults = Opts{ + PassivePullup: false, + ResetLow: 560 * time.Microsecond, + PresenceDetect: 68 * time.Microsecond, + Write0Low: 64 * time.Microsecond, + Write0Recovery: 5250 * time.Nanosecond, + PullupRes: R1000Ω, +} + +func (d *Dev) makeDev(opts *Opts) error { + // Doctor the opts to apply default values. + if opts == nil { + opts = &defaults + } + if opts.ResetLow == 0 { + opts.ResetLow = defaults.ResetLow + } + if opts.PresenceDetect == 0 { + opts.PresenceDetect = defaults.PresenceDetect + } + if opts.Write0Low == 0 { + opts.Write0Low = defaults.Write0Low + } + if opts.Write0Recovery == 0 { + opts.Write0Recovery = defaults.Write0Recovery + } + if opts.PullupRes == 0 { + opts.PullupRes = defaults.PullupRes + } + d.tReset = 2 * opts.ResetLow + d.tSlot = opts.Write0Low + opts.Write0Recovery + + // Issue a reset command. + if err := d.i2c.Tx([]byte{cmdReset}, nil); err != nil { + return fmt.Errorf("ds248x: error while resetting: %s", err) + } + + // Read the status register to confirm that we have a responding ds248x + var stat [1]byte + if err := d.i2c.Tx([]byte{cmdSetReadPtr, regStatus}, stat[:]); err != nil { + return fmt.Errorf("ds248x: error while reading status register: %s", err) + } + if stat[0] != 0x18 { + return fmt.Errorf("ds248x: invalid status register value: %#x, expected 0x18\n", stat[0]) + } + + // Write the device configuration register to get the chip out of reset state, immediately + // read it back to get confirmation. + d.confReg = 0xe1 // standard-speed, no strong pullup, no powerdown, active pull-up + if opts.PassivePullup { + d.confReg ^= 0x11 + } + var dcr [1]byte + if err := d.i2c.Tx([]byte{cmdWriteConfig, d.confReg}, dcr[:]); err != nil { + return fmt.Errorf("ds248x: error while writing device config register: %s", err) + } + // When reading back we only get the bottom nibble + if dcr[0] != d.confReg&0x0f { + return fmt.Errorf("ds248x: failure to write device config register, wrote %#x got %#x back", + d.confReg, dcr[0]) + } + + // Set the read ptr to the port configuration register to determine whether we have a + // ds2483 vs ds2482-100. This will fail on devices that do not have a port config + // register, such as the ds2482-100. + d.isDS2483 = d.i2c.Tx([]byte{cmdSetReadPtr, regPCR}, nil) == nil + + // Set the options for the ds2483. + if d.isDS2483 { + buf := []byte{cmdAdjPort, + byte(0x00 + ((opts.ResetLow/time.Microsecond - 430) / 20 & 0x0f)), + byte(0x20 + ((opts.PresenceDetect/time.Microsecond - 55) / 2 & 0x0f)), + byte(0x40 + ((opts.Write0Low/time.Microsecond - 51) / 2 & 0x0f)), + byte(0x60 + (((opts.Write0Recovery-1250)/2500 + 5) & 0x0f)), + byte(0x80 + (opts.PullupRes & 0x0f)), + } + if err := d.i2c.Tx(buf, nil); err != nil { + return fmt.Errorf("ds248x: error while setting port config values: %s", err) + } + } + + return nil +} + +const ( + cmdReset = 0xf0 // reset ds248x + cmdSetReadPtr = 0xe1 // set the read pointer + cmdWriteConfig = 0xd2 // write the device configuration + cmdAdjPort = 0xc3 // adjust 1-wire port + cmd1WReset = 0xb4 // reset the 1-wire bus + cmd1WBit = 0x87 // perform a single-bit transaction on the 1-wire bus + cmd1WWrite = 0xa5 // perform a byte write on the 1-wire bus + cmd1WRead = 0x96 // perform a byte read on the 1-wire bus + cmd1WTriplet = 0x78 // perform a triplet operation (2 bit reads, a bit write) + + regDCR = 0xc3 // read ptr for device configuration register + regStatus = 0xf0 // read ptr for status register + regRDR = 0xe1 // read ptr for read-data register + regPCR = 0xb4 // read ptr for port configuration register +) diff --git a/experimental/devices/ds248x/ds248x_test.go b/experimental/devices/ds248x/ds248x_test.go new file mode 100644 index 0000000..cf32740 --- /dev/null +++ b/experimental/devices/ds248x/ds248x_test.go @@ -0,0 +1,106 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package ds248x + +import ( + "fmt" + "testing" + + "github.com/google/periph/conn/i2c" + "github.com/google/periph/conn/i2c/i2ctest" +) + +// TestInit tests the initialization of a ds2483 using a recording. +func TestInit(t *testing.T) { + var ops = []i2ctest.IO{ + {Addr: 0x18, Write: []byte{0xf0}, Read: []byte(nil)}, + {Addr: 0x18, Write: []byte{0xe1, 0xf0}, Read: []byte{0x18}}, + {Addr: 0x18, Write: []byte{0xd2, 0xe1}, Read: []byte{0x1}}, + {Addr: 0x18, Write: []byte{0xe1, 0xb4}, Read: []byte(nil)}, + {Addr: 0x18, Write: []byte{0xc3, 0x6, 0x26, 0x46, 0x66, 0x86}, Read: []byte(nil)}, + {Addr: 0x18, Write: []byte{0x78, 0x0}, Read: []byte(nil)}, + {Addr: 0x18, Write: []byte{}, Read: []byte{0xe8}}, + } + + bus := &i2ctest.Playback{Ops: ops} + if _, err := New(bus, nil); err != nil { + t.Fatal(err) + } +} + +func Example() { + // Open the I²C bus to which the DS248x is connected. + i2cBus, err := i2c.New(-1) + if err != nil { + fmt.Println(err) + return + } + defer i2cBus.Close() + + // Open the DS248x to get a 1-wire bus. + owBus, err := New(i2cBus, nil) + if err != nil { + fmt.Println(err) + return + } + // Search devices on the bus + devices, err := owBus.Search(false) + if err != nil { + fmt.Println(err) + return + } + fmt.Printf("Found %d 1-wire devices: ", len(devices)) + for _, d := range devices { + fmt.Printf(" %#16x", uint64(d)) + } + fmt.Print('\n') +} + +/* Commented out in order not to import periph/host, need to move to smoke test +// TestRecordInit tests and records the initialization of a ds248x by accessing +// real hardware and outputs the recording ready to use for playback in +// TestInit. +// +// This test is skipped unless the -record flag is passed to the test executable. +// Use either `go test -args -record` or `ds2483.test -test.v -record`. +func TestRecordInit(t *testing.T) { + // Only proceed to init hardware and test if -record flag is passed + if !*record { + t.SkipNow() + } + host.Init() + + i2cReal, err := i2c.New(-1) + if err != nil { + t.Fatal(err) + } + i2cBus := &i2ctest.Record{Bus: i2cReal} + // Now init the ds248x. + owBus, err := New(i2cBus, nil) + if err != nil { + t.Fatal(err) + } + // Perform a search triplet operation to see whether anyone is on the bus + // (we could do a full search but that would produce a very long recording). + _, err = owBus.SearchTriplet(0) + if err != nil { + t.Fatal(err) + } + // Output the recording. + t.Logf("var ops = i2ctest.IO{\n") + for _, op := range i2cBus.Ops { + t.Logf(" {Addr: %#x, Write: %#v, Read: %#v},\n", op.Addr, op.Write, op.Read) + } + t.Logf("}\n") +} + +// + +var record *bool + +func init() { + record = flag.Bool("record", false, "record real hardware accesses") +} +*/