From 2b2b42ee2aacd27cd74c9ea4796d50686efab14e Mon Sep 17 00:00:00 2001 From: SoulKa Date: Tue, 16 Jan 2024 11:53:46 +0100 Subject: [PATCH 1/9] add Sense() test --- aht20/aht20_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 aht20/aht20_test.go diff --git a/aht20/aht20_test.go b/aht20/aht20_test.go new file mode 100644 index 0000000..ef833bd --- /dev/null +++ b/aht20/aht20_test.go @@ -0,0 +1,36 @@ +package aht20 + +import ( + "periph.io/x/conn/v3/i2c" + "periph.io/x/conn/v3/i2c/i2ctest" + "periph.io/x/conn/v3/physic" + "testing" +) + +func TestDev_Sense(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Trigger measurement + {Addr: deviceAddress, W: argsMeasure}, + // Read measurement + {Addr: deviceAddress, R: []byte{0x18, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x7F}}, + }, + } + dev := Dev{d: &i2c.Dev{Bus: &bus, Addr: deviceAddress}, opts: DefaultOpts} + e := physic.Env{} + if err := dev.Sense(&e); err != nil { + t.Fatal(err) + } + if expected := 19445800781*physic.NanoKelvin + physic.ZeroCelsius; e.Temperature != expected { + t.Fatalf("temperature %s(%d) != %s(%d)", expected, expected, e.Temperature, e.Temperature) + } + if expected := 4582824 * physic.TenthMicroRH; e.Humidity != expected { + t.Fatalf("humidity %s(%d) != %s(%d)", expected, expected, e.Humidity, e.Humidity) + } + if expected := 0 * physic.Pascal; e.Pressure != expected { + t.Fatalf("pressure %s(%d) != %s(%d)", expected, expected, e.Pressure, e.Pressure) + } + if err := bus.Close(); err != nil { + t.Fatal(err) + } +} From 6c156bdb5d9efcb883a17ee7596e9c154f2b6465 Mon Sep 17 00:00:00 2001 From: SoulKa Date: Tue, 16 Jan 2024 13:12:47 +0100 Subject: [PATCH 2/9] fix IsInitialized() always returning false --- aht20/aht20.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/aht20/aht20.go b/aht20/aht20.go index 8bab0d9..5d6cf3c 100644 --- a/aht20/aht20.go +++ b/aht20/aht20.go @@ -70,10 +70,10 @@ func NewI2C(b i2c.Bus, opts *Opts) (*Dev, error) { d.opts.MeasurementWaitInterval = 10 * time.Millisecond } - if err, initialized := d.isInitialized(); err != nil { + if err, initialized := d.IsInitialized(); err != nil { return nil, fmt.Errorf("%w; could read sensor status", err) } else if !initialized { - if err := d.initialize(); err != nil { + if err := d.Initialize(); err != nil { return nil, fmt.Errorf("%w; could not calibrate sensor", err) } } @@ -191,15 +191,17 @@ func (d *Dev) Halt() error { return nil } -func (d *Dev) isInitialized() (error, bool) { - var data byte - if err := d.d.Tx([]byte{cmdStatus}, []byte{data}); err != nil { +// IsInitialized returns true if the sensor is initialized (calibrated) +func (d *Dev) IsInitialized() (error, bool) { + data := make([]byte, 1) + if err := d.d.Tx([]byte{cmdStatus}, data); err != nil { return err, false } - return nil, data&bitInitialized == 1 + return nil, (data[0] & bitInitialized) != 0 } -func (d *Dev) initialize() error { +// Initialize calibrates the sensor. It takes 10ms. +func (d *Dev) Initialize() error { if err := d.d.Tx(argsInitialize, nil); err != nil { return err } From 0b0968f0c6d234d2d51513dbfda9d58f68d01bee Mon Sep 17 00:00:00 2001 From: SoulKa Date: Tue, 16 Jan 2024 13:13:10 +0100 Subject: [PATCH 3/9] add tests for most exported Methods --- aht20/aht20_test.go | 82 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/aht20/aht20_test.go b/aht20/aht20_test.go index ef833bd..15efe42 100644 --- a/aht20/aht20_test.go +++ b/aht20/aht20_test.go @@ -7,13 +7,80 @@ import ( "testing" ) +const byteStatusInitialized = bitInitialized | 0x10 + +func TestNewI2C(t *testing.T) { + type TestCase struct { + name string + ops []i2ctest.IO + } + + testCases := []TestCase{ + { + name: "device already initialized", + ops: []i2ctest.IO{ + // Read status + {Addr: deviceAddress, W: []byte{cmdStatus}, R: []byte{byteStatusInitialized}}, + }, + }, + { + name: "device not initialized", + ops: []i2ctest.IO{ + // Read status + {Addr: deviceAddress, W: []byte{cmdStatus}, R: []byte{0x00}}, + // Initialize + {Addr: deviceAddress, W: argsInitialize}, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + bus := i2ctest.Playback{Ops: tc.ops} + if dev, err := NewI2C(&bus, nil); err != nil { + t.Fatal(err) + } else if dev == nil { + t.Fatal("expected device") + } + }) + } +} + +func TestDev_IsInitialized(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Read status + {Addr: deviceAddress, W: []byte{cmdStatus}, R: []byte{byteStatusInitialized}}, + }, + } + dev := Dev{d: &i2c.Dev{Bus: &bus, Addr: deviceAddress}, opts: DefaultOpts} + if err, initialized := dev.IsInitialized(); err != nil { + t.Fatal(err) + } else if !initialized { + t.Fatal("expected initialized") + } +} + +func TestDev_Initialize(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Initialize + {Addr: deviceAddress, W: argsInitialize}, + }, + } + dev := Dev{d: &i2c.Dev{Bus: &bus, Addr: deviceAddress}, opts: DefaultOpts} + if err := dev.Initialize(); err != nil { + t.Fatal(err) + } +} + func TestDev_Sense(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ // Trigger measurement {Addr: deviceAddress, W: argsMeasure}, // Read measurement - {Addr: deviceAddress, R: []byte{0x18, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x7F}}, + {Addr: deviceAddress, R: []byte{byteStatusInitialized, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x7F}}, }, } dev := Dev{d: &i2c.Dev{Bus: &bus, Addr: deviceAddress}, opts: DefaultOpts} @@ -34,3 +101,16 @@ func TestDev_Sense(t *testing.T) { t.Fatal(err) } } + +func TestDev_SoftReset(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Soft reset + {Addr: deviceAddress, W: []byte{cmdSoftReset}}, + }, + } + dev := Dev{d: &i2c.Dev{Bus: &bus, Addr: deviceAddress}, opts: DefaultOpts} + if err := dev.SoftReset(); err != nil { + t.Fatal(err) + } +} From 4e1fd2f0a8bca476c836fbf069f62a3237da6592 Mon Sep 17 00:00:00 2001 From: SoulKa Date: Tue, 16 Jan 2024 13:25:54 +0100 Subject: [PATCH 4/9] add negative test cases for Sense() --- aht20/aht20_test.go | 74 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/aht20/aht20_test.go b/aht20/aht20_test.go index 15efe42..e54c752 100644 --- a/aht20/aht20_test.go +++ b/aht20/aht20_test.go @@ -5,6 +5,7 @@ import ( "periph.io/x/conn/v3/i2c/i2ctest" "periph.io/x/conn/v3/physic" "testing" + "time" ) const byteStatusInitialized = bitInitialized | 0x10 @@ -46,7 +47,7 @@ func TestNewI2C(t *testing.T) { } } -func TestDev_IsInitialized(t *testing.T) { +func TestDev_IsInitialized_true(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ // Read status @@ -61,6 +62,21 @@ func TestDev_IsInitialized(t *testing.T) { } } +func TestDev_IsInitialized_false(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Read status + {Addr: deviceAddress, W: []byte{cmdStatus}, R: []byte{0x00}}, + }, + } + dev := Dev{d: &i2c.Dev{Bus: &bus, Addr: deviceAddress}, opts: DefaultOpts} + if err, initialized := dev.IsInitialized(); err != nil { + t.Fatal(err) + } else if initialized { + t.Fatal("expected not initialized") + } +} + func TestDev_Initialize(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ @@ -74,7 +90,7 @@ func TestDev_Initialize(t *testing.T) { } } -func TestDev_Sense(t *testing.T) { +func TestDev_Sense_successful(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ // Trigger measurement @@ -102,6 +118,60 @@ func TestDev_Sense(t *testing.T) { } } +func TestDev_Sense_error(t *testing.T) { + type TestCase struct { + name string + data []byte + opts Opts + error error + } + + testCases := []TestCase{ + { + name: "data corrupt", + data: []byte{byteStatusInitialized, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x7E}, + opts: DefaultOpts, + error: &DataCorruptionError{0x7F, 0x7E}, + }, + { + name: "read timeout", + data: []byte{0x00, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x7F}, + opts: Opts{ + MeasurementReadTimeout: 1, + MeasurementWaitInterval: 10 * time.Millisecond, + ValidateData: true, + }, + error: &ReadTimeoutError{1}, + }, + { + name: "not initialized", + data: []byte{0x00, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x20}, + opts: DefaultOpts, + error: &NotInitializedError{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + bus := i2ctest.Playback{ + Ops: []i2ctest.IO{ + // Trigger measurement + {Addr: deviceAddress, W: argsMeasure}, + // Read measurement + {Addr: deviceAddress, R: tc.data}, + }, + } + dev := Dev{d: &i2c.Dev{Bus: &bus, Addr: deviceAddress}, opts: tc.opts} + e := physic.Env{} + if err := dev.Sense(&e); err == nil { + t.Fatal("expected error") + } else if err.Error() != tc.error.Error() { + t.Fatalf("expected error %s, got %s", tc.error, err) + } + }) + } +} + func TestDev_SoftReset(t *testing.T) { bus := i2ctest.Playback{ Ops: []i2ctest.IO{ From c93fae5a202b5b3689f464732b3165fecf1d8ac4 Mon Sep 17 00:00:00 2001 From: SoulKa Date: Tue, 16 Jan 2024 13:26:42 +0100 Subject: [PATCH 5/9] add license to test --- aht20/aht20_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aht20/aht20_test.go b/aht20/aht20_test.go index e54c752..6b90704 100644 --- a/aht20/aht20_test.go +++ b/aht20/aht20_test.go @@ -1,3 +1,7 @@ +// Copyright 2024 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 aht20 import ( From 66f1d3456b0f0a4e29339432d1bd80e462b308fb Mon Sep 17 00:00:00 2001 From: SoulKa Date: Tue, 16 Jan 2024 14:20:53 +0100 Subject: [PATCH 6/9] fix CRC checksum of read timeout test --- aht20/aht20_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aht20/aht20_test.go b/aht20/aht20_test.go index 6b90704..157e584 100644 --- a/aht20/aht20_test.go +++ b/aht20/aht20_test.go @@ -139,7 +139,7 @@ func TestDev_Sense_error(t *testing.T) { }, { name: "read timeout", - data: []byte{0x00, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x7F}, + data: []byte{byteStatusInitialized, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x7F}, opts: Opts{ MeasurementReadTimeout: 1, MeasurementWaitInterval: 10 * time.Millisecond, From 1ad27c2924e8a57b17bbac7907ce70d76b3e2b0c Mon Sep 17 00:00:00 2001 From: SoulKa Date: Tue, 16 Jan 2024 14:44:57 +0100 Subject: [PATCH 7/9] fix read timeout test --- aht20/aht20_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/aht20/aht20_test.go b/aht20/aht20_test.go index 157e584..6eab015 100644 --- a/aht20/aht20_test.go +++ b/aht20/aht20_test.go @@ -9,7 +9,6 @@ import ( "periph.io/x/conn/v3/i2c/i2ctest" "periph.io/x/conn/v3/physic" "testing" - "time" ) const byteStatusInitialized = bitInitialized | 0x10 @@ -139,10 +138,10 @@ func TestDev_Sense_error(t *testing.T) { }, { name: "read timeout", - data: []byte{byteStatusInitialized, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x7F}, + data: []byte{bitInitialized | bitBusy, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x16}, opts: Opts{ MeasurementReadTimeout: 1, - MeasurementWaitInterval: 10 * time.Millisecond, + MeasurementWaitInterval: -1, ValidateData: true, }, error: &ReadTimeoutError{1}, From e41bf8b05095c3e498f024a6a54971279c192ed4 Mon Sep 17 00:00:00 2001 From: SoulKa Date: Tue, 16 Jan 2024 14:52:16 +0100 Subject: [PATCH 8/9] remove read timeout test --- aht20/aht20_test.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/aht20/aht20_test.go b/aht20/aht20_test.go index 6eab015..6cae619 100644 --- a/aht20/aht20_test.go +++ b/aht20/aht20_test.go @@ -136,16 +136,6 @@ func TestDev_Sense_error(t *testing.T) { opts: DefaultOpts, error: &DataCorruptionError{0x7F, 0x7E}, }, - { - name: "read timeout", - data: []byte{bitInitialized | bitBusy, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x16}, - opts: Opts{ - MeasurementReadTimeout: 1, - MeasurementWaitInterval: -1, - ValidateData: true, - }, - error: &ReadTimeoutError{1}, - }, { name: "not initialized", data: []byte{0x00, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x20}, From e5f433a0f5d40b725966c8f0b8a550dd5f19c871 Mon Sep 17 00:00:00 2001 From: SoulKa Date: Tue, 16 Jan 2024 14:52:56 +0100 Subject: [PATCH 9/9] remove opts from test case --- aht20/aht20_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/aht20/aht20_test.go b/aht20/aht20_test.go index 6cae619..3ef3a79 100644 --- a/aht20/aht20_test.go +++ b/aht20/aht20_test.go @@ -125,7 +125,6 @@ func TestDev_Sense_error(t *testing.T) { type TestCase struct { name string data []byte - opts Opts error error } @@ -133,13 +132,11 @@ func TestDev_Sense_error(t *testing.T) { { name: "data corrupt", data: []byte{byteStatusInitialized, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x7E}, - opts: DefaultOpts, error: &DataCorruptionError{0x7F, 0x7E}, }, { name: "not initialized", data: []byte{0x00, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x20}, - opts: DefaultOpts, error: &NotInitializedError{}, }, } @@ -154,7 +151,7 @@ func TestDev_Sense_error(t *testing.T) { {Addr: deviceAddress, R: tc.data}, }, } - dev := Dev{d: &i2c.Dev{Bus: &bus, Addr: deviceAddress}, opts: tc.opts} + dev := Dev{d: &i2c.Dev{Bus: &bus, Addr: deviceAddress}, opts: DefaultOpts} e := physic.Env{} if err := dev.Sense(&e); err == nil { t.Fatal("expected error")