diff --git a/max6675/max6675.go b/max6675/max6675.go index eb67596b6..e016ce5da 100644 --- a/max6675/max6675.go +++ b/max6675/max6675.go @@ -3,7 +3,6 @@ package max6675 import ( "errors" - "machine" "tinygo.org/x/drivers" ) @@ -14,13 +13,13 @@ var ErrThermocoupleOpen = errors.New("thermocouple input open") type Device struct { bus drivers.SPI - cs machine.Pin + cs drivers.Pin } // Create a new Device to read from a MAX6675 thermocouple. // Pins must be configured before use. Frequency for SPI // should be 4.3MHz maximum. -func NewDevice(bus drivers.SPI, cs machine.Pin) *Device { +func NewDevice(bus drivers.SPI, cs drivers.Pin) *Device { return &Device{ bus: bus, cs: cs, diff --git a/max6675/max6675_test.go b/max6675/max6675_test.go new file mode 100644 index 000000000..3e77ae430 --- /dev/null +++ b/max6675/max6675_test.go @@ -0,0 +1,48 @@ +package max6675_test + +import ( + "testing" + + qt "github.com/frankban/quicktest" + "tinygo.org/x/drivers/max6675" + "tinygo.org/x/drivers/tester" +) + +func Test_MAX6675_Read(t *testing.T) { + c := qt.New(t) + spi := tester.NewSPIBus(c) + cs := tester.NewPin(c) + + var expectedCelsius float32 = 20.25 + + temp := uint16(expectedCelsius/0.25) << 3 + + cs.ExpectSet(false) + spi.Expect( + []byte{0, 0}, + []byte{byte(temp >> 8), byte(temp)}, + ) + cs.ExpectSet(true) + + dev := max6675.NewDevice(spi, cs) + + actual, err := dev.Read() + c.Assert(err, qt.Equals, nil) + c.Assert(actual, qt.Equals, expectedCelsius) +} + +func Test_MAX6675_Read_ErrThermocoupleOpen(t *testing.T) { + c := qt.New(t) + spi := tester.NewSPIBus(c) + + spi.Expect( + []byte{0, 0}, + []byte{0, 0x04}, + ) + + dev := max6675.NewDevice(spi, tester.NewNoopPin()) + + actual, err := dev.Read() + c.Assert(err, qt.Equals, max6675.ErrThermocoupleOpen) + c.Assert(actual, qt.Equals, float32(0)) +} diff --git a/pin.go b/pin.go new file mode 100644 index 000000000..7442a339e --- /dev/null +++ b/pin.go @@ -0,0 +1,8 @@ +package drivers + +type Pin interface { + Get() bool + High() + Low() + Set(value bool) +} diff --git a/tester/pin.go b/tester/pin.go new file mode 100644 index 000000000..f688a4581 --- /dev/null +++ b/tester/pin.go @@ -0,0 +1,77 @@ +package tester + +import "tinygo.org/x/drivers" + +var _ drivers.Pin = (*Pin)(nil) +var _ drivers.Pin = (*NoopPin)(nil) + +type pinExpectation struct { + get bool + value bool +} + +type Pin struct { + c Failer + expectations []pinExpectation +} + +// Expect a Get() call, and return the provided value. +func (p *Pin) ExpectGet(value bool) { + p.expectations = append(p.expectations, pinExpectation{get: true, value: value}) +} + +// Expect a Set(bool) call, with the provided value. +// true is High, false is Low +func (p *Pin) ExpectSet(value bool) { + p.expectations = append(p.expectations, pinExpectation{get: false, value: value}) +} + +func (p *Pin) Get() bool { + if len(p.expectations) == 0 { + p.c.Fatalf("unexpected pin read") + } + ex := p.expectations[0] + if !ex.get { + p.c.Fatalf("unexpected pin read") + } + p.expectations = p.expectations[1:] + return ex.value +} + +func (p *Pin) Set(value bool) { + if len(p.expectations) == 0 { + p.c.Fatalf("unexpected pin write") + } + ex := p.expectations[0] + if ex.get { + p.c.Fatalf("unexpected pin write") + } + if ex.value != value { + p.c.Fatalf("unexpected pin write: got %v, expecting %v", value, ex.value) + } + p.expectations = p.expectations[1:] +} + +func (p *Pin) High() { + p.Set(true) +} + +func (p *Pin) Low() { + p.Set(false) +} + +func NewPin(c Failer) *Pin { + return &Pin{c, []pinExpectation{}} +} + +// NoopPin is a pin that does nothing, and always returns true for Get() +type NoopPin struct{} + +func NewNoopPin() *NoopPin { + return &NoopPin{} +} + +func (n *NoopPin) Get() bool { return true } +func (n *NoopPin) High() {} +func (n *NoopPin) Low() {} +func (n *NoopPin) Set(high bool) {} diff --git a/tester/spi.go b/tester/spi.go new file mode 100644 index 000000000..7095c9a38 --- /dev/null +++ b/tester/spi.go @@ -0,0 +1,48 @@ +package tester + +import ( + "bytes" +) + +type spiExpectation struct { + write []byte + read []byte +} + +type SPIBus struct { + c Failer + expectations []spiExpectation +} + +func NewSPIBus(c Failer) *SPIBus { + return &SPIBus{ + c: c, + expectations: []spiExpectation{}, + } +} + +func (s *SPIBus) Tx(w, r []byte) error { + if len(s.expectations) == 0 { + s.c.Fatalf("unexpected SPI exchange") + } + ex := s.expectations[0] + if !bytes.Equal(ex.write, w) { + s.c.Fatalf("unexpected SPI write: got %#v, expecting %#v", w, ex.write) + } + copy(r, ex.read) + s.expectations = s.expectations[1:] + return nil +} + +func (s *SPIBus) Transfer(b byte) (byte, error) { + buf := make([]byte, 1) + err := s.Tx([]byte{b}, buf) + return buf[0], err +} + +func (s *SPIBus) Expect(in []byte, out []byte) { + if len(in) != len(out) { + s.c.Fatalf("Expect: input and output slices must be the same length") + } + s.expectations = append(s.expectations, spiExpectation{in, out}) +}