cap1188: refactor a bit. (#203)

- Make errors consistents.
- Standardize comments.
- Make unit test much faster by not sleeping.
- Reduce the number of properties of Dev.
  - Do not export Opts.
- Commented out I/O for unused data.
- Slightly reduce memory usage.
- Comment out NewSPI() since it is not implemented.
- Remove verbosity by default.
pull/1/head
M-A 8 years ago committed by GitHub
parent b46e37feba
commit 2f8171ed6c

@ -3,19 +3,18 @@
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
// Package cap1188 controls a Microchip cap1188 device over I²C. // Package cap1188 controls a Microchip cap1188 device over I²C.
// The device is a 8 Channel Capacitive Touch Sensor with 8 LED Drivers //
// The cap1188 device is a 8 channel capacitive touch sensor with 8 LED drivers.
// //
// Datasheet // Datasheet
// //
// The official data sheet can be found here: // The official data sheet can be found here:
// //
// http://ww1.microchip.com/downloads/en/DeviceDoc/CAP1188.pdf // http://ww1.microchip.com/downloads/en/DeviceDoc/CAP1188.pdf
//
package cap1188 package cap1188
import ( import (
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"log" "log"
"time" "time"
@ -24,71 +23,48 @@ import (
"periph.io/x/periph/conn/gpio" "periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/i2c" "periph.io/x/periph/conn/i2c"
"periph.io/x/periph/conn/mmr" "periph.io/x/periph/conn/mmr"
"periph.io/x/periph/conn/spi"
"periph.io/x/periph/devices" "periph.io/x/periph/devices"
) )
// TouchStatus is the status of an input sensor // TouchStatus is the status of an input sensor.
type TouchStatus int8 type TouchStatus int8
func (t TouchStatus) String() string {
switch t {
case OffStatus:
return strOffStatus
case PressedStatus:
return strPressedStatus
case HeldStatus:
return strHeldStatus
case ReleasedStatus:
return strReleasedStatus
default:
return "Unknown"
}
}
const ( const (
// OffStatus indicates that the input sensor isn't being activated // OffStatus indicates that the input sensor isn't being activated.
OffStatus TouchStatus = iota OffStatus TouchStatus = iota
// PressedStatus indicates that the input sensor is currently pressed // PressedStatus indicates that the input sensor is currently pressed.
PressedStatus PressedStatus
// HeldStatus indicates that the input sensor was pressed and is still held pressed // HeldStatus indicates that the input sensor was pressed and is still held
// pressed.
HeldStatus HeldStatus
// ReleasedStatus indicates that the input sensor was pressed and is being released // ReleasedStatus indicates that the input sensor was pressed and is being
// released.
ReleasedStatus ReleasedStatus
) )
const ( const touchStatusName = "OffStatusPressedStatusHeldStatusReleasedStatus"
nbrOfLEDs = 8
strOffStatus = "Off"
strPressedStatus = "Pressed"
strHeldStatus = "Held"
strReleasedStatus = "Released"
)
const ( var touchStatusIndex = [...]uint8{0, 9, 22, 32, 46}
// regLEDLinking - The Sensor Input LED Linking register controls whether a
// capacitive touch sensor input is linked to an LED output. If the func (i TouchStatus) String() string {
// corresponding bit is set, then the appropriate LED output will change if i < 0 || i >= TouchStatus(len(touchStatusIndex)-1) {
// states defined by the LED Behavior controls in response to the capacitive return fmt.Sprintf("TouchStatus(%d)", i)
// touch sensor input. }
regLEDLinking = 0x72 return touchStatusName[touchStatusIndex[i]:touchStatusIndex[i+1]]
// regLEDOutputControl - The LED Output Control Register controls the output }
// state of the LED pins that are not linked to sensor inputs.
regLEDOutputControl = 0x74
)
// Dev is a handle to a cap1188. // Dev is a handle to a cap1188.
type Dev struct { type Dev struct {
Opts c mmr.Dev8
d conn.Conn opts Opts
regWrapper mmr.Dev8 isSPI bool
isSPI bool
inputStatuses []TouchStatus inputStatuses [8]TouchStatus
resetAt time.Time lastReset time.Time
} }
func (d *Dev) String() string { func (d *Dev) String() string {
return fmt.Sprintf("cap1188{%s}", d.regWrapper.Conn) return fmt.Sprintf("cap1188{%s}", d.c.Conn)
} }
// Halt is a noop for the cap1188. // Halt is a noop for the cap1188.
@ -96,49 +72,40 @@ func (d *Dev) Halt() error {
return nil return nil
} }
// InputStatus reads and returns the status of the 8 inputs as an array where // InputStatus reads and returns the status of the 8 inputs.
// each entry indicates a touch event or not. func (d *Dev) InputStatus() ([8]TouchStatus, error) {
func (d *Dev) InputStatus() ([]TouchStatus, error) { d.resetSinceAtLeast(200 * time.Millisecond)
// first check that we are ready // Read inputs.
now := time.Now() status, err := d.c.ReadUint8(0x3)
readyAt := d.resetAt.Add(200 * time.Millisecond)
if now.Before(readyAt) {
time.Sleep(readyAt.Sub(now))
}
// read inputs
status, err := d.regWrapper.ReadUint8(0x3)
if err != nil { if err != nil {
return d.inputStatuses, wrap(fmt.Errorf("failed to read the input values - %s", err)) return d.inputStatuses, wrapf("failed to read the input values: %v", err)
} }
// read deltas (in two's complement, capped at -128 to 127) // Read deltas (in two's complement, capped at -128 to 127).
deltasB := [nbrOfLEDs]byte{} //deltasB := [len(d.inputStatuses)]byte{}
if err = d.regWrapper.ReadStruct(0x10, &deltasB); err != nil { //if err = d.c.ReadStruct(0x10, &deltasB); err != nil {
return d.inputStatuses, wrap(fmt.Errorf("failed to read the delta values - %s", err)) // return d.inputStatuses, wrapf("failed to read the delta values: %v", err)
} //}
deltas := [nbrOfLEDs]int{} //deltas := [len(d.inputStatuses)]int{}
for i, b := range deltasB { //for i, b := range deltasB {
deltas[i] = int(int8(b)) // deltas[i] = int(int8(b))
} //}
// Read thresholds.
// read threshold //thresholds := [len(d.inputStatuses)]byte{}
thresholds := [nbrOfLEDs]byte{} //if err = d.c.ReadStruct(0x30, &thresholds); err != nil {
if err = d.regWrapper.ReadStruct(0x30, &thresholds); err != nil { // return d.inputStatuses, wrapf("failed to read the threshold values: %v", err)
return d.inputStatuses, wrap(fmt.Errorf("failed to read the threshold values - %s", err)) //}
}
// Convert the data into a sensor state.
// convert the data into a sensor state
var touched bool
for i := uint8(0); i < uint8(len(d.inputStatuses)); i++ { for i := uint8(0); i < uint8(len(d.inputStatuses)); i++ {
// check if the bit is set. // Check if the bit is set.
touched = (status>>(7-i))&1 == 1
// TODO(mattetti): check if the event is passed the threshold: // TODO(mattetti): check if the event is passed the threshold:
// deltas[i] > int(thresholds[i]) // deltas[i] > int(thresholds[i])
if touched { // If the bit is set, it was touched.
if status&(1<<(7-i)) != 0 {
if d.inputStatuses[i] == PressedStatus { if d.inputStatuses[i] == PressedStatus {
if d.RetriggerOnHold { if d.opts.RetriggerOnHold {
d.inputStatuses[i] = HeldStatus d.inputStatuses[i] = HeldStatus
} }
continue continue
@ -156,369 +123,396 @@ func (d *Dev) InputStatus() ([]TouchStatus, error) {
// Doing so, disabled the option for the host to set specific LEDs on/off. // Doing so, disabled the option for the host to set specific LEDs on/off.
func (d *Dev) LinkLEDs(on bool) error { func (d *Dev) LinkLEDs(on bool) error {
if on { if on {
if err := d.regWrapper.WriteUint8(regLEDLinking, 0xff); err != nil { if err := d.c.WriteUint8(regLEDLinking, 0xff); err != nil {
return wrap(fmt.Errorf("failed to link LEDs - %s", err)) return wrapf("failed to link LEDs: %v", err)
} }
} else { } else {
if err := d.regWrapper.WriteUint8(regLEDLinking, 0x00); err != nil { if err := d.c.WriteUint8(regLEDLinking, 0x00); err != nil {
return wrap(fmt.Errorf("failed to unlink LEDs - %s", err)) return wrapf("failed to unlink LEDs: %v", err)
} }
} }
d.LinkedLEDs = on d.opts.LinkedLEDs = on
return nil return nil
} }
// AllLEDs turns all the LEDs on or off. // AllLEDs turns all the LEDs on or off.
// //
// This is quite more efficient than looping through each led and turn them on/off. // This is quite more efficient than looping through each LED and turn them
// on/off.
func (d *Dev) AllLEDs(on bool) error { func (d *Dev) AllLEDs(on bool) error {
if d.LinkedLEDs { if d.opts.LinkedLEDs {
return wrap(fmt.Errorf("can't manually set LEDs when they are linked to sensors")) return wrapf("can't manually set LEDs when they are linked to sensors")
} }
if on { if on {
return d.regWrapper.WriteUint8(regLEDOutputControl, 0xff) if err := d.c.WriteUint8(regLEDOutputControl, 0xff); err != nil {
return wrapf("failed to turn all LEDs on: %v", err)
}
} }
if err := d.c.WriteUint8(regLEDOutputControl, 0x00); err != nil {
return d.regWrapper.WriteUint8(regLEDOutputControl, 0x00) return wrapf("failed to turn all LEDs off: %v", err)
}
return nil
} }
// SetLED sets the state of a LED as on or off // SetLED sets the state of a LED as on or off.
//
// Only works if the LEDs are not linked to the sensors // Only works if the LEDs are not linked to the sensors
func (d *Dev) SetLED(idx int, state bool) error { func (d *Dev) SetLED(idx int, state bool) error {
if d.LinkedLEDs { if d.opts.LinkedLEDs {
return wrap(fmt.Errorf("can't manually set LEDs when they are linked to sensors")) return wrapf("can't manually set LEDs when they are linked to sensors")
} }
if idx > 7 || idx < 0 { if idx > 7 || idx < 0 {
return wrap(fmt.Errorf("invalid led idx %d", idx)) return wrapf("invalid led idx %d", idx)
} }
if d.Debug { if d.opts.Debug {
log.Printf("Set LED state %d - %t\n", idx, state) log.Printf("cap1188: Set LED state %d - %t", idx, state)
} }
if state { if state {
return d.setBit(regLEDOutputControl, idx) if err := d.setBit(regLEDOutputControl, idx); err != nil {
return wrapf("failed to set LED #%d to %t: %v", idx, state, err)
}
}
if err := d.clearBit(regLEDOutputControl, idx); err != nil {
return wrapf("failed to set LED #%d to %t: %v", idx, state, err)
} }
return d.clearBit(regLEDOutputControl, idx) return nil
} }
// Reset issues a soft reset to the device using the reset pin // Reset issues a soft reset to the device using the reset pin if available.
// if available.
func (d *Dev) Reset() error { func (d *Dev) Reset() error {
if err := d.ClearInterrupt(); err != nil { if err := d.ClearInterrupt(); err != nil {
return err return err
} }
if d != nil && d.ResetPin != nil { if d.opts.ResetPin != nil {
if d.Debug { if d.opts.Debug {
log.Println("cap1188: Resetting the device using the reset pin") log.Println("cap1188: Resetting the device using the reset pin")
} }
if err := d.ResetPin.Out(gpio.Low); err != nil { if err := d.opts.ResetPin.Out(gpio.Low); err != nil {
return err return wrapf("failed to set reset pin low: %v", err)
} }
time.Sleep(1 * time.Microsecond) sleep(1 * time.Microsecond)
if err := d.ResetPin.Out(gpio.High); err != nil { if err := d.opts.ResetPin.Out(gpio.High); err != nil {
return err return wrapf("failed to set reset pin high: %v", err)
} }
time.Sleep(10 * time.Millisecond) sleep(10 * time.Millisecond)
if err := d.ResetPin.Out(gpio.Low); err != nil { if err := d.opts.ResetPin.Out(gpio.Low); err != nil {
return err return wrapf("failed to set reset pin low: %v", err)
} }
} }
// Track the reset time since the device won't be ready for up to 15ms // Track the reset time since the device won't be ready for up to 15ms and
// and won't be ready for first conversion for up to 200ms. // won't be ready for first conversion for up to 200ms.
d.resetAt = time.Now() d.lastReset = time.Now()
// Time to communications is 15ms.
sleep(15 * time.Millisecond)
return nil return nil
} }
// ClearInterrupt resets the interrupt flag // ClearInterrupt resets the interrupt flag.
func (d *Dev) ClearInterrupt() error { func (d *Dev) ClearInterrupt() error {
// clear the main control bit // Clear the main control bit.
return d.clearBit(0x0, 0) if err := d.clearBit(0x0, 0); err != nil {
return wrapf("failed to clean interrupt: %v")
}
return nil
} }
// NewI2C returns a new device that communicates over I²C to cap1188. // NewI2C returns a new device that communicates over I²C to cap1188.
//
// Use default options if nil is used.
func NewI2C(b i2c.Bus, opts *Opts) (*Dev, error) { func NewI2C(b i2c.Bus, opts *Opts) (*Dev, error) {
addr := uint16(0x28) // default address if opts == nil {
if opts != nil { opts = DefaultOpts()
switch opts.Address {
case 0x28, 0x29, 0x2a, 0x2b, 0x2c:
addr = opts.Address
case 0x00:
// do not do anything
default:
return nil, wrap(errors.New("given address not supported by device"))
}
} }
d := &Dev{d: &i2c.Dev{Bus: b, Addr: addr}, isSPI: false} addr, err := opts.i2cAddr()
if d.Debug { if err != nil {
log.Printf("cap1188: Connecting via I2C address: %#X\n", addr) return nil, wrapf("%v", err)
} }
d.inputStatuses = make([]TouchStatus, nbrOfLEDs) d, err := makeDev(&i2c.Dev{Bus: b, Addr: addr}, false, opts)
if err := d.makeDev(opts); err != nil { if err != nil {
return nil, err return nil, err
} }
// time to communications is 15ms log.Printf("cap1188: Connected via I²C address: %#x", addr)
now := time.Now()
readyAt := d.resetAt.Add(15 * time.Millisecond)
if now.Before(readyAt) {
time.Sleep(readyAt.Sub(now))
}
return d, nil return d, nil
} }
/*
// NewSPI returns an object that communicates over SPI to cap1188 environmental // NewSPI returns an object that communicates over SPI to cap1188 environmental
// sensor. // sensor.
//
// TODO(mattetti): Expose once implemented and tested.
func NewSPI(p spi.Port, opts *Opts) (*Dev, error) { func NewSPI(p spi.Port, opts *Opts) (*Dev, error) {
return nil, fmt.Errorf("not implemented") return nil, fmt.Errorf("cap1188: not implemented")
} }
*/
func (d *Dev) makeDev(opts *Opts) error { //
// Use default options if none are passed.
if opts == nil { func makeDev(c conn.Conn, isSPI bool, opts *Opts) (*Dev, error) {
opts = DefaultOpts() d := &Dev{
opts: *opts,
isSPI: isSPI,
c: mmr.Dev8{Conn: c, Order: binary.LittleEndian},
} }
d.Opts = *opts
d.regWrapper = mmr.Dev8{Conn: d.d, Order: binary.LittleEndian}
var productID byte
var err error
// Read the product id to confirm it matches our expectations. // Read the product id to confirm it matches our expectations.
if productID, err = d.regWrapper.ReadUint8(0xFD); err != nil { if productID, err := d.c.ReadUint8(0xFD); err != nil {
return fmt.Errorf("failed to read product id - %s", err) return nil, wrapf("failed to read product id: %v", err)
} } else if productID != 0x50 {
if productID != 0x50 { return nil, wrapf("unexpected chip id %x; is this a cap1188?", productID)
return fmt.Errorf("cap1188: unexpected chip id %x; is this a cap1188?", productID)
} }
// manufacturer ID on 0xFE, should be 0x5D // manufacturer ID on 0xFE, should be 0x5D
// revision ID on 0xFF, should be 0x83 // revision ID on 0xFF, should be 0x83
// reset the device // Reset the device.
if err = d.Reset(); err != nil { if err := d.Reset(); err != nil {
return fmt.Errorf("failed to reset the device - %s", err) return nil, err
} }
var recalFlag byte var recalFlag byte
if opts.EnableRecalibration { if d.opts.EnableRecalibration {
recalFlag = 1 recalFlag = 1
} }
var intOnRel byte var intOnRel byte
if !opts.InterruptOnRelease { if !d.opts.InterruptOnRelease {
intOnRel = 1 // 0 = trigger on release intOnRel = 1 // 0 = trigger on release
} }
// enable all inputs // Enable all inputs.
if err = d.regWrapper.WriteUint8(0x21, 0xff); err != nil { if err := d.c.WriteUint8(0x21, 0xff); err != nil {
return wrap(fmt.Errorf("failed to enable all inputs - %s", err)) return nil, wrapf("failed to enable all inputs: %v", err)
} }
// enable interrupts // Enable interrupts.
if err = d.regWrapper.WriteUint8(0x27, 0xff); err != nil { if err := d.c.WriteUint8(0x27, 0xff); err != nil {
return wrap(fmt.Errorf("failed to enable interrupts - %s", err)) return nil, wrapf("failed to enable interrupts: %v", err)
} }
// enable/disable repeats // Enable/disable repeats.
// TODO(mattetti): make it an option // TODO(mattetti): make it an option.
if err = d.regWrapper.WriteUint8(0x28, 0xff); err != nil { if err := d.c.WriteUint8(0x28, 0xff); err != nil {
return fmt.Errorf("failed to disable repeats - %s", err) return nil, wrapf("failed to disable repeats: %v", err)
} }
// enable multitouch // Enable multitouch.
multitouchConfig := ( multitouchConfig := (
// Enables the multiple button blocking circuitry. // Enables the multiple button blocking circuitry.
// „ 0 - The multiple touch circuitry is disabled. The device will not // - 0: The multiple touch circuitry is disabled. The device will not block
// block multiple touches. // multiple touches.
// „ 1 (default) - The multiple touch circuitry is enabled. The device // - 1 (default): The multiple touch circuitry is enabled. The device will
// will flag the number of touches equal to programmed multiple touch // flag the number of touches equal to programmed multiple touch threshold
// threshold and block all others. It will remember which sensor inputs // and block all others. It will remember which sensor inputs are valid and
// are valid and block all others until that sensor pad has been // block all others until that sensor pad has been released. Once a sensor
// released. Once a sensor pad has been released, the N detected touches // pad has been released, the N detected touches (determined via the cycle
// (determined via the cycle order of CS1 - CS8) will be flagged and all // order of CS1 - CS8) will be flagged and all others blocked.
// others blocked.
byte(0)<<7 | byte(0)<<7 |
byte(0)<<6 | byte(0)<<5 | byte(0)<<4 | byte(0)<<6 | byte(0)<<5 | byte(0)<<4 |
// Determines the number of simultaneous touches on all sensor pads // Determines the number of simultaneous touches on all sensor pads before
// before a Multiple Touch Event is detected and sensor inputs are blocked. // a Multiple Touch Event is detected and sensor inputs are blocked.
// set to 2 // Set to 2.
byte(0)<<3 | byte(1)<<2 | byte(0)<<3 | byte(1)<<2 |
byte(0)<<1 | byte(0)<<0) byte(0)<<1 | byte(0)<<0)
if err = d.regWrapper.WriteUint8(0x2a, multitouchConfig); err != nil { if err := d.c.WriteUint8(0x2a, multitouchConfig); err != nil {
return wrap(fmt.Errorf("failed to enable multitouch - %s", err)) return nil, fmt.Errorf("failed to enable multitouch: %v", err)
} }
// Averaging and Sampling Config // Averaging and Sampling Config.
samplingConfig := (byte(0)<<7 | samplingConfig := (byte(0)<<7 |
// number of samples taken per measurement // Number of samples taken per measurement.
// TODO(mattetti): use opts.SamplesPerMeasurement // TODO(mattetti): use d.opts.SamplesPerMeasurement
byte(0)<<6 | byte(0)<<6 |
byte(0)<<5 | byte(0)<<5 |
byte(0)<<4 | byte(0)<<4 |
// sample time // Sample time.
// TODO(mattetti): use opts.SamplingTime // TODO(mattetti): use d.opts.SamplingTime
byte(1)<<3 | byte(1)<<3 |
byte(0)<<2 | byte(0)<<2 |
// overall cycle time // Overall cycle time.
// TODO(mattetti): use opts.CycleTime // TODO(mattetti): use d.opts.CycleTime
byte(0)<<1 | byte(0)<<1 |
byte(0)<<0) byte(0)<<0)
if d.Debug { if d.opts.Debug {
log.Printf("cap1188: Sampling config mask: %08b\n", samplingConfig) log.Printf("cap1188: Sampling config mask: %08b", samplingConfig)
} }
if err = d.regWrapper.WriteUint8(0x24, samplingConfig); err != nil { if err := d.c.WriteUint8(0x24, samplingConfig); err != nil {
return fmt.Errorf("failed to enable multitouch - %s", err) return nil, wrapf("failed to enable multitouch: %v", err)
} }
// customize sensitivity (TODO) // Customize sensitivity.
sensitivity := (byte(0)<<7 | sensitivity := (byte(0)<<7 |
// Controls the sensitivity of a touch detection. The sensitivity settings act // Controls the sensitivity of a touch detection. The sensitivity settings
// to scale the relative delta count value higher or lower based on the system parameters. A setting of // act to scale the relative delta count value higher or lower based on the
// 000b is the most sensitive while a setting of 111b is the least sensitive. At the more sensitive settings, // system parameters. A setting of 000b is the most sensitive while a
// touches are detected for a smaller delta capacitance corresponding to a “lighter” touch. These settings // setting of 111b is the least sensitive. At the more sensitive settings,
// are more sensitive to noise, however, and a noisy environment may flag more false touches with higher // touches are detected for a smaller delta capacitance corresponding to a
// “lighter” touch. These settings are more sensitive to noise, however,
// and a noisy environment may flag more false touches with higher
// sensitivity levels. // sensitivity levels.
// Set to 4x // Set to 4x.
// TODO(mattetti): make that configurable. // TODO(mattetti): make that configurable.
byte(1)<<6 | byte(0)<<5 | byte(1)<<4 | byte(1)<<6 | byte(0)<<5 | byte(1)<<4 |
byte(0)<<3 | byte(0)<<2 | byte(0)<<1 | byte(0)<<0) byte(0)<<3 | byte(0)<<2 | byte(0)<<1 | byte(0)<<0)
if d.Debug { if d.opts.Debug {
log.Printf("cap1188: Sensitivity mask: %08b\n", sensitivity) log.Printf("cap1188: Sensitivity mask: %08b", sensitivity)
} }
if err = d.regWrapper.WriteUint8(0x1F, sensitivity); err != nil { if err := d.c.WriteUint8(0x1F, sensitivity); err != nil {
return fmt.Errorf("failed to set sensitivity - %s", err) return nil, wrapf("failed to set sensitivity: %v", err)
} }
if opts.LinkedLEDs { if d.opts.LinkedLEDs {
if err = d.LinkLEDs(true); err != nil { if err := d.LinkLEDs(true); err != nil {
return err return nil, err
} }
} }
if opts.RetriggerOnHold { if d.opts.RetriggerOnHold {
if err = d.regWrapper.WriteUint8(0x28, 0xff); err != nil { if err := d.c.WriteUint8(0x28, 0xff); err != nil {
return fmt.Errorf("failed to set retrigger on hold - %s", err) return nil, wrapf("failed to set retrigger on hold: %v", err)
} }
} else { } else {
if err = d.regWrapper.WriteUint8(0x28, 0x00); err != nil { if err := d.c.WriteUint8(0x28, 0x00); err != nil {
return fmt.Errorf("failed to turn off retrigger on hold - %s", err) return nil, wrapf("failed to turn off retrigger on hold: %v", err)
} }
} }
// page 47 - configuration registers // page 47 - configuration registers
config := ( config := (
// Timeout: Enables the timeout and idle functionality of the SMBus protocol. // Timeout: Enables the timeout and idle functionality of the SMBus protocol.
// default 0: The SMBus timeout and idle functionality are disabled. The // - 0 (default): The SMBus timeout and idle functionality are disabled. The
// SMBus interface will not time out if the clock line is held low. // SMBus interface will not time out if the clock line is held low.
// Likewise, it will not reset if both data and clock lines are held // Likewise, it will not reset if both data and clock lines are held high
// high for longer than 200us // for longer than 200us
byte(0)<<7 | byte(0)<<7 |
// Configures the operation of the WAKE pin. // Configures the operation of the WAKE pin.
// default 0: The WAKE pin is not asserted when a touch is detected while the // - 0 (default): The WAKE pin is not asserted when a touch is detected
// device is in Standby. It will still be used to wake the device from // while the device is in Standby. It will still be used to wake the
// Deep Sleep when driven high. // device from Deep Sleep when driven high.
byte(0)<<6 | byte(0)<<6 |
// digital noise threshold // Digital noise threshold.
// Determines whether the digital noise threshold is used by the device. // Determines whether the digital noise threshold is used by the device.
// default 1: The noise threshold is disabled. Any delta count that // - 1 (default): The noise threshold is disabled. Any delta count that is
// is less than the touch threshold is used for the automatic // less than the touch threshold is used for the automatic re-calibration
// re-calibration routine. // routine.
byte(1)<<5 | byte(1)<<5 |
// analog noise filter // Analog noise filter.
// default 0: Determines whether the analog noise filter is enabled. Setting this // - 0 (default): The analog noise filter is enabled.
// bit disables the feature. // - 1: Disables the feature.
byte(1)<<4 | byte(1)<<4 |
// maximum duration recalibration // Maximum duration recalibration.
// Determines whether the maximum duration recalibration is enabled. // Determines whether the maximum duration recalibration is enabled.
// // - 0: The maximum duration recalibration functionality is disabled. A
// if 0, the maximum duration recalibration functionality is // touch may be held indefinitely and no re-calibration will be performed
// disabled. A touch may be held indefinitely and no re-calibration // on any sensor input.
// will be performed on any sensor input. // - 1: The maximum duration recalibration functionality is enabled. If a
// // touch is held for longer than the d.opts.MaxTouchDuration, then the
// if 1, The maximum duration recalibration functionality is // re-calibration routine will be restarted.
// enabled. If a touch is held for longer than the
// opts.MaxTouchDuration, then the re-calibration routine will be
// restarted
recalFlag<<3 | recalFlag<<3 |
byte(0)<<2 | byte(0)<<2 |
byte(0)<<1 | byte(0)<<1 |
byte(0)<<0) byte(0)<<0)
if d.Debug { if d.opts.Debug {
log.Printf("cap1188: Config mask: %08b\n", config) log.Printf("cap1188: Config mask: %08b", config)
} }
if err = d.regWrapper.WriteUint8(0x20, config); err != nil { if err := d.c.WriteUint8(0x20, config); err != nil {
return fmt.Errorf("failed to set the device configuration - %s", err) return nil, wrapf("failed to set the device configuration: %v", err)
} }
config2 := ( config2 := (
// Linked LED Transition controls // Linked LED Transition controls.
// default 0: The Linked LED Transition controls set the min duty // - 0 (default): The Linked LED Transition controls set the min duty cycle
// cycle equal to the max duty cycle // equal to the max duty cycle.
byte(0)<<7 | byte(0)<<7 |
// Determines the ALERT# pin polarity and behavior. // Determines the ALERT# pin polarity and behavior.
// default 1: the ALERT# pin is active low and open drain. // - 1 (default): The ALERT# pin is active low and open drain.
byte(1)<<6 | byte(1)<<6 |
// Determines whether the device will reduce power consumption // Determines whether the device will reduce power consumption while
// while waiting between conversion time completion and the end of // waiting between conversion time completion and the end of the polling
// the polling cycle. // cycle.
// default 0: The device will always power down as much as possible // - 0 (default): The device will always power down as much as possible
// during the time between the end of the last conversion and the // during the time between the end of the last conversion and the end of
// end of the polling cycle. // the polling cycle.
byte(1)<<5 | byte(1)<<5 |
// Determines whether the LED Mirror Control register bits are // Determines whether the LED Mirror Control register bits are linked to
// linked to the LED Polarity bits. Setting this bit blocks the // the LED Polarity bits. Setting this bit blocks the normal behavior which
// normal behavior which is to automatically set and clear the LED // is to automatically set and clear the LED Mirror Control bits when the
// Mirror Control bits when the LED Polarity bits are set or // LED Polarity bits are set or cleared.
// cleared. // - 0 (default): When the LED Polarity controls are set, the corresponding
// default 0: When the LED Polarity controls are set, the // LED Mirror control is automatically set. Likewise, when the LED
// corresponding LED Mirror control is automatically set. Likewise, // Polarity controls are cleared, the corresponding LED Mirror control is
// when the LED Polarity controls are cleared, the corresponding // also cleared.
// LED Mirror control is also cleared.
byte(0)<<4 | byte(0)<<4 |
// Determines whether the Noise Status bits will show RF Noise as // Determines whether the Noise Status bits will show RF Noise as the only
// the only input source. // input source.
// default 0: The Noise Status registers will show both RF noise and // - 0 (default): The Noise Status registers will show both RF noise and low
// low frequency EMI noise if either is detected on a capacitive // frequency EMI noise if either is detected on a capacitive touch sensor
// touch sensor input. // input.
byte(0)<<3 | byte(0)<<3 |
// Determines whether the RF noise filter is enabled. // Determines whether the RF noise filter is enabled.
// default 0: If RF noise is detected by the analog block, the // - 0 (default): If RF noise is detected by the analog block, the delta
// delta count on the corresponding channel is set to 0. Note that // count on the corresponding channel is set to 0. Note that this does
// this does not require that Noise Status bits be set. // not require that Noise Status bits be set.
byte(0)<<2 | byte(0)<<2 |
byte(0)<<1 | byte(0)<<1 |
// Controls the interrupt behavior when a release is detected on a button. // Controls the interrupt behavior when a release is detected on a button.
// when 0: An interrupt is generated when a press is detected and // - 0: An interrupt is generated when a press is detected and again when a
// again when a release is detected and at the repeat rate (if enabled) // release is detected and at the repeat rate (if enabled)
// when 1: An interrupt is generated when a press is detected and // - 1: An interrupt is generated when a press is detected and at the
// at the repeat rate but not when a release is detected. // repeat rate but not when a release is detected.
intOnRel<<0) intOnRel<<0)
if d.Debug { if d.opts.Debug {
log.Printf("cap1188: Config2 mask: %08b\n", config2) log.Printf("cap1188: Config2 mask: %08b", config2)
} }
if err = d.regWrapper.WriteUint8(0x44, config2); err != nil { if err := d.c.WriteUint8(0x44, config2); err != nil {
return fmt.Errorf("failed to set the device configuration 2 - %s", err) return nil, wrapf("failed to set the device configuration 2: %v", err)
} }
return nil return d, nil
} }
// setBit sets a specific bit on a register func (d *Dev) resetSinceAtLeast(t time.Duration) {
// TODO(mattetti): avoid reading before writing, keep states in memory readyAt := d.lastReset.Add(t)
if now := time.Now(); now.Before(readyAt) {
sleep(readyAt.Sub(now))
}
}
// setBit sets a specific bit on a register.
//
// TODO(mattetti): avoid reading before writing, keep states in memory.
func (d *Dev) setBit(regID uint8, idx int) error { func (d *Dev) setBit(regID uint8, idx int) error {
v, err := d.regWrapper.ReadUint8(regID) v, err := d.c.ReadUint8(regID)
if err != nil { if err != nil {
return err return err
} }
v |= (1 << uint8(idx)) v |= (1 << uint8(idx))
return d.regWrapper.WriteUint8(regID, v) return d.c.WriteUint8(regID, v)
} }
// clearBit clears a specific bit on a register // clearBit clears a specific bit on a register.
// TODO(mattetti): avoid reading before writing, keep states in memory //
// TODO(mattetti): avoid reading before writing, keep states in memory.
func (d *Dev) clearBit(regID uint8, idx int) error { func (d *Dev) clearBit(regID uint8, idx int) error {
v, err := d.regWrapper.ReadUint8(regID) v, err := d.c.ReadUint8(regID)
if err != nil { if err != nil {
return err return err
} }
v &= ^(1 << uint8(idx)) v &= ^(1 << uint8(idx))
return d.regWrapper.WriteUint8(regID, v) return d.c.WriteUint8(regID, v)
} }
func wrap(err error) error { //
return fmt.Errorf("cap1188: %v", err)
const (
// regLEDLinking is the Sensor Input LED Linking register controls whether a
// capacitive touch sensor input is linked to an LED output. If the
// corresponding bit is set, then the appropriate LED output will change
// states defined by the LED Behavior controls in response to the capacitive
// touch sensor input.
regLEDLinking = 0x72
// regLEDOutputControl is the LED Output Control Register controls the output
// state of the LED pins that are not linked to sensor inputs.
regLEDOutputControl = 0x74
)
var sleep = time.Sleep
func wrapf(format string, a ...interface{}) error {
return fmt.Errorf("cap1188: "+format, a...)
} }
var _ devices.Device = &Dev{} var _ devices.Device = &Dev{}

@ -0,0 +1,77 @@
// Copyright 2017 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package cap1188_test
import (
"fmt"
"log"
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/gpio/gpioreg"
"periph.io/x/periph/conn/i2c/i2creg"
"periph.io/x/periph/experimental/devices/cap1188"
)
func Example() {
// Open the I²C bus to which the cap1188 is connected.
i2cBus, err := i2creg.Open("")
if err != nil {
log.Fatal(err)
}
defer i2cBus.Close()
// We need to set an alert ping that will let us know when a touch event
// occurs. The alert pin is the pin connected to the IRQ/interrupt pin.
alertPin := gpioreg.ByName("GPIO25")
if alertPin == nil {
log.Fatal("invalid alert GPIO pin number")
}
// We set the alert pin to monitor for interrupts.
if err := alertPin.In(gpio.PullUp, gpio.BothEdges); err != nil {
log.Fatalf("Can't monitor the alert pin")
}
// Optionally but highly recommended, we can also set a reset pin to
// start/leave things in a clean state.
resetPin := gpioreg.ByName("GPIO21")
if resetPin == nil {
log.Fatal("invalid reset GPIO pin number")
}
// We will configure the cap1188 by setting some options, we can start by the
// defaults.
opts := cap1188.DefaultOpts()
opts.AlertPin = alertPin
opts.ResetPin = resetPin
// Open the device so we can detect touch events.
dev, err := cap1188.NewI2C(i2cBus, opts)
if err != nil {
log.Fatalf("couldn't open cap1188: %v", err)
}
fmt.Println("Monitoring for touch events")
maxTouches := 42 // Stop the program after 42 touches.
for maxTouches > 0 {
if alertPin.WaitForEdge(-1) {
maxTouches--
statuses, err := dev.InputStatus()
if err != nil {
fmt.Printf("Error reading inputs: %v\n", err)
continue
}
// print the status of each sensor
for i, st := range statuses {
fmt.Printf("#%d: %s\t", i, st)
}
fmt.Println()
// we need to clear the interrupt so it can be triggered again
if err := dev.ClearInterrupt(); err != nil {
fmt.Println(err, "while clearing the interrupt")
}
}
}
fmt.Print("\n")
}

@ -4,25 +4,28 @@
package cap1188 package cap1188
import "periph.io/x/periph/conn/gpio" import (
"errors"
// SamplingTime determines the time to take a single sample "periph.io/x/periph/conn/gpio"
)
// SamplingTime determines the time to make a single sample.
type SamplingTime uint8 type SamplingTime uint8
// Possible sampling time values (written as 2 bits) // Possible sampling time values. (written as 2 bits)
const ( const (
S320us SamplingTime = 0 S320us SamplingTime = 0
S640us SamplingTime = 1 S640us SamplingTime = 1
// S1_28ms represents 1.28ms sampling time, which is the default. // S1_28ms represents 1.28ms sampling time, which is the default.
S1_28ms SamplingTime = 2 // default S1_28ms SamplingTime = 2
S2_56ms SamplingTime = 3 S2_56ms SamplingTime = 3
) )
// AvgSampling set the number of samples per measurement that get // AvgSampling set the number of samples per measurement that get averaged.
// averaged
type AvgSampling uint8 type AvgSampling uint8
// possible average sampling values. (written as 3 bits) // Possible average sampling values. (written as 3 bits)
const ( const (
// Avg1 means that 1 sample is taken per measurement // Avg1 means that 1 sample is taken per measurement
Avg1 AvgSampling = iota // 0 Avg1 AvgSampling = iota // 0
@ -39,7 +42,7 @@ const (
// normal operation. // normal operation.
type CycleTime uint8 type CycleTime uint8
// possible cycle time values. (written as 2 bits) // Possible cycle time values. (written as 2 bits)
const ( const (
C35ms CycleTime = iota // 0 C35ms CycleTime = iota // 0
C70ms // default C70ms // default
@ -51,7 +54,7 @@ const (
// recalibration. // recalibration.
type MaxDur uint8 type MaxDur uint8
// possible touch duration values. (written as 4 bits) // Possible touch duration values. (written as 4 bits)
const ( const (
MaxDur560ms MaxDur = iota MaxDur560ms MaxDur = iota
MaxDur840ms MaxDur840ms
@ -71,28 +74,26 @@ const (
MaxDur11200ms MaxDur11200ms
) )
// Opts is optional options to pass to the constructor. // Opts is options to pass to the constructor.
//
// Address is only used on creation of an I²C-device. Its default value is 0x28.
// It can be set to other values (0x29, 0x2a, 0x2b, 0x2c) depending on the HW
// configuration of the ADDR_COMM pin. This has no effect with NewSPI()
type Opts struct { type Opts struct {
// Address is the I2C slave address to use // Debug turns on extra logging capabilities.
Address uint16
// Debug turns on extra logging capabilities
Debug bool Debug bool
// I2CAddr is the I²C slave address to use. It can only used on creation of
// an I²C-device. Its default value is 0x28. It can be set to other values
// (0x29, 0x2a, 0x2b, 0x2c) depending on the HW configuration of the
// ADDR_COMM pin. Must not be set when used over SPI.
I2CAddr uint16
// LinkedLEDs indicates if the LEDs should be activated automatically // LinkedLEDs indicates if the LEDs should be activated automatically
// when their sensors detect a touch event. // when their sensors detect a touch event.
LinkedLEDs bool LinkedLEDs bool
// MaxTouchDuration sets the touch duration threshold. It is possible that a // MaxTouchDuration sets the touch duration threshold. It is possible that a
// “stuck button” occurs when something is placed on a button which causes a // “stuck button” occurs when something is placed on a button which causes a
// touch to be detected for a long period. By setting this value, // touch to be detected for a long period. By setting this value, a
// a recalibration can be forced when a touch is held on a button for longer // recalibration can be forced when a touch is held on a button for longer
// than the duration specified. // than the duration specified.
MaxTouchDuration MaxDur MaxTouchDuration MaxDur
// EnableRecalibration is used to force the recalibration if a touch event lasts // EnableRecalibration is used to force the recalibration if a touch event
// longer than MaxTouchDuration. // lasts longer than MaxTouchDuration.
EnableRecalibration bool EnableRecalibration bool
// AlertPin is the pin receiving the interrupt when a touch event is detected // AlertPin is the pin receiving the interrupt when a touch event is detected
@ -105,11 +106,11 @@ type Opts struct {
// interrupt on the AlertPin when a release event is detected. // interrupt on the AlertPin when a release event is detected.
InterruptOnRelease bool InterruptOnRelease bool
// RetriggerOnHold forces a retrigger of the interrupt when a sensor is pressed // RetriggerOnHold forces a retrigger of the interrupt when a sensor is
// for longer than MaxTouchDuration // pressed for longer than MaxTouchDuration
RetriggerOnHold bool RetriggerOnHold bool
// Averaging and Sampling Configuration Register // Averaging and Sampling Configuration Register.
// SamplesPerMeasurement is the number of samples taken per measurement. All // SamplesPerMeasurement is the number of samples taken per measurement. All
// samples are taken consecutively on the same channel before the next // samples are taken consecutively on the same channel before the next
@ -118,10 +119,10 @@ type Opts struct {
// Available options: 1, 2, 4, 8 (default), 16, 32, 64, 128 // Available options: 1, 2, 4, 8 (default), 16, 32, 64, 128
SamplesPerMeasurement AvgSampling SamplesPerMeasurement AvgSampling
// SamplingTime Determines the time to take a single sample as shown // SamplingTime Determines the time to take a single sample as shown.
SamplingTime SamplingTime SamplingTime SamplingTime
// CycleTime determines the overall cycle time for all measured channels // CycleTime determines the overall cycle time for all measured channels
// during normal operation. All measured channels are sampled at the // during normal operation. All measured channels are sampled at the
// beginning of the cycle time. If additional time is remaining, then the // beginning of the cycle time. If additional time is remaining, then the
// device is placed into a lower power state for the remaining duration of // device is placed into a lower power state for the remaining duration of
@ -129,6 +130,18 @@ type Opts struct {
CycleTime CycleTime CycleTime CycleTime
} }
func (o *Opts) i2cAddr() (uint16, error) {
switch o.I2CAddr {
case 0:
// Default address.
return 0x28, nil
case 0x28, 0x29, 0x2a, 0x2b, 0x2c:
return o.I2CAddr, nil
default:
return 0, errors.New("given address not supported by device")
}
}
// DefaultOpts returns a pointer to a new Opts with the default option values. // DefaultOpts returns a pointer to a new Opts with the default option values.
func DefaultOpts() *Opts { func DefaultOpts() *Opts {
return &Opts{ return &Opts{

@ -2,86 +2,20 @@
// Use of this source code is governed under the Apache License, Version 2.0 // Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
package cap1188_test package cap1188
import ( import (
"fmt" "flag"
"io/ioutil"
"log" "log"
"reflect" "reflect"
"testing" "testing"
"time" "time"
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/gpio/gpioreg"
"periph.io/x/periph/conn/i2c" "periph.io/x/periph/conn/i2c"
"periph.io/x/periph/conn/i2c/i2creg"
"periph.io/x/periph/conn/i2c/i2ctest" "periph.io/x/periph/conn/i2c/i2ctest"
"periph.io/x/periph/experimental/devices/cap1188"
) )
func Example() {
// Open the I²C bus to which the cap1188 is connected.
i2cBus, err := i2creg.Open("")
if err != nil {
log.Fatal(err)
}
defer i2cBus.Close()
// We will configure the cap1188 by setting some options, we can start by the defaults.
opts := cap1188.DefaultOpts()
// We need to set an alert ping that will let us know when a touch event
// occurs. The alert pin is the pin connected to the IRQ/interrupt pin.
alertPin := gpioreg.ByName("GPIO25")
if alertPin == nil {
log.Fatal("invalid alert GPIO pin number")
}
// We set the alert pin to monitor for interrupts
if err := alertPin.In(gpio.PullUp, gpio.BothEdges); err != nil {
log.Fatalf("Can't monitor the alert pin")
}
// Optionally but highly recommended, we can also set a reset pin to
// start/leave things in a clean state.
resetPin := gpioreg.ByName("GPIO21")
if resetPin == nil {
log.Fatal("invalid reset GPIO pin number")
}
opts.AlertPin = alertPin
opts.ResetPin = resetPin
// open the device so we can detect touch events
dev, err := cap1188.NewI2C(i2cBus, opts)
if err != nil {
log.Fatalf("couldn't open cap1188 - %s", err)
}
time.Sleep(200 * time.Millisecond)
fmt.Println("Monitoring for touch events")
maxTouches := 42 // stop the program after 42 touches
for maxTouches > 0 {
if alertPin.WaitForEdge(-1) {
maxTouches--
statuses, err := dev.InputStatus()
if err != nil {
fmt.Printf("Error reading inputs: %s\n", err)
continue
}
// print the status of each sensor
for i, st := range statuses {
fmt.Printf("#%d: %s\t", i, st)
}
fmt.Println()
// we need to clear the interrupt so it can be triggered again
if err := dev.ClearInterrupt(); err != nil {
fmt.Println(err, "while clearing the interrupt")
}
}
}
fmt.Print("\n")
}
func TestNewI2C(t *testing.T) { func TestNewI2C(t *testing.T) {
bus := i2ctest.Playback{ bus := i2ctest.Playback{
Ops: []i2ctest.IO{ Ops: []i2ctest.IO{
@ -110,7 +44,7 @@ func TestNewI2C(t *testing.T) {
{Addr: 40, W: []byte{0x44, 0x61}, R: nil}, {Addr: 40, W: []byte{0x44, 0x61}, R: nil},
}, },
} }
d, err := cap1188.NewI2C(&bus, &cap1188.Opts{Debug: true}) d, err := NewI2C(&bus, &Opts{Debug: true})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -129,8 +63,7 @@ func TestDev_InputStatus(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
bus i2c.Bus bus i2c.Bus
opts *cap1188.Opts want [8]TouchStatus
want []cap1188.TouchStatus
}{ }{
{name: "all off", {name: "all off",
bus: &i2ctest.Playback{ bus: &i2ctest.Playback{
@ -138,128 +71,77 @@ func TestDev_InputStatus(t *testing.T) {
// status // status
{Addr: 40, W: []byte{0x3}, R: []byte{0x0}}, {Addr: 40, W: []byte{0x3}, R: []byte{0x0}},
// deltas // deltas
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
// thresholds // thresholds
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...), }...),
}, },
opts: cap1188.DefaultOpts(), want: [8]TouchStatus{OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus},
want: []cap1188.TouchStatus{cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus},
}, },
{name: "all pressed", {name: "all pressed",
bus: &i2ctest.Playback{ bus: &i2ctest.Playback{
Ops: append(setupPlaybackIO(), []i2ctest.IO{ Ops: append(setupPlaybackIO(), []i2ctest.IO{
{Addr: 40, W: []byte{0x3}, R: []byte{0xff}}, {Addr: 40, W: []byte{0x3}, R: []byte{0xff}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...), }...),
}, },
opts: cap1188.DefaultOpts(), want: [8]TouchStatus{PressedStatus, PressedStatus, PressedStatus, PressedStatus, PressedStatus, PressedStatus, PressedStatus, PressedStatus},
want: []cap1188.TouchStatus{cap1188.PressedStatus, cap1188.PressedStatus, cap1188.PressedStatus, cap1188.PressedStatus, cap1188.PressedStatus, cap1188.PressedStatus, cap1188.PressedStatus, cap1188.PressedStatus},
}, },
{name: "first pressed", {name: "first pressed",
bus: &i2ctest.Playback{ bus: &i2ctest.Playback{
Ops: append(setupPlaybackIO(), []i2ctest.IO{ Ops: append(setupPlaybackIO(), []i2ctest.IO{
{Addr: 40, W: []byte{0x3}, R: []byte{1 << 7}}, {Addr: 40, W: []byte{0x3}, R: []byte{0x80}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...), }...),
}, },
opts: cap1188.DefaultOpts(), want: [8]TouchStatus{PressedStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus},
want: []cap1188.TouchStatus{cap1188.PressedStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus},
}, },
{name: "second pressed", {name: "second pressed",
bus: &i2ctest.Playback{ bus: &i2ctest.Playback{
Ops: append(setupPlaybackIO(), []i2ctest.IO{ Ops: append(setupPlaybackIO(), []i2ctest.IO{
{Addr: 40, W: []byte{0x3}, R: []byte{1 << 6}}, {Addr: 40, W: []byte{0x3}, R: []byte{0x40}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...), }...),
}, },
opts: cap1188.DefaultOpts(), want: [8]TouchStatus{OffStatus, PressedStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus},
want: []cap1188.TouchStatus{cap1188.OffStatus, cap1188.PressedStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus},
}, },
{name: "third pressed", {name: "third pressed",
bus: &i2ctest.Playback{ bus: &i2ctest.Playback{
Ops: append(setupPlaybackIO(), []i2ctest.IO{ Ops: append(setupPlaybackIO(), []i2ctest.IO{
{Addr: 40, W: []byte{0x3}, R: []byte{1 << 5}}, {Addr: 40, W: []byte{0x3}, R: []byte{0x20}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...),
},
opts: cap1188.DefaultOpts(),
want: []cap1188.TouchStatus{cap1188.OffStatus, cap1188.OffStatus, cap1188.PressedStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus},
},
{name: "forth pressed",
bus: &i2ctest.Playback{
Ops: append(setupPlaybackIO(), []i2ctest.IO{
{Addr: 40, W: []byte{0x3}, R: []byte{1 << 4}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...),
},
opts: cap1188.DefaultOpts(),
want: []cap1188.TouchStatus{cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.PressedStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus},
},
{name: "fifth pressed",
bus: &i2ctest.Playback{
Ops: append(setupPlaybackIO(), []i2ctest.IO{
{Addr: 40, W: []byte{0x3}, R: []byte{1 << 3}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...),
},
opts: cap1188.DefaultOpts(),
want: []cap1188.TouchStatus{cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.PressedStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus},
},
{name: "sixth pressed",
bus: &i2ctest.Playback{
Ops: append(setupPlaybackIO(), []i2ctest.IO{
{Addr: 40, W: []byte{0x3}, R: []byte{1 << 2}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...), }...),
}, },
opts: cap1188.DefaultOpts(), want: [8]TouchStatus{OffStatus, OffStatus, PressedStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus},
want: []cap1188.TouchStatus{cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.PressedStatus, cap1188.OffStatus, cap1188.OffStatus},
},
{name: "seventh pressed",
bus: &i2ctest.Playback{
Ops: append(setupPlaybackIO(), []i2ctest.IO{
{Addr: 40, W: []byte{0x3}, R: []byte{1 << 1}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...),
},
opts: cap1188.DefaultOpts(),
want: []cap1188.TouchStatus{cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.PressedStatus, cap1188.OffStatus},
}, },
{name: "eighth pressed", {name: "eighth pressed",
bus: &i2ctest.Playback{ bus: &i2ctest.Playback{
Ops: append(setupPlaybackIO(), []i2ctest.IO{ Ops: append(setupPlaybackIO(), []i2ctest.IO{
{Addr: 40, W: []byte{0x3}, R: []byte{1 << 0}}, {Addr: 40, W: []byte{0x3}, R: []byte{0x1}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...), }...),
}, },
opts: cap1188.DefaultOpts(), want: [8]TouchStatus{OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, PressedStatus},
want: []cap1188.TouchStatus{cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.PressedStatus},
}, },
{name: "3 pressed", {name: "3 pressed",
bus: &i2ctest.Playback{ bus: &i2ctest.Playback{
Ops: append(setupPlaybackIO(), []i2ctest.IO{ Ops: append(setupPlaybackIO(), []i2ctest.IO{
{Addr: 40, W: []byte{0x3}, R: []byte{(1 << 7) ^ (1 << 4) ^ (1 << 0)}}, {Addr: 40, W: []byte{0x3}, R: []byte{0x91}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...), }...),
}, },
opts: cap1188.DefaultOpts(), want: [8]TouchStatus{PressedStatus, OffStatus, OffStatus, PressedStatus, OffStatus, OffStatus, OffStatus, PressedStatus},
want: []cap1188.TouchStatus{cap1188.PressedStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.PressedStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.PressedStatus},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
d, err := cap1188.NewI2C(tt.bus, tt.opts) d, err := NewI2C(tt.bus, DefaultOpts())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -276,25 +158,25 @@ func TestDev_InputStatus(t *testing.T) {
t.Run("held touch sensors", func(t *testing.T) { t.Run("held touch sensors", func(t *testing.T) {
bus := &i2ctest.Playback{ bus := &i2ctest.Playback{
Ops: append(setupPlaybackIO(), []i2ctest.IO{ Ops: append(setupPlaybackIO(), []i2ctest.IO{
{Addr: 40, W: []byte{0x3}, R: []byte{1 << 7}}, {Addr: 40, W: []byte{0x3}, R: []byte{0x80}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
// repeat call to get status (still pressed) // repeat call to get status (still pressed)
{Addr: 40, W: []byte{0x3}, R: []byte{1 << 7}}, {Addr: 40, W: []byte{0x3}, R: []byte{0x80}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
// finall call // finall call
{Addr: 40, W: []byte{0x3}, R: []byte{0x0}}, {Addr: 40, W: []byte{0x3}, R: []byte{0x0}},
{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x10}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, //{Addr: 40, W: []byte{0x30}, R: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
}...), }...),
} }
// set the recorded response to have the retrigger option on // Set the recorded response to have the retrigger option on.
bus.Ops[10] = i2ctest.IO{Addr: 40, W: []byte{0x28, 0xff}, R: nil} bus.Ops[10] = i2ctest.IO{Addr: 40, W: []byte{0x28, 0xff}, R: nil}
opts := cap1188.DefaultOpts() opts := DefaultOpts()
// following option needs to be true so we can get the held status // Following option needs to be true so we can get the held status.
opts.RetriggerOnHold = true opts.RetriggerOnHold = true
d, err := cap1188.NewI2C(bus, opts) d, err := NewI2C(bus, opts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -303,7 +185,7 @@ func TestDev_InputStatus(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(got, []cap1188.TouchStatus{cap1188.PressedStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus}) { if !reflect.DeepEqual(got, [8]TouchStatus{PressedStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus}) {
t.Fatalf("expected to have the first sensor touched but instead got %v", got) t.Fatalf("expected to have the first sensor touched but instead got %v", got)
} }
// 2nd check // 2nd check
@ -311,7 +193,7 @@ func TestDev_InputStatus(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(got, []cap1188.TouchStatus{cap1188.HeldStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus, cap1188.OffStatus}) { if !reflect.DeepEqual(got, [8]TouchStatus{HeldStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus, OffStatus}) {
t.Fatalf("expected to have the first sensor touched but instead got %v", got) t.Fatalf("expected to have the first sensor touched but instead got %v", got)
} }
}) })
@ -346,3 +228,11 @@ func setupPlaybackIO() []i2ctest.IO {
{Addr: 40, W: []byte{0x44, 0x61}, R: nil}, {Addr: 40, W: []byte{0x44, 0x61}, R: nil},
} }
} }
func init() {
sleep = func(time.Duration) {}
flag.Parse()
if !testing.Verbose() {
log.SetOutput(ioutil.Discard)
}
}

Loading…
Cancel
Save