devices: Add devices.Device and Halt() for all device drivers

This starts a semblance of coherence for drivers. In particular Halt() marks a
clear difference from Close (connection) and is the term used by gobot.
pull/1/head
Marc-Antoine Ruel 9 years ago
parent 860d4aa457
commit fbf842d637

@ -6,6 +6,7 @@ package apa102
import (
"errors"
"fmt"
"image"
"image/color"
@ -224,6 +225,10 @@ type Dev struct {
pixels []byte
}
func (d *Dev) String() string {
return fmt.Sprintf("APA102{I:%d, T:%dK, %dLEDs, %s}", d.Intensity, d.Temperature, d.numLights, d.s)
}
// ColorModel implements devices.Display. There's no surprise, it is
// color.NRGBAModel.
func (d *Dev) ColorModel() color.Model {
@ -262,7 +267,7 @@ func (d *Dev) Write(pixels []byte) (int, error) {
}
d.l.init(d.Intensity, d.Temperature)
// Trying to write more pixels than defined?
if o := len(d.pixels) / 4; o < len(pixels)/3 {
if o := d.numLights; o < len(pixels)/3 {
pixels = pixels[:o*3]
}
// Do not touch header and footer.
@ -271,6 +276,12 @@ func (d *Dev) Write(pixels []byte) (int, error) {
return len(pixels), err
}
// Halt turns off all the lights.
func (d *Dev) Halt() error {
_, err := d.Write(make([]byte, d.numLights*3))
return err
}
// New returns a strip that communicates over SPI to APA102 LEDs.
//
// The SPI bus speed should be high, at least in the Mhz range, as
@ -309,3 +320,4 @@ func New(s spi.Conn, numLights int, intensity uint8, temperature uint16) (*Dev,
var errLength = errors.New("apa102: invalid RGB stream length")
var _ devices.Display = &Dev{}
var _ devices.Device = &Dev{}

@ -14,6 +14,7 @@ import (
"log"
"testing"
"periph.io/x/periph/conn/conntest"
"periph.io/x/periph/conn/spi"
"periph.io/x/periph/conn/spi/spireg"
"periph.io/x/periph/conn/spi/spitest"
@ -334,6 +335,9 @@ func TestDevEmpty(t *testing.T) {
if expected := []byte{0x0, 0x0, 0x0, 0x0, 0xFF}; !bytes.Equal(expected, buf.Bytes()) {
t.Fatalf("%#v != %#v", expected, buf.Bytes())
}
if s := d.String(); s != "APA102{I:255, T:6500K, 0LEDs, recordraw}" {
t.Fatal(s)
}
}
func TestDevParamsFail(t *testing.T) {
@ -547,6 +551,23 @@ func TestDrawRGBA(t *testing.T) {
}
}
func TestHalt(t *testing.T) {
bus := spitest.Playback{
Playback: conntest.Playback{
Ops: []conntest.IO{
{W: []byte{0x0, 0x0, 0x0, 0x0, 0xe1, 0x0, 0x0, 0x0, 0xe1, 0x0, 0x0, 0x0, 0xe1, 0x0, 0x0, 0x0, 0xe1, 0x0, 0x0, 0x0, 0xff}},
},
},
}
d, _ := New(&bus, 4, 250, 5000)
if err := d.Halt(); err != nil {
t.Fatal(err)
}
if err := bus.Close(); err != nil {
t.Fatal(err)
}
}
func TestInit(t *testing.T) {
// Catch the "maxB == maxG" line.
l := lut{}

@ -77,6 +77,10 @@ type Dev struct {
c calibration
}
func (d *Dev) String() string {
return fmt.Sprintf("BME280{%s}", d.d)
}
// Sense returns measurements as °C, kPa and % of relative humidity.
func (d *Dev) Sense(env *devices.Environment) error {
// All registers must be read in a single pass, as noted at page 21, section
@ -105,9 +109,10 @@ func (d *Dev) Sense(env *devices.Environment) error {
return nil
}
// Stop stops the bme280 from acquiring measurements. It is recommended to call
// to reduce idle power usage.
func (d *Dev) Stop() error {
// Halt stops the bme280 from acquiring measurements.
//
// It is recommended to call to reduce idle power usage.
func (d *Dev) Halt() error {
// Page 27 (for register) and 12~13 section 3.3.
return d.writeCommands([]byte{0xF4, byte(sleep)})
}
@ -397,3 +402,4 @@ func (c *calibration) compensateHumidityInt(raw, tFine int32) uint32 {
}
var _ devices.Environmental = &Dev{}
var _ devices.Device = &Dev{}

@ -79,6 +79,9 @@ func TestSPISense_success(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if s := dev.String(); s != "BME280{playback}" {
t.Fatal(s)
}
env := devices.Environment{}
if err := dev.Sense(&env); err != nil {
t.Fatal(err)
@ -149,7 +152,7 @@ func TestNewSPI_fail_chipid(t *testing.T) {
}
}
func TestNewI2C_fail(t *testing.T) {
func TestNewI2C_fail_io(t *testing.T) {
bus := i2ctest.Playback{
Ops: []i2ctest.IO{
// Chipd ID detection.
@ -167,7 +170,7 @@ func TestNewI2C_fail(t *testing.T) {
}
}
func TestNewI2C_chipid(t *testing.T) {
func TestNewI2C_fail_chipid(t *testing.T) {
bus := i2ctest.Playback{
Ops: []i2ctest.IO{
// Chipd ID detection.
@ -305,7 +308,7 @@ func TestI2CSense_success(t *testing.T) {
{Addr: 0x76, W: []byte{0xf4, 0x6c, 0xf2, 0x3, 0xf5, 0xe0, 0xf4, 0x6f}, R: nil},
// Read.
{Addr: 0x76, W: []byte{0xf7}, R: []byte{0x4a, 0x52, 0xc0, 0x80, 0x96, 0xc0, 0x7a, 0x76}},
// Stop.
// Halt.
{Addr: 0x76, W: []byte{0xf4, 0x0}},
},
}
@ -313,6 +316,9 @@ func TestI2CSense_success(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if s := dev.String(); s != "BME280{playback(118)}" {
t.Fatal(s)
}
env := devices.Environment{}
if err := dev.Sense(&env); err != nil {
t.Fatal(err)
@ -326,7 +332,7 @@ func TestI2CSense_success(t *testing.T) {
if env.Humidity != 6531 {
t.Fatalf("humidity %d", env.Humidity)
}
if err := dev.Stop(); err != nil {
if err := dev.Halt(); err != nil {
t.Fatal(err)
}
if err := bus.Close(); err != nil {

@ -133,7 +133,7 @@ func run(i2cBus i2c.Bus, spiBus spi.ConnCloser) (err error) {
return err2
}
defer func() {
if err2 := i2cDev.Stop(); err == nil {
if err2 := i2cDev.Halt(); err == nil {
err = err2
}
}()
@ -143,7 +143,7 @@ func run(i2cBus i2c.Bus, spiBus spi.ConnCloser) (err error) {
return err2
}
defer func() {
if err2 := spiDev.Stop(); err == nil {
if err2 := spiDev.Halt(); err == nil {
err = err2
}
}()

@ -11,6 +11,18 @@ import (
"io"
)
// Device is a basic device.
type Device interface {
fmt.Stringer
// Halt stops the device.
//
// Unlike a connection, a device cannot be closed, only the port can be
// closed. On the other hand, a device can be halted. What halting entails
// depends on the actual device but it should stop motion, sensing or light
// emition.
Halt() error
}
// Display represents a pixel output device. It is a write-only interface.
//
// What Display represents can be as varied as a 1 bit OLED display or a strip

@ -23,6 +23,7 @@ package ds18b20
import (
"errors"
"fmt"
"time"
"periph.io/x/periph/conn/onewire"
@ -93,6 +94,15 @@ type Dev struct {
resolution int // resolution in bits (9..12)
}
func (d *Dev) String() string {
return fmt.Sprintf("DS18B20{%v}", d.onewire)
}
// Halt implements devices.Device.
func (d *Dev) Halt() error {
return nil
}
// Temperature performs a conversion and returns the temperature.
func (d *Dev) Temperature() (devices.Celsius, error) {
if err := d.onewire.TxPower([]byte{0x44}, nil); err != nil {
@ -163,3 +173,5 @@ func (d *Dev) readScratchpad() ([]byte, error) {
return spad[:8], nil
}
var _ devices.Device = &Dev{}

@ -13,7 +13,7 @@ import (
"periph.io/x/periph/devices"
)
func TestNew_resolution(t *testing.T) {
func TestNew_fail_resolution(t *testing.T) {
bus := &onewiretest.Playback{}
var addr onewire.Address = 0x740000070e41ac28
if d, err := New(bus, addr, 1); d != nil || err == nil {
@ -21,7 +21,7 @@ func TestNew_resolution(t *testing.T) {
}
}
func TestNew_read(t *testing.T) {
func TestNew_fail_read(t *testing.T) {
bus := &onewiretest.Playback{DontPanic: true}
var addr onewire.Address = 0x740000070e41ac28
if d, err := New(bus, addr, 9); d != nil || err == nil {
@ -53,14 +53,16 @@ func TestTemperature(t *testing.T) {
var addr onewire.Address = 0x740000070e41ac28
var temp devices.Celsius = 30000 // 30.000°C
bus := onewiretest.Playback{Ops: ops}
// Init the ds18b20.
ds18b20, err := New(&bus, addr, 10)
dev, err := New(&bus, addr, 10)
if err != nil {
t.Fatal(err)
}
if s := dev.String(); s != "DS18B20{{playback 8358680938703596584}}" {
t.Fatal(s)
}
// Read the temperature.
t0 := time.Now()
now, err := ds18b20.Temperature()
now, err := dev.Temperature()
dt := time.Since(t0)
if err != nil {
t.Fatal(err)
@ -73,6 +75,9 @@ func TestTemperature(t *testing.T) {
if dt < 188*time.Millisecond {
t.Errorf("expected conversion to take >187ms, took %s", dt)
}
if err := dev.Halt(); err != nil {
t.Fatal(err)
}
if err := bus.Close(); err != nil {
t.Fatal(err)
}
@ -101,17 +106,17 @@ func TestConvertAll(t *testing.T) {
}
}
func TestConvertAll_resolution(t *testing.T) {
func TestConvertAll_fail_resolution(t *testing.T) {
bus := &onewiretest.Playback{}
if err := ConvertAll(bus, 1); err == nil {
t.Fatal("invalid resolution")
}
}
func TestConvertAll_fail(t *testing.T) {
func TestConvertAll_fail_io(t *testing.T) {
bus := &onewiretest.Playback{DontPanic: true}
if err := ConvertAll(bus, 9); err == nil {
t.Fatal("invalid resolution")
t.Fatal("invalid io")
}
}

@ -11,6 +11,7 @@ import (
"periph.io/x/periph/conn"
"periph.io/x/periph/conn/onewire"
"periph.io/x/periph/devices"
)
// Dev is a handle to a ds248x device and it implements the onewire.Bus
@ -36,7 +37,15 @@ type Dev struct {
}
func (d *Dev) String() string {
return "ds248x"
if d.isDS2483 {
return fmt.Sprintf("DS2483{%s}", d.i2c)
}
return fmt.Sprintf("DS2482-100{%s}", d.i2c)
}
// Halt implements devices.Device.
func (d *Dev) Halt() error {
return nil
}
// Tx performs a bus transaction, sending and receiving bytes, and ending by
@ -187,3 +196,5 @@ type busError string
func (e busError) Error() string { return string(e) }
func (e busError) BusError() bool { return true }
var _ devices.Device = &Dev{}

@ -54,9 +54,12 @@ func TestNew(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if s := d.String(); s != "ds248x" {
if s := d.String(); s != "DS2483{playback(24)}" {
t.Fatal(s)
}
if err := d.Halt(); err != nil {
t.Fatal(err)
}
if err := bus.Close(); err != nil {
t.Fatal(err)
}

@ -488,3 +488,4 @@ const (
)
var _ devices.Display = &Dev{}
var _ devices.Device = &Dev{}

@ -11,10 +11,12 @@ package tm1637
import (
"errors"
"fmt"
"runtime"
"time"
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/devices"
"periph.io/x/periph/host/cpu"
)
@ -67,6 +69,10 @@ type Dev struct {
data gpio.PinIO
}
func (d *Dev) String() string {
return fmt.Sprintf("TM1637{clk:%s, data:%s}", d.clk, d.data)
}
// SetBrightness changes the brightness and/or turns the display on and off.
func (d *Dev) SetBrightness(b Brightness) error {
// This helps reduce jitter a little.
@ -113,6 +119,13 @@ func (d *Dev) Write(seg []byte) (int, error) {
return len(seg), nil
}
// Halt turns the display off.
func (d *Dev) Halt() error {
b := [6]byte{}
_, err := d.Write(b[:])
return err
}
// New returns an object that communicates over two pins to a TM1637.
func New(clk gpio.PinOut, data gpio.PinIO) (*Dev, error) {
// Spec calls to idle at high.
@ -186,3 +199,5 @@ func (d *Dev) writeByte(b byte) (bool, error) {
func (d *Dev) sleepHalfCycle() {
cpu.Nanospin(clockHalfCycle)
}
var _ devices.Device = &Dev{}

@ -40,12 +40,18 @@ func TestNew(t *testing.T) {
if err != nil {
t.Fatalf("failed to initialize tm1637: %v", err)
}
if s := dev.String(); s != "TM1637{clk:(0), data:(0)}" {
t.Fatal(s)
}
if _, err := dev.Write(Clock(12, 00, true)); err != nil {
log.Fatalf("failed to write to tm1637: %v", err)
}
if err := dev.SetBrightness(Brightness10); err != nil {
log.Fatalf("failed to write to tm1637: %v", err)
}
if err := dev.Halt(); err != nil {
t.Fatal(err)
}
// TODO(maruel): Check the state of the pins. That's hard since it has to
// emulate the quasi-I²C protocol.
}

Loading…
Cancel
Save