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  }