air-sensors

Air Quality Sensor library
git clone https://www.brianlane.com/git/air-sensors
Log | Files | Refs | README | LICENSE

sgp30_test.go (6968B)


      1 // Copyright 2020 by Brian C. Lane <bcl@brianlane.com>. All rights reserved.
      2 // Use of this source code is governed under the Apache License, Version 2.0
      3 // that can be found in the LICENSE file.
      4 
      5 package sgp30
      6 
      7 import (
      8 	"io/ioutil"
      9 	"os"
     10 	"testing"
     11 	"time"
     12 
     13 	"periph.io/x/periph/conn/i2c/i2ctest"
     14 )
     15 
     16 var (
     17 	BadSerialNumber    = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0}
     18 	GoodSerialNumber   = []byte{0x00, 0x00, 0x81, 0x01, 0x57, 0x9C, 0xAC, 0xA2, 0x54}
     19 	BadBaselineData    = []byte{0, 0, 0, 0, 0, 0}
     20 	GoodBaselineData   = []byte{0x88, 0xa1, 0x58, 0x8d, 0xc4, 0x61}
     21 	BadFeaturesData    = []byte{0, 0, 0}
     22 	GoodFeaturesData   = []byte{0x00, 0x22, 0x65}
     23 	BadAirQualityData  = []byte{0, 0, 0, 0, 0, 0}
     24 	GoodAirQualityData = []byte{0x01, 0x9e, 0x53, 0x00, 0x0d, 0xcd}
     25 )
     26 
     27 func TestWord(t *testing.T) {
     28 	data := []byte{0x00, 0x01, 0x80, 0x0A, 0x55, 0xAA, 0xFF, 0x7F}
     29 	result := []uint16{0x0001, 0x800A, 0x55AA, 0xFF7F}
     30 	for i := 0; i < len(result); i++ {
     31 		if word(data, i*2) != result[i] {
     32 			t.Errorf("word error: i == %d", i)
     33 		}
     34 	}
     35 }
     36 
     37 func TestChecksum(t *testing.T) {
     38 	if !checkCRC8(GoodSerialNumber[0:3]) {
     39 		t.Fatal("serial number word 1 CRC8 error")
     40 	}
     41 	if !checkCRC8(GoodSerialNumber[3:6]) {
     42 		t.Fatal("serial number word 2 CRC8 error")
     43 	}
     44 	if !checkCRC8(GoodSerialNumber[6:9]) {
     45 		t.Fatal("serial number word 3 CRC8 error")
     46 	}
     47 }
     48 
     49 func TestFailReadChipID(t *testing.T) {
     50 	bus := i2ctest.Playback{
     51 		// Chip ID detection read fail.
     52 		Ops:       []i2ctest.IO{},
     53 		DontPanic: true,
     54 	}
     55 	if _, err := New(&bus, "", time.Second); err == nil {
     56 		t.Fatal("can't read chip ID")
     57 	}
     58 }
     59 
     60 func TestBadSerialNumber(t *testing.T) {
     61 	bus := i2ctest.Playback{
     62 		Ops: []i2ctest.IO{
     63 			// Bad serial number
     64 			{Addr: 0x58, W: []byte{0x36, 0x82}, R: BadSerialNumber},
     65 		},
     66 	}
     67 	if _, err := New(&bus, "", time.Second); err == nil {
     68 		t.Fatal("Bad serial number Error")
     69 	}
     70 }
     71 
     72 func TestGoodSerialNumber(t *testing.T) {
     73 	bus := i2ctest.Playback{
     74 		Ops: []i2ctest.IO{
     75 			// Good serial number
     76 			{Addr: 0x58, W: []byte{0x36, 0x82}, R: GoodSerialNumber},
     77 		},
     78 	}
     79 	if _, err := New(&bus, "", time.Second); err != nil {
     80 		t.Fatalf("Good serial number Error: %s", err)
     81 	}
     82 }
     83 
     84 func TestBadBaselineData(t *testing.T) {
     85 	// Temporary baseline file, defer removal
     86 	bf, err := ioutil.TempFile("", "sgp30.")
     87 	if err != nil {
     88 		t.Fatalf("TempFile Error: %s", err)
     89 	}
     90 	defer os.Remove(bf.Name())
     91 
     92 	_, err = bf.Write(BadBaselineData)
     93 	if err != nil {
     94 		t.Fatalf("TempFile Write Error: %s", err)
     95 	}
     96 
     97 	// Calling New with a baseline reads the serial number, starts measurements,
     98 	// and then writes the baseline data
     99 	bus := i2ctest.Playback{
    100 		Ops: []i2ctest.IO{
    101 			// Good serial number
    102 			{Addr: 0x58, W: []byte{0x36, 0x82}, R: GoodSerialNumber},
    103 			{Addr: 0x58, W: []byte{0x20, 0x03}, R: []byte{}},
    104 			{Addr: 0x58, W: []byte{0x20, 0x15}, R: BadBaselineData},
    105 		},
    106 	}
    107 	if _, err := New(&bus, bf.Name(), time.Second); err == nil {
    108 		t.Fatal("Bad baseline data")
    109 	}
    110 }
    111 
    112 func TestGoodBaselineData(t *testing.T) {
    113 	// Temporary baseline file, defer removal
    114 	bf, err := ioutil.TempFile("", "sgp30.")
    115 	if err != nil {
    116 		t.Fatalf("TempFile Error: %s", err)
    117 	}
    118 	defer os.Remove(bf.Name())
    119 
    120 	_, err = bf.Write(GoodBaselineData)
    121 	if err != nil {
    122 		t.Fatalf("TempFile Write Error: %s", err)
    123 	}
    124 
    125 	// The CO2 and TVOC data is swapped when writing it back to the SGP30
    126 	BaselineWrite := append(append([]byte{0x20, 0x1e}, GoodBaselineData[3:6]...), GoodBaselineData[0:3]...)
    127 
    128 	// Calling New with a baseline reads the serial number, starts measurements,
    129 	// and then writes the baseline data
    130 	bus := i2ctest.Playback{
    131 		Ops: []i2ctest.IO{
    132 			// Good serial number
    133 			{Addr: 0x58, W: []byte{0x36, 0x82}, R: GoodSerialNumber},
    134 			{Addr: 0x58, W: []byte{0x20, 0x03}, R: []byte{}},
    135 			{Addr: 0x58, W: BaselineWrite, R: []byte{}},
    136 		},
    137 	}
    138 	if _, err := New(&bus, bf.Name(), time.Second); err != nil {
    139 		t.Fatalf("Good Baseline Error: %s", err)
    140 	}
    141 }
    142 
    143 func TestBadFeatures(t *testing.T) {
    144 	// Calling New with a baseline reads the serial number
    145 	bus := i2ctest.Playback{
    146 		Ops: []i2ctest.IO{
    147 			// Good serial number
    148 			{Addr: 0x58, W: []byte{0x36, 0x82}, R: GoodSerialNumber},
    149 			{Addr: 0x58, W: []byte{0x20, 0x2f}, R: BadFeaturesData},
    150 		},
    151 	}
    152 	d, err := New(&bus, "", time.Second)
    153 	if err != nil {
    154 		t.Fatalf("Bad Features: %s", err)
    155 	}
    156 	if _, _, err := d.GetFeatures(); err == nil {
    157 		t.Fatal("Bad Features Error")
    158 	}
    159 }
    160 
    161 func TestGoodFeatures(t *testing.T) {
    162 	// Calling New with a baseline reads the serial number
    163 	bus := i2ctest.Playback{
    164 		Ops: []i2ctest.IO{
    165 			// Good serial number
    166 			{Addr: 0x58, W: []byte{0x36, 0x82}, R: GoodSerialNumber},
    167 			{Addr: 0x58, W: []byte{0x20, 0x2f}, R: GoodFeaturesData},
    168 		},
    169 	}
    170 	d, err := New(&bus, "", time.Second)
    171 	if err != nil {
    172 		t.Fatalf("Good Features: %s", err)
    173 	}
    174 	ProdType, ProdVersion, err := d.GetFeatures()
    175 	if err != nil {
    176 		t.Fatalf("Good Features Error: %s", err)
    177 	}
    178 	if ProdType != 0x00 {
    179 		t.Error("Wrong Product type")
    180 	}
    181 	if ProdVersion != 0x22 {
    182 		t.Error("Wrong Product version")
    183 	}
    184 }
    185 
    186 func TestReadBadBaseline(t *testing.T) {
    187 	bus := i2ctest.Playback{
    188 		Ops: []i2ctest.IO{
    189 			// Good serial number
    190 			{Addr: 0x58, W: []byte{0x36, 0x82}, R: GoodSerialNumber},
    191 			{Addr: 0x58, W: []byte{0x20, 0x15}, R: BadBaselineData},
    192 		},
    193 	}
    194 	d, err := New(&bus, "", time.Second)
    195 	if err != nil {
    196 		t.Fatalf("Good serial number Error: %s", err)
    197 	}
    198 	if _, err := d.ReadBaseline(); err == nil {
    199 		t.Fatal("Read Bad Baseline Error")
    200 	}
    201 }
    202 
    203 func TestReadGoodBaseline(t *testing.T) {
    204 	bus := i2ctest.Playback{
    205 		Ops: []i2ctest.IO{
    206 			// Good serial number
    207 			{Addr: 0x58, W: []byte{0x36, 0x82}, R: GoodSerialNumber},
    208 			{Addr: 0x58, W: []byte{0x20, 0x15}, R: GoodBaselineData},
    209 		},
    210 	}
    211 	d, err := New(&bus, "", time.Second)
    212 	if err != nil {
    213 		t.Fatalf("Good serial number Error: %s", err)
    214 	}
    215 	if _, err := d.ReadBaseline(); err != nil {
    216 		t.Fatalf("Read Good Baseline Error: %s", err)
    217 	}
    218 }
    219 
    220 func TestBadAirQuality(t *testing.T) {
    221 	bus := i2ctest.Playback{
    222 		Ops: []i2ctest.IO{
    223 			// Good serial number
    224 			{Addr: 0x58, W: []byte{0x36, 0x82}, R: GoodSerialNumber},
    225 			{Addr: 0x58, W: []byte{0x20, 0x08}, R: []byte{}},
    226 			{Addr: 0x58, W: []byte{}, R: BadAirQualityData},
    227 		},
    228 	}
    229 	d, err := New(&bus, "", time.Second)
    230 	if err != nil {
    231 		t.Fatalf("Good serial number Error: %s", err)
    232 	}
    233 	if _, _, err := d.ReadAirQuality(); err == nil {
    234 		t.Fatalf("Read Bad AirQuality Error")
    235 	}
    236 }
    237 
    238 func TestGoodAirQuality(t *testing.T) {
    239 	bus := i2ctest.Playback{
    240 		Ops: []i2ctest.IO{
    241 			// Good serial number
    242 			{Addr: 0x58, W: []byte{0x36, 0x82}, R: GoodSerialNumber},
    243 			{Addr: 0x58, W: []byte{0x20, 0x08}, R: []byte{}},
    244 			{Addr: 0x58, W: []byte{}, R: GoodAirQualityData},
    245 		},
    246 	}
    247 	d, err := New(&bus, "", time.Second)
    248 	if err != nil {
    249 		t.Fatalf("Good serial number Error: %s", err)
    250 	}
    251 	co2, tvoc, err := d.ReadAirQuality()
    252 	if err != nil {
    253 		t.Fatalf("Read Good AirQuality Error: %s", err)
    254 	}
    255 	if co2 != 414 {
    256 		t.Error("CO2 reading is wrong")
    257 	}
    258 	if tvoc != 13 {
    259 		t.Error("TVOC reading is wrong")
    260 	}
    261 }