gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/pca953x_driver_test.go (about) 1 package i2c 2 3 import ( 4 "fmt" 5 "math" 6 "strings" 7 "testing" 8 9 "gobot.io/x/gobot/v2" 10 "gobot.io/x/gobot/v2/gobottest" 11 ) 12 13 // this ensures that the implementation is based on i2c.Driver, which implements the gobot.Driver 14 // and tests all implementations, so no further tests needed here for gobot.Driver interface 15 var _ gobot.Driver = (*PCA953xDriver)(nil) 16 17 func initPCA953xTestDriverWithStubbedAdaptor() (*PCA953xDriver, *i2cTestAdaptor) { 18 a := newI2cTestAdaptor() 19 d := NewPCA953xDriver(a) 20 d.Start() 21 return d, a 22 } 23 24 func TestNewPCA953xDriver(t *testing.T) { 25 // arrange, act 26 var di interface{} = NewPCA953xDriver(newI2cTestAdaptor()) 27 // assert 28 d, ok := di.(*PCA953xDriver) 29 if !ok { 30 t.Errorf("NewPCA953xDriver() should have returned a *PCA953xDriver") 31 } 32 gobottest.Refute(t, d.Driver, nil) 33 gobottest.Assert(t, strings.HasPrefix(d.Name(), "PCA953x"), true) 34 gobottest.Assert(t, d.defaultAddress, 0x63) 35 } 36 37 func TestPCA953xWriteGPIO(t *testing.T) { 38 // sequence to write: 39 // * choose LED select register according to the given GPIO index (0x05 for 0..3, 0x06 for 4..7) 40 // * read current state of LED select register (write reg, read val) 41 // * modify 2 bits according to given index of GPIO 42 // * write the new state to the LED select register (write reg, write val) 43 var tests = map[string]struct { 44 idx uint8 45 ls0State uint8 46 ls1State uint8 47 val uint8 48 wantWritten []uint8 49 wantErr error 50 }{ 51 "out_0_0": { 52 idx: 0, 53 ls0State: 0xFE, 54 ls1State: 0xAF, 55 val: 0, 56 wantWritten: []byte{0x05, 0x05, 0xFD}, // set lowest bits to "01" for ls0 57 }, 58 "out_0_1": { 59 idx: 0, 60 ls0State: 0xFF, 61 ls1State: 0xAF, 62 val: 1, 63 wantWritten: []byte{0x05, 0x05, 0xFC}, // set lowest bits to "00" for ls0 64 }, 65 "out_5_0": { 66 idx: 5, 67 ls0State: 0xAF, 68 ls1State: 0xFB, 69 val: 0, 70 wantWritten: []byte{0x06, 0x06, 0xF7}, // set bit 2,3 to "01" for ls1 71 }, 72 "out_5_1": { 73 idx: 5, 74 ls0State: 0xAF, 75 ls1State: 0xFF, 76 val: 1, 77 wantWritten: []byte{0x06, 0x06, 0xF3}, // set bit 2,3 to "00" for ls1 78 }, 79 "read_error": { 80 idx: 3, 81 wantWritten: []byte{0x05}, 82 wantErr: fmt.Errorf("a read error"), 83 }, 84 } 85 for name, tc := range tests { 86 t.Run(name, func(t *testing.T) { 87 // arrange 88 d, a := initPCA953xTestDriverWithStubbedAdaptor() 89 a.written = []byte{} // reset writes of Start() and former test 90 a.i2cReadImpl = func(b []byte) (int, error) { 91 if a.written[0] == 0x05 { 92 b[0] = tc.ls0State 93 } 94 if a.written[0] == 0x06 { 95 b[0] = tc.ls1State 96 } 97 return 1, tc.wantErr 98 } 99 // act 100 err := d.WriteGPIO(tc.idx, tc.val) 101 // assert 102 gobottest.Assert(t, err, tc.wantErr) 103 gobottest.Assert(t, a.written, tc.wantWritten) 104 }) 105 } 106 } 107 108 func TestPCA953xReadGPIO(t *testing.T) { 109 // sequence to read: 110 // * read current state of INPUT register (write reg 0x00, read val) 111 // * convert bit position to output value 112 var tests = map[string]struct { 113 idx uint8 114 want uint8 115 wantErr error 116 }{ 117 "in_0_0": { 118 idx: 0, 119 want: 0, 120 }, 121 "in_0_1": { 122 idx: 0, 123 want: 1, 124 }, 125 "in_2_0": { 126 idx: 2, 127 want: 0, 128 }, 129 "in_2_1": { 130 idx: 2, 131 want: 1, 132 }, 133 "in_7_0": { 134 idx: 7, 135 want: 0, 136 }, 137 "in_7_1": { 138 idx: 7, 139 want: 1, 140 }, 141 "read_error": { 142 idx: 2, 143 want: 0, 144 wantErr: fmt.Errorf("a read error"), 145 }, 146 } 147 for name, tc := range tests { 148 t.Run(name, func(t *testing.T) { 149 // arrange 150 const wantReg = uint8(0x00) // input register 151 d, a := initPCA953xTestDriverWithStubbedAdaptor() 152 a.written = []byte{} // reset writes of Start() and former test 153 bits := tc.want << tc.idx 154 a.i2cReadImpl = func(b []byte) (int, error) { 155 b[0] = bits 156 return 1, tc.wantErr 157 } 158 // act 159 got, err := d.ReadGPIO(tc.idx) 160 // assert 161 gobottest.Assert(t, err, tc.wantErr) 162 gobottest.Assert(t, len(a.written), 1) 163 gobottest.Assert(t, a.written[0], wantReg) 164 gobottest.Assert(t, got, tc.want) 165 }) 166 } 167 } 168 169 func TestPCA953xWritePeriod(t *testing.T) { 170 // sequence to write: 171 // * calculate PSC value (0..255) from given value in seconds, valid values are 0.00658 ... 1.68 [s] 172 // * choose PSC0 (0x01) or PSC1 (0x03) frequency prescaler register by the given index 173 // * write the value to the register (write reg, write val) 174 var tests = map[string]struct { 175 idx uint8 176 val float32 177 wantWritten []uint8 178 }{ 179 "write_ok_psc0": { 180 idx: 0, 181 val: 1, 182 wantWritten: []byte{0x01, 151}, 183 }, 184 "write_ok_psc1": { 185 idx: 2, 186 val: 0.5, 187 wantWritten: []byte{0x03, 75}, 188 }, 189 "write_shrinked_noerror": { 190 idx: 0, 191 val: 2, 192 wantWritten: []byte{0x01, 255}, 193 }, 194 } 195 for name, tc := range tests { 196 t.Run(name, func(t *testing.T) { 197 // arrange 198 d, a := initPCA953xTestDriverWithStubbedAdaptor() 199 a.written = []byte{} // reset writes of Start() and former test 200 // act 201 err := d.WritePeriod(tc.idx, tc.val) 202 // assert 203 gobottest.Assert(t, err, nil) 204 gobottest.Assert(t, a.written, tc.wantWritten) 205 }) 206 } 207 } 208 209 func TestPCA953xReadPeriod(t *testing.T) { 210 // sequence to write: 211 // * choose PSC0 (0x01) or PSC1 (0x03) frequency prescaler register by the given index 212 // * read the value from the register (write reg, write val) 213 // * calculate value in seconds from PSC value 214 var tests = map[string]struct { 215 idx uint8 216 val uint8 217 want float32 218 wantWritten []uint8 219 wantErr error 220 }{ 221 "read_ok_psc0": { 222 idx: 0, 223 val: 151, 224 want: 1, 225 wantWritten: []byte{0x01}, 226 }, 227 "read_ok_psc1": { 228 idx: 1, 229 val: 75, 230 want: 0.5, 231 wantWritten: []byte{0x03}, 232 }, 233 "read_error": { 234 idx: 5, 235 val: 75, 236 want: -1, 237 wantWritten: []byte{0x03}, 238 wantErr: fmt.Errorf("read psc error"), 239 }, 240 } 241 for name, tc := range tests { 242 t.Run(name, func(t *testing.T) { 243 // arrange 244 d, a := initPCA953xTestDriverWithStubbedAdaptor() 245 a.written = []byte{} // reset writes of Start() and former test 246 a.i2cReadImpl = func(b []byte) (int, error) { 247 b[0] = tc.val 248 return 1, tc.wantErr 249 } 250 // act 251 got, err := d.ReadPeriod(tc.idx) 252 // assert 253 gobottest.Assert(t, err, tc.wantErr) 254 gobottest.Assert(t, got, tc.want) 255 gobottest.Assert(t, a.written, tc.wantWritten) 256 }) 257 } 258 } 259 260 func TestPCA953xWriteFrequency(t *testing.T) { 261 // sequence to write: 262 // * calculate PSC value (0..255) from given value in Hz, valid values are 0.6 ... 152 [Hz] 263 // * choose PSC0 (0x01) or PSC1 (0x03) frequency prescaler register by the given index 264 // * write the value to the register (write reg, write val) 265 var tests = map[string]struct { 266 idx uint8 267 val float32 268 wantWritten []uint8 269 }{ 270 "write_ok_psc0": { 271 idx: 0, 272 val: 1, 273 wantWritten: []byte{0x01, 151}, 274 }, 275 "write_ok_psc1": { 276 idx: 5, 277 val: 2, 278 wantWritten: []byte{0x03, 75}, 279 }, 280 "write_shrinked_noerror": { 281 idx: 0, 282 val: 153, 283 wantWritten: []byte{0x01, 0}, 284 }, 285 } 286 for name, tc := range tests { 287 t.Run(name, func(t *testing.T) { 288 // arrange 289 d, a := initPCA953xTestDriverWithStubbedAdaptor() 290 a.written = []byte{} // reset writes of Start() and former test 291 // act 292 err := d.WriteFrequency(tc.idx, tc.val) 293 // assert 294 gobottest.Assert(t, err, nil) 295 gobottest.Assert(t, a.written, tc.wantWritten) 296 }) 297 } 298 } 299 300 func TestPCA953xReadFrequency(t *testing.T) { 301 // sequence to write: 302 // * choose PSC0 (0x01) or PSC1 (0x03) frequency prescaler register by the given index 303 // * read the value from the register (write reg, write val) 304 // * calculate value in Hz from PSC value 305 var tests = map[string]struct { 306 idx uint8 307 val uint8 308 want float32 309 wantWritten []uint8 310 wantErr error 311 }{ 312 "read_ok_psc0": { 313 idx: 0, 314 val: 75, 315 want: 2, 316 wantWritten: []byte{0x01}, 317 }, 318 "read_ok_psc1": { 319 idx: 1, 320 val: 151, 321 want: 1, 322 wantWritten: []byte{0x03}, 323 }, 324 "read_error": { 325 idx: 3, 326 val: 75, 327 want: -1, 328 wantWritten: []byte{0x03}, 329 wantErr: fmt.Errorf("read psc error"), 330 }, 331 } 332 for name, tc := range tests { 333 t.Run(name, func(t *testing.T) { 334 // arrange 335 d, a := initPCA953xTestDriverWithStubbedAdaptor() 336 a.written = []byte{} // reset writes of Start() and former test 337 a.i2cReadImpl = func(b []byte) (int, error) { 338 b[0] = tc.val 339 return 1, tc.wantErr 340 } 341 // act 342 got, err := d.ReadFrequency(tc.idx) 343 // assert 344 gobottest.Assert(t, err, tc.wantErr) 345 gobottest.Assert(t, got, tc.want) 346 gobottest.Assert(t, a.written, tc.wantWritten) 347 }) 348 } 349 } 350 351 func TestPCA953xWriteDutyCyclePercent(t *testing.T) { 352 // sequence to write: 353 // * calculate PWM value (0..255) from given value in percent, valid values are 0 ... 100 [%] 354 // * choose PWM0 (0x02) or PWM1 (0x04) pwm register by the given index 355 // * write the value to the register (write reg, write val) 356 var tests = map[string]struct { 357 idx uint8 358 val float32 359 wantWritten []uint8 360 }{ 361 "write_ok_pwm0": { 362 idx: 0, 363 val: 10, 364 wantWritten: []byte{0x02, 26}, 365 }, 366 "write_ok_pwm1": { 367 idx: 5, 368 val: 50, 369 wantWritten: []byte{0x04, 128}, 370 }, 371 "write_shrinked_noerror": { 372 idx: 1, 373 val: 101, 374 wantWritten: []byte{0x04, 255}, 375 }, 376 } 377 for name, tc := range tests { 378 t.Run(name, func(t *testing.T) { 379 // arrange 380 d, a := initPCA953xTestDriverWithStubbedAdaptor() 381 a.written = []byte{} // reset writes of Start() and former test 382 // act 383 err := d.WriteDutyCyclePercent(tc.idx, tc.val) 384 // assert 385 gobottest.Assert(t, err, nil) 386 gobottest.Assert(t, a.written, tc.wantWritten) 387 }) 388 } 389 } 390 391 func TestPCA953xReadDutyCyclePercent(t *testing.T) { 392 // sequence to write: 393 // * choose PWM0 (0x02) or PWM1 (0x04) pwm register by the given index 394 // * read the value from the register (write reg, write val) 395 // * calculate value percent from PWM value 396 var tests = map[string]struct { 397 idx uint8 398 val uint8 399 want float32 400 wantWritten []uint8 401 wantErr error 402 }{ 403 "read_ok_psc0": { 404 idx: 0, 405 val: 128, 406 want: 50.19608, 407 wantWritten: []byte{0x02}, 408 }, 409 "read_ok_psc1": { 410 idx: 1, 411 val: 26, 412 want: 10.196078, 413 wantWritten: []byte{0x04}, 414 }, 415 "read_error": { 416 idx: 0, 417 val: 75, 418 want: -1, 419 wantWritten: []byte{0x02}, 420 wantErr: fmt.Errorf("read psc error"), 421 }, 422 } 423 for name, tc := range tests { 424 t.Run(name, func(t *testing.T) { 425 // arrange 426 d, a := initPCA953xTestDriverWithStubbedAdaptor() 427 a.written = []byte{} // reset writes of Start() and former test 428 a.i2cReadImpl = func(b []byte) (int, error) { 429 b[0] = tc.val 430 return 1, tc.wantErr 431 } 432 // act 433 got, err := d.ReadDutyCyclePercent(tc.idx) 434 // assert 435 gobottest.Assert(t, err, tc.wantErr) 436 gobottest.Assert(t, got, tc.want) 437 gobottest.Assert(t, a.written, tc.wantWritten) 438 }) 439 } 440 } 441 442 func TestPCA953x_readRegister(t *testing.T) { 443 // arrange 444 const ( 445 wantRegAddress = pca953xRegister(0x03) 446 wantReadByteCount = 1 447 wantRegVal = uint8(0x04) 448 ) 449 readByteCount := 0 450 d, a := initPCA953xTestDriverWithStubbedAdaptor() 451 // prepare all writes 452 numCallsWrite := 0 453 a.i2cWriteImpl = func([]byte) (int, error) { 454 numCallsWrite++ 455 return 0, nil 456 } 457 // prepare all reads 458 numCallsRead := 0 459 a.i2cReadImpl = func(b []byte) (int, error) { 460 numCallsRead++ 461 readByteCount = len(b) 462 b[0] = wantRegVal 463 return readByteCount, nil 464 } 465 // act 466 val, err := d.readRegister(wantRegAddress) 467 // assert 468 gobottest.Assert(t, err, nil) 469 gobottest.Assert(t, numCallsRead, 1) 470 gobottest.Assert(t, numCallsWrite, 1) 471 gobottest.Assert(t, val, wantRegVal) 472 gobottest.Assert(t, readByteCount, wantReadByteCount) 473 gobottest.Assert(t, len(a.written), 1) 474 gobottest.Assert(t, a.written[0], uint8(wantRegAddress)) 475 } 476 477 func TestPCA953x_writeRegister(t *testing.T) { 478 // arrange 479 const ( 480 wantRegAddress = pca953xRegister(0x03) 481 wantRegVal = uint8(0x97) 482 wantByteCount = 2 483 ) 484 d, a := initPCA953xTestDriverWithStubbedAdaptor() 485 // prepare all writes 486 numCallsWrite := 0 487 a.i2cWriteImpl = func(b []byte) (int, error) { 488 numCallsWrite++ 489 return 0, nil 490 } 491 // act 492 err := d.writeRegister(wantRegAddress, wantRegVal) 493 // assert 494 gobottest.Assert(t, err, nil) 495 gobottest.Assert(t, numCallsWrite, 1) 496 gobottest.Assert(t, numCallsWrite, 1) 497 gobottest.Assert(t, len(a.written), wantByteCount) 498 gobottest.Assert(t, a.written[0], uint8(wantRegAddress)) 499 gobottest.Assert(t, a.written[1], uint8(wantRegVal)) 500 } 501 502 func TestPCA953x_pca953xCalcPsc(t *testing.T) { 503 // arrange 504 tests := map[string]struct { 505 period float32 506 want uint8 507 wantErr error 508 }{ 509 "error_to_small": {period: 0.0065, want: 0, wantErr: errToSmallPeriod}, 510 "minimum": {period: 0.0066, want: 0, wantErr: nil}, 511 "one": {period: 1, want: 151, wantErr: nil}, 512 "maximum": {period: 1.684, want: 255, wantErr: nil}, 513 "error_to_big5": {period: 1.685, want: 255, wantErr: errToBigPeriod}, 514 } 515 for name, tc := range tests { 516 t.Run(name, func(t *testing.T) { 517 // act 518 val, err := pca953xCalcPsc(tc.period) 519 // assert 520 gobottest.Assert(t, err, tc.wantErr) 521 gobottest.Assert(t, val, tc.want) 522 }) 523 } 524 } 525 526 func TestPCA953x_pca953xCalcPeriod(t *testing.T) { 527 // arrange 528 tests := map[string]struct { 529 psc uint8 530 want float32 531 }{ 532 "minimum": {psc: 0, want: 0.0066}, 533 "one": {psc: 1, want: 0.0132}, 534 "one_want": {psc: 151, want: 1}, 535 "maximum": {psc: 255, want: 1.6842}, 536 } 537 for name, tc := range tests { 538 t.Run(name, func(t *testing.T) { 539 // act 540 val := pca953xCalcPeriod(tc.psc) 541 // assert 542 gobottest.Assert(t, float32(math.Round(float64(val)*10000)/10000), tc.want) 543 }) 544 } 545 } 546 547 func TestPCA953x_pca953xCalcPwm(t *testing.T) { 548 // arrange 549 tests := map[string]struct { 550 percent float32 551 want uint8 552 wantErr error 553 }{ 554 "error_to_small": {percent: -0.1, want: 0, wantErr: errToSmallDutyCycle}, 555 "zero": {percent: 0, want: 0, wantErr: nil}, 556 "below_medium": {percent: 49.9, want: 127, wantErr: nil}, 557 "medium": {percent: 50, want: 128, wantErr: nil}, 558 "maximum": {percent: 100, want: 255, wantErr: nil}, 559 "error_to_big": {percent: 100.1, want: 255, wantErr: errToBigDutyCycle}, 560 } 561 for name, tc := range tests { 562 t.Run(name, func(t *testing.T) { 563 // act 564 val, err := pca953xCalcPwm(tc.percent) 565 // assert 566 gobottest.Assert(t, err, tc.wantErr) 567 gobottest.Assert(t, val, tc.want) 568 }) 569 } 570 } 571 572 func TestPCA953x_pca953xCalcDutyCyclePercent(t *testing.T) { 573 // arrange 574 tests := map[string]struct { 575 pwm uint8 576 want float32 577 }{ 578 "minimum": {pwm: 0, want: 0}, 579 "below_medium": {pwm: 127, want: 49.8}, 580 "medium": {pwm: 128, want: 50.2}, 581 "maximum": {pwm: 255, want: 100}, 582 } 583 for name, tc := range tests { 584 t.Run(name, func(t *testing.T) { 585 // act 586 val := pca953xCalcDutyCyclePercent(tc.pwm) 587 // assert 588 gobottest.Assert(t, float32(math.Round(float64(val)*10)/10), tc.want) 589 }) 590 } 591 }