381 lines
9.3 KiB
Go
381 lines
9.3 KiB
Go
package i2c
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"strconv"
|
|
"time"
|
|
|
|
"fmt"
|
|
|
|
"gobot.io/x/gobot"
|
|
)
|
|
|
|
const (
|
|
|
|
// ADS1x15DefaultAddress is the default I2C address for the ADS1x15 components
|
|
ADS1x15DefaultAddress = 0x48
|
|
|
|
ads1x15PointerConversion = 0x00
|
|
ads1x15PointerConfig = 0x01
|
|
ads1x15PointerLowThreshold = 0x02
|
|
ads1x15PointerHighThreshold = 0x03
|
|
// Write: Set to start a single-conversion
|
|
ads1x15ConfigOsSingle = 0x8000
|
|
ads1x15ConfigMuxOffset = 12
|
|
ads1x15ConfigModeContinuous = 0x0000
|
|
//Single shoot mode
|
|
ads1x15ConfigModeSingle = 0x0100
|
|
|
|
ads1x15ConfigCompWindow = 0x0010
|
|
ads1x15ConfigCompAactiveHigh = 0x0008
|
|
ads1x15ConfigCompLatching = 0x0004
|
|
ads1x15ConfigCompQueDisable = 0x0003
|
|
)
|
|
|
|
// ADS1x15Driver is the Gobot driver for the ADS1015/ADS1115 ADC
|
|
type ADS1x15Driver struct {
|
|
name string
|
|
connector Connector
|
|
connection Connection
|
|
gainConfig map[int]uint16
|
|
dataRates map[int]uint16
|
|
gainVoltage map[int]float64
|
|
converter func([]byte) float64
|
|
DefaultGain int
|
|
DefaultDataRate int
|
|
Config
|
|
}
|
|
|
|
// NewADS1015Driver creates a new driver for the ADS1015 (12-bit ADC)
|
|
// Largely inspired by: https://github.com/adafruit/Adafruit_Python_ADS1x15
|
|
func NewADS1015Driver(a Connector, options ...func(Config)) *ADS1x15Driver {
|
|
l := newADS1x15Driver(a, options...)
|
|
|
|
l.dataRates = map[int]uint16{
|
|
128: 0x0000,
|
|
250: 0x0020,
|
|
490: 0x0040,
|
|
920: 0x0060,
|
|
1600: 0x0080,
|
|
2400: 0x00A0,
|
|
3300: 0x00C0,
|
|
}
|
|
if l.DefaultDataRate == 0 {
|
|
l.DefaultDataRate = 1600
|
|
}
|
|
|
|
l.converter = func(data []byte) (value float64) {
|
|
result := (int(data[0]) << 8) | int(data[1])
|
|
|
|
if result&0x8000 != 0 {
|
|
result -= 1 << 16
|
|
}
|
|
|
|
return float64(result) / float64(1<<15)
|
|
}
|
|
|
|
return l
|
|
}
|
|
|
|
// NewADS1115Driver creates a new driver for the ADS1115 (16-bit ADC)
|
|
func NewADS1115Driver(a Connector, options ...func(Config)) *ADS1x15Driver {
|
|
l := newADS1x15Driver(a, options...)
|
|
|
|
l.dataRates = map[int]uint16{
|
|
8: 0x0000,
|
|
16: 0x0020,
|
|
32: 0x0040,
|
|
64: 0x0060,
|
|
128: 0x0080,
|
|
250: 0x00A0,
|
|
475: 0x00C0,
|
|
860: 0x00E0,
|
|
}
|
|
|
|
if l.DefaultDataRate == 0 {
|
|
l.DefaultDataRate = 128
|
|
}
|
|
|
|
l.converter = func(data []byte) (value float64) {
|
|
result := (int(data[0]) << 8) | int(data[1])
|
|
|
|
if result&0x8000 != 0 {
|
|
result -= 1 << 16
|
|
}
|
|
|
|
return float64(result) / float64(1<<15)
|
|
}
|
|
|
|
return l
|
|
}
|
|
|
|
func newADS1x15Driver(a Connector, options ...func(Config)) *ADS1x15Driver {
|
|
l := &ADS1x15Driver{
|
|
name: gobot.DefaultName("ADS1x15"),
|
|
connector: a,
|
|
// Mapping of gain values to config register values.
|
|
gainConfig: map[int]uint16{
|
|
2 / 3: 0x0000,
|
|
1: 0x0200,
|
|
2: 0x0400,
|
|
4: 0x0600,
|
|
8: 0x0800,
|
|
16: 0x0A00,
|
|
},
|
|
gainVoltage: map[int]float64{
|
|
2 / 3: 6.144,
|
|
1: 4.096,
|
|
2: 2.048,
|
|
4: 1.024,
|
|
8: 0.512,
|
|
16: 0.256,
|
|
},
|
|
DefaultGain: 1,
|
|
|
|
Config: NewConfig(),
|
|
}
|
|
|
|
for _, option := range options {
|
|
option(l)
|
|
}
|
|
|
|
// TODO: add commands to API
|
|
return l
|
|
}
|
|
|
|
// Start initializes the sensor
|
|
func (d *ADS1x15Driver) Start() (err error) {
|
|
bus := d.GetBusOrDefault(d.connector.GetDefaultBus())
|
|
address := d.GetAddressOrDefault(ADS1x15DefaultAddress)
|
|
|
|
if d.connection, err = d.connector.GetConnection(address, bus); err != nil {
|
|
return err
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Name returns the Name for the Driver
|
|
func (d *ADS1x15Driver) Name() string { return d.name }
|
|
|
|
// SetName sets the Name for the Driver
|
|
func (d *ADS1x15Driver) SetName(n string) { d.name = n }
|
|
|
|
// Connection returns the connection for the Driver
|
|
func (d *ADS1x15Driver) Connection() gobot.Connection { return d.connector.(gobot.Connection) }
|
|
|
|
// Halt returns true if devices is halted successfully
|
|
func (d *ADS1x15Driver) Halt() (err error) { return }
|
|
|
|
// WithADS1x15Gain option sets the ADS1x15Driver gain option.
|
|
// Valid gain settings are any of the ADS1x15RegConfigPga* values
|
|
func WithADS1x15Gain(val int) func(Config) {
|
|
return func(c Config) {
|
|
d, ok := c.(*ADS1x15Driver)
|
|
if ok {
|
|
d.DefaultGain = val
|
|
} else {
|
|
// TODO: return error for trying to set Gain for non-ADS1015Driver
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// WithADS1x15DataRate option sets the ADS1x15Driver data rate option.
|
|
// Valid gain settings are any of the ADS1x15RegConfigPga* values
|
|
func WithADS1x15DataRate(val int) func(Config) {
|
|
return func(c Config) {
|
|
d, ok := c.(*ADS1x15Driver)
|
|
if ok {
|
|
d.DefaultDataRate = val
|
|
} else {
|
|
// TODO: return error for trying to set data rate for non-ADS1015Driver
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// BestGainForVoltage returns the gain the most adapted to read up to the specified difference of potential.
|
|
func (d *ADS1x15Driver) BestGainForVoltage(voltage float64) (bestGain int, err error) {
|
|
var max float64
|
|
difference := math.MaxFloat64
|
|
currentBestGain := -1
|
|
|
|
for key, value := range d.gainVoltage {
|
|
max = math.Max(max, value)
|
|
newDiff := value - voltage
|
|
if newDiff >= 0 && newDiff < difference {
|
|
difference = newDiff
|
|
currentBestGain = key
|
|
}
|
|
}
|
|
|
|
if currentBestGain < 0 {
|
|
err = fmt.Errorf("The maximum voltage which can be read is %f", max)
|
|
return
|
|
}
|
|
|
|
bestGain = currentBestGain
|
|
return
|
|
}
|
|
|
|
// ReadDifferenceWithDefaults reads the difference in V between 2 inputs. It uses the default gain and data rate
|
|
// diff can be:
|
|
// * 0: Channel 0 - channel 1
|
|
// * 1: Channel 0 - channel 3
|
|
// * 2: Channel 1 - channel 3
|
|
// * 3: Channel 2 - channel 3
|
|
func (d *ADS1x15Driver) ReadDifferenceWithDefaults(diff int) (value float64, err error) {
|
|
return d.ReadDifference(diff, d.DefaultGain, d.DefaultDataRate)
|
|
}
|
|
|
|
// ReadDifference reads the difference in V between 2 inputs.
|
|
// diff can be:
|
|
// * 0: Channel 0 - channel 1
|
|
// * 1: Channel 0 - channel 3
|
|
// * 2: Channel 1 - channel 3
|
|
// * 3: Channel 2 - channel 3
|
|
func (d *ADS1x15Driver) ReadDifference(diff int, gain int, dataRate int) (value float64, err error) {
|
|
if err = d.checkChannel(diff); err != nil {
|
|
return
|
|
}
|
|
|
|
return d.rawRead(diff, gain, dataRate)
|
|
}
|
|
|
|
// ReadWithDefaults reads the voltage at the specified channel (between 0 and 3).
|
|
// Default values are used for the gain and data rate. The result is in V.
|
|
func (d *ADS1x15Driver) ReadWithDefaults(channel int) (value float64, err error) {
|
|
return d.Read(channel, d.DefaultGain, d.DefaultDataRate)
|
|
}
|
|
|
|
// Read reads the voltage at the specified channel (between 0 and 3). The result is in V.
|
|
func (d *ADS1x15Driver) Read(channel int, gain int, dataRate int) (value float64, err error) {
|
|
if err = d.checkChannel(channel); err != nil {
|
|
return
|
|
}
|
|
mux := channel + 0x04
|
|
|
|
return d.rawRead(mux, gain, dataRate)
|
|
}
|
|
|
|
// AnalogRead returns value from analog reading of specified pin
|
|
func (d *ADS1x15Driver) AnalogRead(pin string) (value int, err error) {
|
|
var useDifference = false
|
|
var channel int
|
|
var read float64
|
|
|
|
// First case: the ADC is used in difference mode
|
|
switch pin {
|
|
case "0-1":
|
|
useDifference = true
|
|
channel = 0
|
|
break
|
|
case "0-3":
|
|
useDifference = true
|
|
channel = 1
|
|
break
|
|
case "1-3":
|
|
useDifference = true
|
|
channel = 2
|
|
break
|
|
case "2-3":
|
|
useDifference = true
|
|
channel = 3
|
|
break
|
|
}
|
|
|
|
if useDifference {
|
|
read, err = d.ReadDifferenceWithDefaults(channel)
|
|
} else {
|
|
// Second case: read the voltage at a specific pin, compared to the ground
|
|
channel, err = strconv.Atoi(pin)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
read, err = d.ReadWithDefaults(channel)
|
|
}
|
|
|
|
if err == nil {
|
|
value = int(gobot.ToScale(gobot.FromScale(read, 0, d.gainVoltage[d.DefaultGain]), 0, 1023))
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (d *ADS1x15Driver) rawRead(mux int, gain int, dataRate int) (value float64, err error) {
|
|
var config uint16
|
|
config = ads1x15ConfigOsSingle // Go out of power-down mode for conversion.
|
|
// Specify mux value.
|
|
config |= uint16((mux & 0x07) << ads1x15ConfigMuxOffset)
|
|
// Validate the passed in gain and then set it in the config.
|
|
|
|
gainConf, ok := d.gainConfig[gain]
|
|
|
|
if !ok {
|
|
err = errors.New("Gain must be one of: 2/3, 1, 2, 4, 8, 16")
|
|
return
|
|
}
|
|
config |= gainConf
|
|
// Set the mode (continuous or single shot).
|
|
config |= ads1x15ConfigModeSingle
|
|
// Get the default data rate if none is specified (default differs between
|
|
// ADS1015 and ADS1115).
|
|
dataRateConf, ok := d.dataRates[dataRate]
|
|
|
|
if !ok {
|
|
keys := []int{}
|
|
for k := range d.dataRates {
|
|
keys = append(keys, k)
|
|
}
|
|
|
|
err = fmt.Errorf("Invalid data rate. Accepted values: %d", keys)
|
|
return
|
|
}
|
|
// Set the data rate (this is controlled by the subclass as it differs
|
|
// between ADS1015 and ADS1115).
|
|
config |= dataRateConf
|
|
config |= ads1x15ConfigCompQueDisable // Disable comparator mode.
|
|
|
|
// Send the config value to start the ADC conversion.
|
|
// Explicitly break the 16-bit value down to a big endian pair of bytes.
|
|
if _, err = d.connection.Write([]byte{ads1x15PointerConfig, byte((config >> 8) & 0xFF), byte(config & 0xFF)}); err != nil {
|
|
return
|
|
}
|
|
|
|
// Wait for the ADC sample to finish based on the sample rate plus a
|
|
// small offset to be sure (0.1 millisecond).
|
|
time.Sleep(time.Duration(1000000/dataRate+100) * time.Microsecond)
|
|
|
|
// Retrieve the result.
|
|
if _, err = d.connection.Write([]byte{ads1x15PointerConversion}); err != nil {
|
|
return
|
|
}
|
|
|
|
data := make([]byte, 2)
|
|
_, err = d.connection.Read(data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
voltageMultiplier, ok := d.gainVoltage[gain]
|
|
|
|
if !ok {
|
|
err = errors.New("Gain must be one of: 2/3, 1, 2, 4, 8, 16")
|
|
return
|
|
}
|
|
|
|
value = d.converter(data) * voltageMultiplier
|
|
|
|
return
|
|
}
|
|
|
|
func (d *ADS1x15Driver) checkChannel(channel int) (err error) {
|
|
if channel < 0 || channel > 3 {
|
|
err = errors.New("Invalid channel, must be between 0 and 3")
|
|
}
|
|
return
|
|
}
|