diff --git a/AUTHORS b/AUTHORS index 5ef8e2d..e8ff861 100644 --- a/AUTHORS +++ b/AUTHORS @@ -6,8 +6,10 @@ Cássio Botaro Fractal Industries, Inc Google Inc. +Josh Gardiner Matt Aimonetti Max Ekman Rifiniti, Inc Stephan Sperber Thorsten von Eicken + diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 6cc8816..abd45a2 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -29,6 +29,7 @@ Cássio Botaro Eugene Dzhurynsky Hidetoshi Shimokawa +Josh Gardiner Marc-Antoine Ruel Matt Aimonetti Max Ekman @@ -36,3 +37,4 @@ Matias Insaurralde Seán C McCord Stephan Sperber Thorsten von Eicken + diff --git a/experimental/devices/as7262/as7262.go b/experimental/devices/as7262/as7262.go new file mode 100644 index 0000000..79ccf71 --- /dev/null +++ b/experimental/devices/as7262/as7262.go @@ -0,0 +1,532 @@ +// Copyright 2018 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. + +// +build go1.7 + +package as7262 + +import ( + "context" + "encoding/binary" + "errors" + "fmt" + "math" + "sync" + "time" + + "periph.io/x/periph/conn" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/i2c" + "periph.io/x/periph/conn/physic" +) + +// Opts holds the configuration options. +type Opts struct { + InterruptPin gpio.PinIn + Gain Gain +} + +// DefaultOpts are the recommended default options. +var DefaultOpts = Opts{ + InterruptPin: nil, + Gain: G1x, +} + +// New opens a handle to an AS7262 sensor. +func New(bus i2c.Bus, opts *Opts) (*Dev, error) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + <-ctx.Done() + return &Dev{ + c: &i2c.Dev{Bus: bus, Addr: 0x49}, + gain: opts.Gain, + interrupt: opts.InterruptPin, + order: binary.BigEndian, + timeout: 200 * time.Millisecond, + cancel: cancel, + ctx: ctx, + }, nil +} + +// Dev is a handle to the as7262 sensor. +type Dev struct { + mu sync.Mutex + c conn.Conn + timeout time.Duration + interrupt gpio.PinIn + // A new context should be provided for long running operations. + cancel context.CancelFunc + ctx context.Context + gain Gain + order binary.ByteOrder +} + +// Spectrum is the reading from the senor including the actual sensor state for +// the readings. +type Spectrum struct { + Bands []Band + SensorTemperature physic.Temperature + Gain Gain + LedDrive physic.ElectricCurrent + Integration time.Duration + //TODO:(NeuralSpaz) Pretty Printer. +} + +// Band has two types of measurement of relative spectral flux density. +// +// Value +// +// Value are the calibrated readings. The accuracy of the channel counts/μW/cm2 +// is ±12%. +// +// Counts +// +// Counts are the raw readings, there are approximately 45 counts/μW/cm2 with a +// gain of 16 (Gx16). +// +// Wavelength +// +// Wavelength is the nominal center of a band, with a ±40nm bandwidth around the +// center. Wavelengths for the as7262 are: 450nm, 500nm, 550nm, 570nm, 600nm and +// 650nm. +type Band struct { + Wavelength physic.Distance + Value float64 + Counts uint16 + Name string +} + +// Sense preforms a reading of relative spectral radiance of all the sensor +// bands. +// +// Led Drive Current +// +// The AS7262 provides a current limated intergated led drive circuit. Valid +// limits for the drive current are 0mA, 12.5mA, 25mA, 50mA and 100mA. If non +// valid values are given the next lowest valid value is used. +// +// Resolution +// +// For best resolution it is recommended that for a specific led drive +// current that the senseTime or gain is increased until at least one of the +// bands returns a count above 10000. The maximum senseTime time is 714ms +// senseTime will be quantised into intervals of of 2.8ms. Actual time taken to +// make a reading is twice the senseTime. +func (d *Dev) Sense(ledDrive physic.ElectricCurrent, senseTime time.Duration) (Spectrum, error) { + + d.mu.Lock() + defer d.mu.Unlock() + + d.ctx, d.cancel = context.WithCancel(context.Background()) + defer d.cancel() + + it, integration := calcSenseTime(senseTime) + if err := d.writeVirtualRegister(intergrationReg, it); err != nil { + return Spectrum{}, err + } + led, drive := calcLed(ledDrive) + + if err := d.writeVirtualRegister(ledControlReg, led); err != nil { + return Spectrum{}, err + } + + if err := d.writeVirtualRegister(controlReg, uint8(allOneShot)|uint8(d.gain)); err != nil { + return Spectrum{}, err + } + + if d.interrupt != nil { + isEdge := make(chan bool) + go func() { + // TODO(NeuralSpaz): Test on hardware. + isEdge <- d.interrupt.WaitForEdge(integration*2 + d.timeout) + }() + select { + case edge := <-isEdge: + if !edge { + return Spectrum{}, errPinTimeout + } + case <-d.ctx.Done(): + return Spectrum{}, errHalted + } + } else { + select { + // WaitForSensor is time.After(). + case <-waitForSensor(integration * 2): + if err := d.pollDataReady(); err != nil { + return Spectrum{}, err + } + case <-d.ctx.Done(): + return Spectrum{}, errHalted + } + + } + + if err := d.writeVirtualRegister(ledControlReg, 0x00); err != nil { + return Spectrum{}, err + } + + raw := make([]byte, 12) + if err := d.readVirtualRegister(rawBase, raw); err != nil { + return Spectrum{}, err + } + + cal := make([]byte, 24) + if err := d.readVirtualRegister(calBase, cal); err != nil { + return Spectrum{}, err + } + + v := d.order.Uint16(raw[0:2]) + b := d.order.Uint16(raw[2:4]) + g := d.order.Uint16(raw[4:6]) + y := d.order.Uint16(raw[6:8]) + o := d.order.Uint16(raw[8:10]) + r := d.order.Uint16(raw[10:12]) + + vcal := float64(math.Float32frombits(d.order.Uint32(cal[0:4]))) + bcal := float64(math.Float32frombits(d.order.Uint32(cal[4:8]))) + gcal := float64(math.Float32frombits(d.order.Uint32(cal[8:12]))) + ycal := float64(math.Float32frombits(d.order.Uint32(cal[12:16]))) + ocal := float64(math.Float32frombits(d.order.Uint32(cal[16:20]))) + rcal := float64(math.Float32frombits(d.order.Uint32(cal[20:24]))) + + traw := make([]byte, 1) + if err := d.readVirtualRegister(deviceTemperatureReg, traw); err != nil { + return Spectrum{}, err + } + temperature := physic.Temperature(int8(traw[0]))*physic.Kelvin + physic.ZeroCelsius + return Spectrum{ + Bands: []Band{ + {Wavelength: 450 * physic.NanoMetre, Counts: v, Value: vcal, Name: "V"}, + {Wavelength: 500 * physic.NanoMetre, Counts: b, Value: bcal, Name: "B"}, + {Wavelength: 550 * physic.NanoMetre, Counts: g, Value: gcal, Name: "G"}, + {Wavelength: 570 * physic.NanoMetre, Counts: y, Value: ycal, Name: "Y"}, + {Wavelength: 600 * physic.NanoMetre, Counts: o, Value: ocal, Name: "O"}, + {Wavelength: 650 * physic.NanoMetre, Counts: r, Value: rcal, Name: "R"}, + }, + SensorTemperature: temperature, + Gain: d.gain, + LedDrive: drive, + Integration: integration, + }, nil +} + +var waitForSensor = time.After + +// Halt stops any pending operations +func (d *Dev) Halt() error { + d.cancel() + return nil +} + +// String implaments the stringer interface +func (d *Dev) String() string { + return fmt.Sprintf("AMS AS7262 6 channel visible spectrum sensor") +} + +// Gain is the sensor gain for all bands +type Gain int + +const ( + // G1x is gain of 1 + G1x Gain = 0x00 + // G4x is gain of 3.7 + G4x Gain = 0x10 + // G16x is a gain of 16 + G16x Gain = 0x20 + // G64x us a gain of 64 + G64x Gain = 0x30 +) + +// Gain sets the gain of the sensor. There are four levels of gain 1x, 3.7x, 16x, +// and 64x. +func (d *Dev) Gain(gain Gain) error { + // TODO(NeuralSpaz): check that value is valid before writing. Currently + // a client could cast any int as Gain. + d.mu.Lock() + defer d.mu.Unlock() + + d.ctx, d.cancel = context.WithCancel(context.Background()) + defer d.cancel() + + if err := d.writeVirtualRegister(controlReg, uint8(gain)); err != nil { + return err + } + d.gain = gain + return nil +} + +// AS7262 i2c protocol uses virtual registers. To write to a given register the +// MSB of the register must be set when writing the register to the write +// register, also status register must be checked for pending writes or data may +// be discarded. +func (d *Dev) writeVirtualRegister(register, data byte) error { + + // Check for pending writes. + if err := d.pollStatus(writing); err != nil { + return err + } + + // Set virtual register that is being written to. + if err := d.c.Tx([]byte{writeReg, register | 0x80}, nil); err != nil { + return &IOError{"setting virtual register", err} + } + + // Check for pending writes again. + if err := d.pollStatus(writing); err != nil { + return err + } + + // Write data to register that is being written to. + if err := d.c.Tx([]byte{writeReg, data}, nil); err != nil { + return &IOError{"writing virtual register", err} + } + + return nil + +} + +// AS7262 protocol uses virtual registers. To read a virtual register the +// pointer to the virtual register must be written to the write register. Status +// register must be checked for any pending reads or data may be invalid, then +// data maybe read from the read register. +func (d *Dev) readVirtualRegister(register byte, data []byte) error { + rx := make([]byte, 1) + for i := 0; i < len(data); i++ { + // Check for pending reads. + if err := d.pollStatus(clearBuffer); err != nil { + return err + } + + // Set virtual register that is being read from plus offset. + if err := d.c.Tx([]byte{writeReg, register + byte(i)}, nil); err != nil { + return &IOError{"setting virtual register", err} + } + + // Check if read buffer is ready. + if err := d.pollStatus(reading); err != nil { + return err + } + + // Read byte from register that is being read from into our buffer with + // offset. + if err := d.c.Tx([]byte{readReg}, rx); err != nil { + return &IOError{"reading virtual register", err} + } + data[i] = rx[0] + } + return nil +} + +// Polls the data ready bit in the control register(virtual) +func (d *Dev) pollDataReady() error { + pollctx, cancel := context.WithTimeout(context.Background(), d.timeout) + defer cancel() + + for { + if err := d.pollStatus(clearBuffer); err != nil { + return err + } + + // Set virtual register that is being read from plus offset. + if err := d.c.Tx([]byte{writeReg, controlReg}, nil); err != nil { + return &IOError{"setting virtual register", err} + } + + // Check if read buffer is ready. + if err := d.pollStatus(reading); err != nil { + return err + } + + // Read byte from register that is being read from into our buffer with + // offset. + data := make([]byte, 1) + if err := d.c.Tx([]byte{readReg}, data); err != nil { + return &IOError{"reading virtual register", err} + } + if data[0]&0x02 > 0 { + return nil + } + select { + case <-time.After(5 * time.Millisecond): + // Polling interval. + case <-pollctx.Done(): + // Return error if it takes too long. + return errStatusDeadline + case <-d.ctx.Done(): + return errHalted + } + } + +} + +type direction byte + +const ( + // Reading is a bit mask for the status register. + reading direction = 1 + // Writing is a bit mask for the status register. + writing direction = 2 + // ClearBuffer clears any data left in the buffer and then checks the reading + clearBuffer direction = 3 +) + +// The as7262 registers are implemented as virtual registers pollStatus +// provides a way to repeatedly check if there are any pending reads or writes +// in the relevent buffer before a transaction while with a timeout. +// Direction is used to set which buffer is being polled to be ready. +func (d *Dev) pollStatus(dir direction) error { + pollctx, cancel := context.WithTimeout(context.Background(), d.timeout) + defer cancel() + // Check if already canceled first + select { + case <-d.ctx.Done(): + return errHalted + default: + // Proceed. + } + status := make([]byte, 1) + for { + + // Read status register. + err := d.c.Tx([]byte{statusReg}, status) + if err != nil { + return &IOError{"reading status register", err} + } + + switch dir { + case reading: + // Bit 0: rx valid bit. + // 0 → No data is ready to be read in READ register. + // 1 → Data byte available in READ register. + if status[0]&byte(dir) == 1 { + return nil + } + case writing: + // Bit 1: tx valid bit. + // 0 → New data may be written to WRITE register. + // 1 → WRITE register occupied. Do NOT write. + if status[0]&byte(dir) == 0 { + return nil + } + case clearBuffer: + // If there is data left in the buffer read it. + if status[0]&byte(reading) == 1 { + discard := make([]byte, 1) + if err := d.c.Tx([]byte{readReg}, discard); err != nil { + return &IOError{"clearing buffer", err} + } + } + if status[0]&byte(reading) == 0 { + return nil + } + } + select { + case <-time.After(5 * time.Millisecond): + // Polling interval. + case <-pollctx.Done(): + // Return error if it takes too long. + return errStatusDeadline + case <-d.ctx.Done(): + return errHalted + } + } +} + +const ( + maxSenseTime time.Duration = 714 * time.Millisecond + minSenseTime = 2800 * time.Microsecond +) + +// calculateIntergrationTime converts a time.Duration into a value between 0 and +// 256 +func calcSenseTime(t time.Duration) (uint8, time.Duration) { + if t > maxSenseTime { + return 255, maxSenseTime + } + if t < minSenseTime { + return 1, minSenseTime + } + // Minimum step is 2.8ms + quantizedTime := t / minSenseTime + return uint8(quantizedTime), quantizedTime * minSenseTime +} + +func calcLed(drive physic.ElectricCurrent) (uint8, physic.ElectricCurrent) { + switch { + case drive < 12500*physic.MicroAmpere: + return 0x00, 0 + case drive >= 12500*physic.MicroAmpere && drive < 25*physic.MilliAmpere: + return 0x08, 12500 * physic.MicroAmpere + case drive >= 25*physic.MilliAmpere && drive < 50*physic.MilliAmpere: + return 0x18, 25 * physic.MilliAmpere + case drive >= 50*physic.MilliAmpere && drive < 100*physic.MilliAmpere: + return 0x28, 50 * physic.MilliAmpere + default: + return 0x38, 100 * physic.MilliAmpere + } +} + +type mode uint8 + +const ( + // Bank 1 consists of data from the V, G, B, Y photodiodes. + bank1 mode = 0x00 + // Bank 2 consists of data from the G, Y, O, R photodiodes. + bank2 mode = 0x04 + // AllContinuously gets data from both banks continuously, requires 2x + // the intergration time. + allContinuously mode = 0x08 + // AllOneShot gets data from both banks once, and set the data ready bit in + // the status control register when complete requires 2x the intergration + // time. + allOneShot mode = 0x0c +) + +// IOError is a I/O specific error. +type IOError struct { + Op string + Err error +} + +// Error implements the Error interface. +func (e *IOError) Error() string { + if e.Err != nil { + return "ioerror while " + e.Op + ": " + e.Err.Error() + } + return "ioerror while " + e.Op +} + +var ( + errStatusDeadline = errors.New("deadline exceeded reading status register") + errPinTimeout = errors.New("timeout waiting for interrupt signal on pin") + errHalted = errors.New("received halt command") +) + +const ( + statusReg = 0x00 + writeReg = 0x01 + readReg = 0x02 + hardwareVersion = 0x00 + firmwareVersion = 0x02 + controlReg = 0x04 + intergrationReg = 0x05 + deviceTemperatureReg = 0x06 + ledControlReg = 0x07 + // RawBase used as base for reading uint16 values, data must be sequentially. + rawBase = 0x08 + rawVReg = 0x08 + rawBReg = 0x0a + rawGReg = 0x0c + rawYReg = 0x0e + rawOReg = 0x10 + rawRReg = 0x12 + // CalBase used as base for reading float32 values, data must be sequentially. + calBase = 0x14 + calibratedVReg = 0x14 + calibratedBReg = 0x18 + calibratedGReg = 0x1c + calibratedYReg = 0x20 + calibratedOReg = 0x24 + calibratedRReg = 0x28 +) diff --git a/experimental/devices/as7262/as7262_test.go b/experimental/devices/as7262/as7262_test.go new file mode 100644 index 0000000..7097f4e --- /dev/null +++ b/experimental/devices/as7262/as7262_test.go @@ -0,0 +1,913 @@ +// Copyright 2018 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. + +// +build go1.7 + +package as7262 + +import ( + "context" + "reflect" + "testing" + "time" + + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpiotest" + "periph.io/x/periph/conn/i2c" + "periph.io/x/periph/conn/i2c/i2ctest" + "periph.io/x/periph/conn/physic" +) + +func TestDev_Sense(t *testing.T) { + + type timefunc func(*Dev) func(time.Duration) <-chan time.Time + + defer func() { + waitForSensor = time.After + }() + + haltit := func(dev *Dev) func(time.Duration) <-chan time.Time { + return func(d time.Duration) <-chan time.Time { + t := make(chan time.Time, 1) + dev.Halt() + return t + } + } + dontwait := func(dev *Dev) func(time.Duration) <-chan time.Time { + return func(d time.Duration) <-chan time.Time { + t := make(chan time.Time, 1) + t <- time.Now() + dev.timeout = time.Millisecond * 1 + return t + } + } + + intPin := &gpiotest.Pin{N: "GPIO1", Num: 1, Fn: "NotRealPin", EdgesChan: make(chan gpio.Level, 1)} + + tests := []struct { + name string + tx []i2ctest.IO + opts Opts + waiter timefunc + want Spectrum + sendEdge bool + wantErr error + }{ + { + name: "validRead", + opts: DefaultOpts, + waiter: dontwait, + wantErr: nil, + want: validSpectrum, + tx: sensorTestCaseValidRead, + }, + { + name: "errHalted", + opts: DefaultOpts, + waiter: haltit, + wantErr: errHalted, + want: Spectrum{}, + tx: sensorTestCaseValidRead, + }, + { + name: "interuptValid", + opts: Opts{ + InterruptPin: intPin, + }, + waiter: dontwait, + sendEdge: true, + wantErr: nil, + want: validSpectrum, + tx: sensorTestCaseInteruptValidRead, + }, + { + name: "interuptTimeout", + opts: Opts{ + InterruptPin: intPin, + }, + waiter: dontwait, + sendEdge: false, + wantErr: errPinTimeout, + want: Spectrum{}, + tx: sensorTestCaseInteruptValidRead, + }, + { + name: "interuptTimeout", + opts: Opts{ + InterruptPin: intPin, + }, + waiter: dontwait, + sendEdge: false, + wantErr: errPinTimeout, + want: Spectrum{}, + tx: sensorTestCaseInteruptValidRead, + }, + { + name: "ioErrorWritingIntergrationReg", + opts: Opts{ + InterruptPin: intPin, + }, + waiter: dontwait, + sendEdge: false, + wantErr: &IOError{"reading status register", nil}, + want: Spectrum{}, + tx: nil, + }, + { + name: "ioErrorWritingLedControlReg", + opts: DefaultOpts, + waiter: dontwait, + wantErr: &IOError{"reading status register", nil}, + want: Spectrum{}, + tx: sensorTestCaseValidRead[:4], + }, + { + name: "ioErrorWritingControlReg", + opts: DefaultOpts, + waiter: dontwait, + wantErr: &IOError{"reading status register", nil}, + want: Spectrum{}, + tx: sensorTestCaseValidRead[:8], + }, + { + name: "ioErrorPollDataReady", + opts: DefaultOpts, + waiter: dontwait, + wantErr: &IOError{"reading status register", nil}, + want: Spectrum{}, + tx: sensorTestCaseValidRead[:12], + }, + { + name: "ioErrorWritingLedControlReg2", + opts: DefaultOpts, + waiter: dontwait, + wantErr: &IOError{"reading status register", nil}, + want: Spectrum{}, + tx: sensorTestCaseValidRead[:16], + }, + { + name: "ioErrorReadingRawBase", + opts: DefaultOpts, + waiter: dontwait, + wantErr: &IOError{"reading status register", nil}, + want: Spectrum{}, + tx: sensorTestCaseValidRead[:32], + }, + { + name: "ioErrorReadingCalBase", + opts: DefaultOpts, + waiter: dontwait, + wantErr: &IOError{"reading status register", nil}, + want: Spectrum{}, + tx: sensorTestCaseValidRead[:82], + }, + { + name: "ioErrorReadingTemperature", + opts: DefaultOpts, + waiter: dontwait, + wantErr: &IOError{"reading status register", nil}, + want: Spectrum{}, + tx: sensorTestCaseValidRead[:164], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + bus := &i2ctest.Playback{ + Ops: tt.tx, + DontPanic: true, + } + d, _ := New(bus, &tt.opts) + + waitForSensor = tt.waiter(d) + + if d.interrupt != nil && tt.sendEdge { + intPin.EdgesChan <- gpio.High + } + + got, err := d.Sense(physic.MilliAmpere*100, time.Millisecond*3) + + if _, ok := tt.wantErr.(*IOError); ok { + if _, ok := err.(*IOError); !ok { + t.Errorf("expected IOError but %T", err) + } + if err.(*IOError).Op != tt.wantErr.(*IOError).Op { + t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, err.(*IOError).Op) + } + } else if err != tt.wantErr { + t.Errorf("expected error: %v but got: %v", tt.wantErr, got) + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Dev.Sense() = %v, want %v", got, tt.want) + } + + }) + } +} + +func Test_calcSenseTime(t *testing.T) { + var tests = []struct { + name string + t time.Duration + want1 uint8 + want2 time.Duration + }{ + {"0", 2800 * time.Microsecond, 1, 2800 * time.Microsecond}, + {"2.8ms", 0, 1, 2800 * time.Microsecond}, + {"3ms", 3 * time.Millisecond, 1, 2800 * time.Microsecond}, + {"500ms", 500 * time.Millisecond, 178, 498400 * time.Microsecond}, + {"1hour", 1 * time.Hour, 255, 714 * time.Millisecond}, + {"-1hour", -1 * time.Hour, 1, 2800 * time.Microsecond}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got1, got2 := calcSenseTime(test.t) + if got1 != test.want1 { + t.Errorf("calcSenseTime() expected %v but got %v", test.want1, got1) + } + if got2 != test.want2 { + t.Errorf("calcSenseTime() expected %v but got %v", test.want2, got2) + } + }) + } +} + +func Test_calcLed(t *testing.T) { + tests := []struct { + name string + drive physic.ElectricCurrent + want uint8 + }{ + {"Off", 0 * physic.Ampere, 0x00}, + {"12.5", 12500 * physic.MicroAmpere, 0x08}, + {"25", 25 * physic.MilliAmpere, 0x18}, + {"50", 50 * physic.MilliAmpere, 0x28}, + {"100", 100 * physic.MilliAmpere, 0x38}, + {"10", 10 * physic.MilliAmpere, 0x00}, + {"20", 20 * physic.MilliAmpere, 0x08}, + {"30", 30 * physic.MilliAmpere, 0x18}, + {"40", 40 * physic.MilliAmpere, 0x18}, + {"60", 60 * physic.MilliAmpere, 0x28}, + {"110", 110 * physic.MilliAmpere, 0x38}, + {"-1", -1 * physic.MilliAmpere, 0x00}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got, _ := calcLed(tt.drive); got != tt.want { + t.Errorf("calcLed() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDev_pollStatus(t *testing.T) { + tests := []struct { + name string + tx []i2ctest.IO + dir direction + timeout time.Duration + halt time.Duration + wantErr error + }{ + { + name: "errStatusIO", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{}}, + }, + dir: reading, + timeout: time.Millisecond * 1, + wantErr: &IOError{"reading status register", nil}, + }, + { + name: "errStatusDeadline", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + }, + dir: reading, + timeout: time.Millisecond * 1, + wantErr: errStatusDeadline, + }, + { + name: "errHalted", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + }, + dir: reading, + timeout: time.Millisecond * 1000, + halt: time.Nanosecond, + wantErr: errHalted, + }, + { + name: "ReadReady", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + }, + dir: reading, + timeout: time.Millisecond * 1000, + wantErr: nil, + }, + { + name: "WriteReady", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + }, + dir: writing, + timeout: time.Millisecond * 1000, + wantErr: nil, + }, + { + name: "CleanBuffer", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + }, + dir: clearBuffer, + timeout: time.Millisecond * 1000, + wantErr: nil, + }, + { + name: "ClearBuffer", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + }, + dir: clearBuffer, + timeout: time.Millisecond * 1000, + wantErr: nil, + }, + { + name: "errClearingBuffer", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{}}, + }, + dir: clearBuffer, + timeout: time.Millisecond * 1000, + wantErr: &IOError{"clearing buffer", nil}, + }, + { + name: "retry1", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + }, + dir: reading, + timeout: time.Millisecond * 100, + wantErr: nil, + }, + { + name: "retrywithHalt", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + }, + dir: reading, + timeout: time.Millisecond * 100, + halt: time.Millisecond * 3, + wantErr: errHalted, + }, + } + for _, tt := range tests { + + t.Run(tt.name, func(t *testing.T) { + bus := &i2ctest.Playback{ + Ops: tt.tx, + DontPanic: true, + } + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + d := &Dev{ + c: &i2c.Dev{Bus: bus, Addr: 0x49}, + cancel: cancel, + ctx: ctx, + timeout: tt.timeout, + } + if tt.halt > time.Nanosecond { + go func() { + time.Sleep(tt.halt) + cancel() + }() + } else if tt.halt != 0 { + cancel() + } + + got := d.pollStatus(tt.dir) + // t.Errorf("expected error: %v but got: %v", tt.wantErr, got) + + if _, ok := tt.wantErr.(*IOError); ok { + if _, ok := got.(*IOError); !ok { + t.Errorf("expected IOError but %T", got) + } + if got.(*IOError).Op != tt.wantErr.(*IOError).Op { + t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op) + } + } else if got != tt.wantErr { + t.Errorf("expected error: %v but got: %v", tt.wantErr, got) + } + }) + } +} + +func TestDev_writeVirtualRegister(t *testing.T) { + tests := []struct { + name string + tx []i2ctest.IO + timeout time.Duration + halt time.Duration + wantErr error + }{ + { + name: "errHalted", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x02}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x02}}, + }, + timeout: time.Millisecond * 100, + halt: time.Nanosecond, + wantErr: errHalted, + }, + { + name: "errSettingVirtualRegister", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{}, R: []byte{}}, + }, + timeout: time.Millisecond * 100, + wantErr: &IOError{"setting virtual register", nil}, + }, + { + name: "errStatusIO", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x84}, R: []byte{}}, + }, + timeout: time.Millisecond * 100, + wantErr: &IOError{"reading status register", nil}, + }, + { + name: "errWritingVirtualRegister", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x84}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + }, + timeout: time.Millisecond * 100, + wantErr: &IOError{"writing virtual register", nil}, + }, + { + name: "writeOk", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x84}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0xFF}, R: []byte{}}, + }, + timeout: time.Millisecond * 100, + wantErr: nil, + }, + } + for _, tt := range tests { + + t.Run(tt.name, func(t *testing.T) { + bus := &i2ctest.Playback{ + Ops: tt.tx, + DontPanic: true, + } + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + d := &Dev{ + c: &i2c.Dev{Bus: bus, Addr: 0x49}, + cancel: cancel, + ctx: ctx, + timeout: tt.timeout, + } + if tt.halt > time.Nanosecond { + go func() { + time.Sleep(tt.halt) + cancel() + }() + } else if tt.halt != 0 { + cancel() + } + + got := d.writeVirtualRegister(0x04, 0xFF) + if _, ok := tt.wantErr.(*IOError); ok { + if _, ok := got.(*IOError); !ok { + t.Errorf("expected IOError but %T", got) + } + if got.(*IOError).Op != tt.wantErr.(*IOError).Op { + t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op) + } + } else if got != tt.wantErr { + t.Errorf("expected error: %v but got: %v", tt.wantErr, got) + } + }) + } +} + +func TestDev_readVirtualRegister(t *testing.T) { + tests := []struct { + name string + tx []i2ctest.IO + timeout time.Duration + data []byte + halt time.Duration + wantErr error + }{ + { + name: "nodata", + timeout: time.Millisecond * 100, + wantErr: nil, + }, + { + name: "errHalted", + tx: []i2ctest.IO{}, + data: []byte{0x00}, + halt: time.Nanosecond, + timeout: time.Millisecond * 100, + wantErr: errHalted, + }, + { + name: "errClearingBuffer", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + // {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x02}}, + }, + data: []byte{0x00}, + timeout: time.Millisecond * 100, + wantErr: &IOError{"clearing buffer", nil}, + }, + { + name: "errSettingVirtualRegister", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg}, R: []byte{}}, + }, + data: []byte{0x00}, + timeout: time.Millisecond * 100, + wantErr: &IOError{"setting virtual register", nil}, + }, + { + name: "errStatusIO", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x04}, R: []byte{}}, + }, + data: []byte{0x00}, + timeout: time.Millisecond * 100, + wantErr: &IOError{"reading status register", nil}, + }, + { + name: "errReadingVirtualRegister", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x04}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{}}, + }, + data: []byte{0x00}, + timeout: time.Millisecond * 100, + wantErr: &IOError{"reading virtual register", nil}, + }, + { + name: "readSingleByteOk", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x04}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + }, + data: []byte{0x00}, + timeout: time.Millisecond * 100, + wantErr: nil, + }, + { + name: "readTwoBytesOk", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x04}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x05}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + }, + data: []byte{0x00, 0x00}, + timeout: time.Millisecond * 100, + wantErr: nil, + }, + } + for _, tt := range tests { + + t.Run(tt.name, func(t *testing.T) { + bus := &i2ctest.Playback{ + Ops: tt.tx, + DontPanic: true, + } + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + d := &Dev{ + c: &i2c.Dev{Bus: bus, Addr: 0x49}, + cancel: cancel, + ctx: ctx, + timeout: tt.timeout, + } + if tt.halt > time.Nanosecond { + go func() { + time.Sleep(tt.halt) + cancel() + }() + } else if tt.halt != 0 { + cancel() + } + + got := d.readVirtualRegister(0x04, tt.data) + if _, ok := tt.wantErr.(*IOError); ok { + if _, ok := got.(*IOError); !ok { + t.Errorf("expected IOError but %T", got) + } + if got.(*IOError).Op != tt.wantErr.(*IOError).Op { + t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op) + } + } else if got != tt.wantErr { + t.Errorf("expected error: %v but got: %v", tt.wantErr, got) + } + }) + } +} + +func TestDev_pollDataReady(t *testing.T) { + tests := []struct { + name string + tx []i2ctest.IO + timeout time.Duration + halt time.Duration + wantErr error + }{ + { + name: "errHalted", + tx: []i2ctest.IO{}, + halt: time.Nanosecond, + timeout: time.Millisecond * 100, + wantErr: errHalted, + }, + { + name: "errStatusIO", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{}}, + }, + timeout: time.Millisecond * 100, + wantErr: &IOError{"reading status register", nil}, + }, + { + name: "errSettingVirtualRegister", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg}, R: []byte{}}, + }, + timeout: time.Millisecond * 100, + wantErr: &IOError{"setting virtual register", nil}, + }, + { + name: "errStatusIO2", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, controlReg}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + }, + timeout: time.Millisecond * 100, + wantErr: &IOError{"reading status register", nil}, + }, + { + name: "errReadingVirtualRegister", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, controlReg}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{}, R: []byte{}}, + }, + timeout: time.Millisecond * 100, + wantErr: &IOError{"reading virtual register", nil}, + }, + { + name: "errStatusDeadline", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, controlReg}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + }, + timeout: 1 * time.Nanosecond, + wantErr: errStatusDeadline, + }, + { + name: "ok", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, controlReg}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x03}}, + }, + timeout: time.Millisecond * 100, + wantErr: nil, + }, + { + name: "retryOk", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, controlReg}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, controlReg}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x03}}, + }, + timeout: time.Millisecond * 100, + wantErr: nil, + }, + { + name: "errHalted2", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, controlReg}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, controlReg}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x03}}, + }, + halt: time.Millisecond * 2, + timeout: time.Millisecond * 100, + wantErr: errHalted, + }, + } + for _, tt := range tests { + + t.Run(tt.name, func(t *testing.T) { + bus := &i2ctest.Playback{ + Ops: tt.tx, + DontPanic: true, + } + ctx, cancel := context.WithCancel(context.Background()) + + defer cancel() + d := &Dev{ + c: &i2c.Dev{Bus: bus, Addr: 0x49}, + cancel: cancel, + ctx: ctx, + timeout: tt.timeout, + } + if tt.halt > time.Nanosecond { + go func() { + time.Sleep(tt.halt) + d.Halt() + }() + } else if tt.halt != 0 { + d.Halt() + } + + got := d.pollDataReady() + if _, ok := tt.wantErr.(*IOError); ok { + if _, ok := got.(*IOError); !ok { + t.Errorf("expected IOError but %T", got) + } + if got.(*IOError).Op != tt.wantErr.(*IOError).Op { + t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op) + } + } else if got != tt.wantErr { + t.Errorf("expected error: %v but got: %v", tt.wantErr, got) + } + }) + } +} + +func TestNew(t *testing.T) { + // _, cancel := context.WithCancel(context.Background()) + + tests := []struct { + name string + opts Opts + want1 Gain + want2 gpio.PinIn + wantErr bool + }{ + {name: "defautls", + opts: DefaultOpts, + want1: G1x, + want2: nil, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + bus := &i2ctest.Playback{DontPanic: true} + d, err := New(bus, &tt.opts) + if err != nil != tt.wantErr { + t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.want1 != d.gain { + t.Errorf("New() wanted %v but got %v", tt.want1, d.gain) + } + if tt.want2 != d.interrupt { + t.Errorf("New() wanted %v but got %v", tt.want2, d.interrupt) + } + + }) + } +} + +func TestDev_Gain(t *testing.T) { + tests := []struct { + name string + tx []i2ctest.IO + timeout time.Duration + gain Gain + wantErr error + }{ + { + name: "errStatusIO", + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{}}, + // {Addr: 0x49, W: []byte{writeReg}, R: []byte{}}, + }, + timeout: time.Millisecond * 100, + wantErr: &IOError{"reading status register", nil}, + }, + { + name: "ok", + gain: G16x, + tx: []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x84}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x20}, R: []byte{}}, + }, + timeout: time.Millisecond * 100, + wantErr: nil, + }, + } + for _, tt := range tests { + + t.Run(tt.name, func(t *testing.T) { + bus := &i2ctest.Playback{ + Ops: tt.tx, + DontPanic: true, + } + d, _ := New(bus, &DefaultOpts) + + got := d.Gain(tt.gain) + + if _, ok := tt.wantErr.(*IOError); ok { + if _, ok := got.(*IOError); !ok { + t.Errorf("expected IOError but %T", got) + } + if got.(*IOError).Op != tt.wantErr.(*IOError).Op { + t.Errorf("expected %s, but got %s", tt.wantErr.(*IOError).Op, got.(*IOError).Op) + } + } else if got != tt.wantErr { + t.Errorf("expected error: %v but got: %v", tt.wantErr, got) + } + }) + } +} + +func TestDev_String(t *testing.T) { + want := "AMS AS7262 6 channel visible spectrum sensor" + d := &Dev{} + if d.String() != want { + t.Errorf("expected %s but got %s", want, d.String()) + } +} + +func TestIOError_Error(t *testing.T) { + tests := []struct { + name string + op string + err error + want string + }{ + {"nil", "doing nothing", nil, "ioerror while doing nothing"}, + {"errTimeoutPin", "", errPinTimeout, "ioerror while : timeout waiting for interrupt signal on pin"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &IOError{tt.op, tt.err} + got := e.Error() + if tt.want != got { + t.Errorf("expected %s but got %s", tt.want, got) + } + }) + } +} diff --git a/experimental/devices/as7262/doc.go b/experimental/devices/as7262/doc.go new file mode 100644 index 0000000..eca7be0 --- /dev/null +++ b/experimental/devices/as7262/doc.go @@ -0,0 +1,12 @@ +// Copyright 2018 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 as7262 controls an AMS 6 channel visible spectral sensor via an i2c +// interface. The as7262 features 6 spectral channels spaced at 450, 500, 550, +// 570, 600, 650 nm and includes 2 integrated LED drivers. +// +// Datasheet +// +// https://ams.com/documents/20143/36005/AS7262_DS000486_2-00.pdf +package as7262 diff --git a/experimental/devices/as7262/example_test.go b/experimental/devices/as7262/example_test.go new file mode 100644 index 0000000..9e3f6b5 --- /dev/null +++ b/experimental/devices/as7262/example_test.go @@ -0,0 +1,48 @@ +// Copyright 2018 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. + +// +build go1.7 + +package as7262_test + +import ( + "fmt" + "log" + "time" + + "periph.io/x/periph/conn/physic" + + "periph.io/x/periph/conn/i2c/i2creg" + "periph.io/x/periph/experimental/devices/as7262" + "periph.io/x/periph/host" +) + +func Example() { + // Make sure periph is initialized. + if _, err := host.Init(); err != nil { + log.Fatal(err) + } + + // Open default I²C bus. + bus, err := i2creg.Open("") + if err != nil { + log.Fatalf("failed to open I²C: %v", err) + } + defer bus.Close() + + // Create a new spectrum sensor. + sensor, err := as7262.New(bus, &as7262.DefaultOpts) + if err != nil { + log.Fatalln(err) + } + + // Read values from sensor. + spectrum, err := sensor.Sense(25*physic.MilliAmpere, 500*time.Millisecond) + + if err != nil { + log.Fatalln(err) + } + + fmt.Println(spectrum) +} diff --git a/experimental/devices/as7262/testdata_test.go b/experimental/devices/as7262/testdata_test.go new file mode 100644 index 0000000..b020544 --- /dev/null +++ b/experimental/devices/as7262/testdata_test.go @@ -0,0 +1,372 @@ +// Copyright 2018 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. + +// +build go1.7 + +package as7262 + +import ( + "time" + + "periph.io/x/periph/conn/i2c/i2ctest" + "periph.io/x/periph/conn/physic" +) + +// Expected response from sensorTestCaseValidRead or +// sensorTestCaseInteruptValidRead. +var validSpectrum = Spectrum{ + Bands: []Band{ + {450 * physic.NanoMetre, 0.15625, 43707, "V"}, + {500 * physic.NanoMetre, 0.15625, 43707, "B"}, + {550 * physic.NanoMetre, 0.15625, 43707, "G"}, + {570 * physic.NanoMetre, 0.15625, 43707, "Y"}, + {600 * physic.NanoMetre, 0.15625, 43707, "O"}, + {650 * physic.NanoMetre, 0.15625, 43707, "R"}, + }, + SensorTemperature: physic.ZeroCelsius, + LedDrive: physic.MilliAmpere * 100, + Integration: 2800 * time.Microsecond, +} + +// Sequence of i2c traffic that yeilds validSpectrum. +var sensorTestCaseValidRead = []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x85}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x01}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x87}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x38}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x84}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0c}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x04}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x02}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x87}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x00}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x08}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x09}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0a}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0b}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0c}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0d}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0e}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0f}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x10}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x11}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x12}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x13}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x14}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x15}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x16}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x17}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x18}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x19}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1a}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1b}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1c}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1d}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1e}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1f}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x20}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x21}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x22}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x23}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x24}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x25}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x26}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x27}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x28}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x29}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x2a}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x2b}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x06}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, +} + +// Same as sensorTestCaseValidRead but omiting polling for data ready. +var sensorTestCaseInteruptValidRead = []i2ctest.IO{ + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x85}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x01}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x87}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x38}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x84}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0c}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x87}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x00}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x08}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x09}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0a}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0b}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0c}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0d}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0e}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x0f}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x10}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x11}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x12}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xAA}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x13}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0xBB}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x14}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x15}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x16}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x17}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x18}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x19}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1a}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1b}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1c}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1d}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1e}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x1f}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x20}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x21}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x22}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x23}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x24}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x25}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x26}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x27}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x28}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x3e}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x29}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x20}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x2a}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x2b}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{writeReg, 0x06}, R: []byte{}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x01}}, + {Addr: 0x49, W: []byte{readReg}, R: []byte{0x00}}, + {Addr: 0x49, W: []byte{statusReg}, R: []byte{0x00}}, +}