Skip to content

Commit

Permalink
Add AHT20 tests (#70)
Browse files Browse the repository at this point in the history
* add Sense() test

* fix IsInitialized() always returning false

* add tests for most exported Methods

* add negative test cases for Sense()

* add license to test

* fix CRC checksum of read timeout test

* fix read timeout test

* remove read timeout test

* remove opts from test case
  • Loading branch information
SoulKa authored Jan 16, 2024
1 parent e644b7c commit 872d025
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 7 deletions.
16 changes: 9 additions & 7 deletions aht20/aht20.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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
}
Expand Down
176 changes: 176 additions & 0 deletions aht20/aht20_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// 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 (
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/i2c/i2ctest"
"periph.io/x/conn/v3/physic"
"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_true(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_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{
// 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_successful(t *testing.T) {
bus := i2ctest.Playback{
Ops: []i2ctest.IO{
// Trigger measurement
{Addr: deviceAddress, W: argsMeasure},
// Read measurement
{Addr: deviceAddress, R: []byte{byteStatusInitialized, 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)
}
}

func TestDev_Sense_error(t *testing.T) {
type TestCase struct {
name string
data []byte
error error
}

testCases := []TestCase{
{
name: "data corrupt",
data: []byte{byteStatusInitialized, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x7E},
error: &DataCorruptionError{0x7F, 0x7E},
},
{
name: "not initialized",
data: []byte{0x00, 0x75, 0x52, 0x05, 0x8E, 0x40, 0x20},
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: DefaultOpts}
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{
// 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)
}
}

0 comments on commit 872d025

Please sign in to comment.