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 }