Skip to content

Commit

Permalink
i2c: support working for ads1015 adc providing AnalogReader interface
Browse files Browse the repository at this point in the history
Signed-off-by: deadprogram <[email protected]>
  • Loading branch information
deadprogram committed Apr 24, 2017
1 parent 3f89f78 commit 0de5875
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 26 deletions.
91 changes: 66 additions & 25 deletions drivers/i2c/ads1015_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package i2c

import (
"errors"
"strconv"
"time"

"gobot.io/x/gobot"
Expand Down Expand Up @@ -33,13 +34,15 @@ type ADS1015Driver struct {
// Optional params:
// i2c.WithBus(int): bus to use with this driver
// i2c.WithAddress(int): address to use with this driver
// i2c.WithADS1015Gain(int): input gain to use, valid options are
// any of the ADS1015RegConfigPga* const values
//
func NewADS1015Driver(a Connector, options ...func(Config)) *ADS1015Driver {
d := &ADS1015Driver{
name: gobot.DefaultName("ADS1015"),
connector: a,
conversionDelay: ADS1015ConversionDelay,
gain: ADS1015RegConfigPga6144v,
gain: ADS1015RegConfigPga6144V,
Config: NewConfig(),
}

Expand Down Expand Up @@ -75,6 +78,20 @@ func (d *ADS1015Driver) Start() (err error) {
// Halt returns true if device is halted successfully
func (d *ADS1015Driver) Halt() (err error) { return }

// WithADS1015Gain option sets the ADS1015Driver gain option.
// Valid gain settings are any of the ADS1015RegConfigPga* values
func WithADS1015Gain(val int) func(Config) {
return func(c Config) {
d, ok := c.(*ADS1015Driver)
if ok {
d.gain = uint16(val)
} else {
// TODO: return error for trying to set Gain for non-ADS1015Driver
return
}
}
}

// ReadADC gets a single ADC reading from the specified channel
func (d *ADS1015Driver) ReadADC(c uint8) (val uint16, err error) {
cfg := d.getDefaultConfig()
Expand All @@ -93,19 +110,25 @@ func (d *ADS1015Driver) ReadADC(c uint8) (val uint16, err error) {
return
}

if err = d.connection.WriteWordData(ADS1015RegPointerConfig, cfg); err != nil {
if _, err = d.connection.Write([]byte{ADS1015RegPointerConfig, byte(cfg >> 8), byte(cfg & 0xff)}); err != nil {
return
}

time.Sleep(time.Duration(d.conversionDelay) * time.Millisecond)

if val, err = d.connection.ReadWordData(ADS1015RegPointerConvert); err != nil {
val = 0
if _, err = d.connection.Write([]byte{ADS1015RegPointerConvert}); err != nil {
return
}

// Shift 12-bit results right 4 bits for the ADS1015
val >>= 4
b := []byte{0, 0}
if _, err = d.connection.Read(b); err != nil {
return
}

// Convert to 12-bit value
val = uint16(b[0]&0xff) << 4
val |= uint16(b[1]&0xff) >> 4

return
}

Expand Down Expand Up @@ -145,6 +168,26 @@ func (d *ADS1015Driver) ReadADCDifference23() (val int16, err error) {
return d.getConversionResult()
}

// AnalogRead returns value from analog reading of specified pin
func (d *ADS1015Driver) AnalogRead(pin string) (int, error) {
switch pin {
case "0-1":
val, e := d.ReadADCDifference01()
return int(val), e
case "2-3":
val, e := d.ReadADCDifference23()
return int(val), e
}

p, e := strconv.Atoi(pin)
if e != nil {
return 0, e
}

val, e2 := d.ReadADC(uint8(p))
return int(val), e2
}

func (d *ADS1015Driver) getDefaultConfig() uint16 {
var cfg uint16
cfg = ADS1015RegConfigCqueNone |
Expand All @@ -161,20 +204,18 @@ func (d *ADS1015Driver) getDefaultConfig() uint16 {
}

func (d *ADS1015Driver) getConversionResult() (val int16, err error) {
var v uint16
if v, err = d.connection.ReadWordData(ADS1015RegPointerConvert); err != nil {
return int16(0), err
}

// Shift 12-bit results right 4 bits for the ADS1015
v >>= 4

if v > 0x07FF {
// negative number - extend the sign to 16th bit
v |= 0xF000
d.connection.Write([]byte{ADS1015RegPointerConvert})
b := []byte{0, 0}
d.connection.Read(b)
// Convert to 12-bit value
val = int16(b[0]&0xff) << 4
val |= int16(b[1]&0xff) >> 4

if val&0x800 != 0 {
val -= 1 << 12
}

return int16(v), nil
return val, nil
}

const (
Expand All @@ -183,7 +224,7 @@ const (

// pointer register
ADS1015RegPointerMask = 0x03
ADS1015RegPointerConvert = 0x03
ADS1015RegPointerConvert = 0x00
ADS1015RegPointerConfig = 0x01
ADS1015RegPointerLowThresh = 0x02
ADS1015RegPointerHiThresh = 0x03
Expand Down Expand Up @@ -217,17 +258,17 @@ const (

ADS1015RegConfigPgaMask = 0x0e00
// +/-6.144V range = Gain 2/3
ADS1015RegConfigPga6144v = 0x0000
ADS1015RegConfigPga6144V = 0x0000
// +/-4.096V range = Gain 1
ADS1015RegConfigPga4096v = 0x0200
ADS1015RegConfigPga4096V = 0x0200
// +/-2.048V range = Gain 2 (default)
ADS1015RegConfigPga2048v = 0x0400
ADS1015RegConfigPga2048V = 0x0400
// +/-1.024V range = Gain 4
ADS1015RegConfigPga1024v = 0x0600
ADS1015RegConfigPga1024V = 0x0600
// +/-0.512V range = Gain 8
ADS1015RegConfigPga0512v = 0x0800
ADS1015RegConfigPga0512V = 0x0800
// +/-0.256V range = Gain 16
ADS1015RegConfigPga0256v = 0x0800
ADS1015RegConfigPga0256V = 0x0800

ADS1015RegConfigModeMask = 0x0100
// Continuous conversion mode
Expand Down
56 changes: 55 additions & 1 deletion drivers/i2c/ads1015_driver_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package i2c

import (
"errors"
"testing"

"gobot.io/x/gobot"
"gobot.io/x/gobot/drivers/aio"
"gobot.io/x/gobot/gobottest"
)

// the ADS1015Driver is a Driver
var _ gobot.Driver = (*ADS1015Driver)(nil)

// that supports the AnalogReader interface
var _ aio.AnalogReader = (*ADS1015Driver)(nil)

// --------- HELPERS
func initTestADS1015Driver() (driver *ADS1015Driver) {
driver, _ = initTestADS1015DriverWithStubbedAdaptor()
Expand Down Expand Up @@ -37,8 +43,56 @@ func TestADS1015DriverSetName(t *testing.T) {
}

func TestADS1015DriverOptions(t *testing.T) {
d := NewADS1015Driver(newI2cTestAdaptor(), WithBus(2))
d := NewADS1015Driver(newI2cTestAdaptor(), WithBus(2), WithADS1015Gain(ADS1015RegConfigPga2048V))
gobottest.Assert(t, d.GetBusOrDefault(1), 2)
gobottest.Assert(t, d.gain, uint16(ADS1015RegConfigPga2048V))
}

func TestADS1015StartConnectError(t *testing.T) {
d, adaptor := initTestADS1015DriverWithStubbedAdaptor()
adaptor.Testi2cConnectErr(true)
gobottest.Assert(t, d.Start(), errors.New("Invalid i2c connection"))
}

// --------- DRIVER SPECIFIC TESTS

func TestADS1015DriverAnalogRead(t *testing.T) {
d, adaptor := initTestADS1015DriverWithStubbedAdaptor()
d.Start()

adaptor.i2cReadImpl = func(b []byte) (int, error) {
copy(b, []byte{99, 1})
return 2, nil
}

val, err := d.AnalogRead("0")
gobottest.Assert(t, val, 1584)
gobottest.Assert(t, err, nil)

val, err = d.AnalogRead("0-1")
gobottest.Assert(t, val, 1584)
gobottest.Assert(t, err, nil)

val, err = d.AnalogRead("2-3")
gobottest.Assert(t, val, 1584)
gobottest.Assert(t, err, nil)
}

func TestADS1015DriverAnalogReadError(t *testing.T) {
d, a := initTestADS1015DriverWithStubbedAdaptor()
d.Start()

a.i2cReadImpl = func(b []byte) (int, error) {
return 0, errors.New("read error")
}

_, err := d.AnalogRead("0")
gobottest.Assert(t, err, errors.New("read error"))
}

func TestADS1015DriverAnalogReadInvalidPin(t *testing.T) {
d, _ := initTestADS1015DriverWithStubbedAdaptor()

_, err := d.AnalogRead("99")
gobottest.Assert(t, err, errors.New("Invalid channel."))
}
34 changes: 34 additions & 0 deletions examples/raspi_ads1015.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// +build example
//
// Do not build by default.

package main

import (
"fmt"
"time"

"gobot.io/x/gobot"
"gobot.io/x/gobot/drivers/i2c"
"gobot.io/x/gobot/platforms/raspi"
)

func main() {
a := raspi.NewAdaptor()
ads1015 := i2c.NewADS1015Driver(a)

work := func() {
gobot.Every(100*time.Millisecond, func() {
v, _ := ads1015.ReadADC(0)
fmt.Println("A0", v)
})
}

robot := gobot.NewRobot("ads1015bot",
[]gobot.Connection{a},
[]gobot.Device{ads1015},
work,
)

robot.Start()
}
34 changes: 34 additions & 0 deletions examples/raspi_grove_rotary_sensor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// +build example
//
// Do not build by default.

package main

import (
"fmt"

"gobot.io/x/gobot"
"gobot.io/x/gobot/drivers/aio"
"gobot.io/x/gobot/drivers/i2c"
"gobot.io/x/gobot/platforms/raspi"
)

func main() {
board := raspi.NewAdaptor()
ads1015 := i2c.NewADS1015Driver(board)
sensor := aio.NewGroveRotaryDriver(ads1015, "0")

work := func() {
sensor.On(aio.Data, func(data interface{}) {
fmt.Println("sensor", data)
})
}

robot := gobot.NewRobot("sensorBot",
[]gobot.Connection{board},
[]gobot.Device{ads1015, sensor},
work,
)

robot.Start()
}

0 comments on commit 0de5875

Please sign in to comment.