@ -18,39 +18,25 @@ import (
)
)
const (
const (
// I2CAddr is the default I2C address for the ADS1x15 components.
// I2CAddr is the default I2C address for the ADS1x15 components
I2CAddr uint16 = 0x48
I2CAddr uint16 = 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
Channel0 = 0
Channel0 = 0
Channel1 = 1
Channel1 = 1
Channel2 = 2
Channel2 = 2
Channel3 = 3
Channel3 = 3
)
)
// ConversionQuality represents a request for a compromise between energy saving versus conversion quality.
// ConversionQuality represents a request for a compromise between energy
// saving versus conversion quality.
type ConversionQuality int
type ConversionQuality int
const (
const (
// SaveEnergy optimizes the power consumption of the ADC, at the expense of the quality by converting at the gihest rate possible.
// SaveEnergy optimizes the power consumption of the ADC, at the expense of
// the quality by converting at the gihest rate possible.
SaveEnergy ConversionQuality = 0
SaveEnergy ConversionQuality = 0
// BestQuality will use the lowest suitable data rate to reduce the impact of the noise on the reading.
// BestQuality will use the lowest suitable data rate to reduce the impact of
// the noise on the reading.
BestQuality ConversionQuality = 1
BestQuality ConversionQuality = 1
)
)
@ -64,17 +50,12 @@ var DefaultOpts = Opts{
I2cAddress : I2CAddr ,
I2cAddress : I2CAddr ,
}
}
// Dev is the driver for the ADS1015/ADS1115 ADC
// Dev is an handle to an ADS1015/ADS1115 ADC.
type Dev struct {
type Dev struct {
// I2C Communication
c i2c . Dev
c i2c . Dev
name string
name string
gainConfig map [ int ] uint16
dataRates map [ int ] uint16
dataRates map [ int ] uint16
gainVoltage map [ int ] physic . ElectricPotential
mu sync . Mutex
mutex sync . Mutex
}
}
// Reading is the result of AnalogPin.Read() (obviously not the case right now but this could be)
// Reading is the result of AnalogPin.Read() (obviously not the case right now but this could be)
@ -94,21 +75,12 @@ type AnalogPin interface {
ReadContinuous ( ) <- chan Reading
ReadContinuous ( ) <- chan Reading
}
}
type ads1x15AnalogPin struct {
// NewADS1015 creates a new driver for the ADS1015 (12-bit ADC).
adc * Dev
func NewADS1015 ( i i2c . Bus , opts * Opts ) ( * Dev , error ) {
query [ ] byte
return & Dev {
voltageMultiplier physic . ElectricPotential
c : i2c . Dev { Bus : i , Addr : opts . I2cAddress } ,
waitTime time . Duration
name : "ADS1015" ,
requestedFrequency physic . Frequency
dataRates : map [ int ] uint16 {
stop chan struct { }
}
// NewADS1015 creates a new driver for the ADS1015 (12-bit ADC)
// Largely inspired by: https://github.com/adafruit/Adafruit_Python_ADS1x15
func NewADS1015 ( i i2c . Bus , opts * Opts ) ( l * Dev , err error ) {
l , err = newADS1x15 ( i , opts )
l . dataRates = map [ int ] uint16 {
128 : 0x0000 ,
128 : 0x0000 ,
250 : 0x0020 ,
250 : 0x0020 ,
490 : 0x0040 ,
490 : 0x0040 ,
@ -116,18 +88,16 @@ func NewADS1015(i i2c.Bus, opts *Opts) (l *Dev, err error) {
1600 : 0x0080 ,
1600 : 0x0080 ,
2400 : 0x00A0 ,
2400 : 0x00A0 ,
3300 : 0x00C0 ,
3300 : 0x00C0 ,
}
} ,
} , nil
l . name = "ADS1015"
return
}
}
// NewADS1115 creates a new driver for the ADS1115 (16-bit ADC)
// NewADS1115 creates a new driver for the ADS1115 (16-bit ADC).
func NewADS1115 ( i i2c . Bus , opts * Opts ) ( l * Dev , err error ) {
func NewADS1115 ( i i2c . Bus , opts * Opts ) ( * Dev , error ) {
l , err = newADS1x15 ( i , opts )
return & Dev {
c : i2c . Dev { Bus : i , Addr : opts . I2cAddress } ,
l . dataRates = map [ int ] uint16 {
name : "ADS1115" ,
dataRates : map [ int ] uint16 {
8 : 0x0000 ,
8 : 0x0000 ,
16 : 0x0020 ,
16 : 0x0020 ,
32 : 0x0040 ,
32 : 0x0040 ,
@ -136,69 +106,44 @@ func NewADS1115(i i2c.Bus, opts *Opts) (l *Dev, err error) {
250 : 0x00A0 ,
250 : 0x00A0 ,
475 : 0x00C0 ,
475 : 0x00C0 ,
860 : 0x00E0 ,
860 : 0x00E0 ,
}
l . name = "ADS1115"
return
}
func newADS1x15 ( i i2c . Bus , opts * Opts ) ( l * Dev , err error ) {
l = & Dev {
c : i2c . Dev { Bus : i , Addr : opts . I2cAddress } ,
// 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 ] physic . ElectricPotential {
2 / 3 : 6144 * physic . MilliVolt ,
1 : 4096 * physic . MilliVolt ,
2 : 2048 * physic . MilliVolt ,
4 : 1024 * physic . MilliVolt ,
8 : 512 * physic . MilliVolt ,
16 : 256 * physic . MilliVolt ,
} ,
} ,
}
} , nil
return
}
}
// String implements conn.Resource.
func ( d * Dev ) String ( ) string {
func ( d * Dev ) String ( ) string {
return d . name
return d . name
}
}
// Halt returns true if devices is halted successfully
// Halt implements conn.Resource.
func ( d * Dev ) Halt ( ) error { return nil }
func ( d * Dev ) Halt ( ) error {
return nil
}
// PinForChannel returns a pin able to measure the electric potential at the given channel
// PinForChannel returns a pin able to measure the electric potential at the
func ( d * Dev ) PinForChannel ( channel int , maxVoltage physic . ElectricPotential , requestedFrequency physic . Frequency , conversionQuality ConversionQuality ) ( pin AnalogPin , err error ) {
// given channel.
if err = d . checkChannel ( channel ) ; err != nil {
func ( d * Dev ) PinForChannel ( channel int , maxVoltage physic . ElectricPotential , requestedFrequency physic . Frequency , conversionQuality ConversionQuality ) ( AnalogPin , error ) {
return
if err := d . checkChannel ( channel ) ; err != nil {
return nil , err
}
}
mux := channel + 0x04
return d . prepareQuery ( channel + 0x04 , maxVoltage , requestedFrequency , conversionQuality )
return d . prepareQuery ( mux , maxVoltage , requestedFrequency , conversionQuality )
}
}
// PinForDifferenceOfChannels returns a pin which measures the difference in volts between 2 inputs: channelA - channelB.
// PinForDifferenceOfChannels returns a pin which measures the difference in
// volts between 2 inputs: channelA - channelB.
// diff can be:
// diff can be:
// * Channel 0 - channel 1
// * Channel 0 - channel 1
// * Channel 0 - channel 3
// * Channel 0 - channel 3
// * Channel 1 - channel 3
// * Channel 1 - channel 3
// * Channel 2 - channel 3
// * Channel 2 - channel 3
func ( d * Dev ) PinForDifferenceOfChannels ( channelA int , channelB int , maxVoltage physic . ElectricPotential , requestedFrequency physic . Frequency , conversionQuality ConversionQuality ) ( pin AnalogPin, err error ) {
func ( d * Dev ) PinForDifferenceOfChannels ( channelA int , channelB int , maxVoltage physic . ElectricPotential , requestedFrequency physic . Frequency , conversionQuality ConversionQuality ) ( AnalogPin, error ) {
var mux int
var mux int
if err = d . checkChannel ( channelA ) ; err != nil {
if err : = d . checkChannel ( channelA ) ; err != nil {
return
return nil , err
}
}
if err = d . checkChannel ( channelB ) ; err != nil {
if err : = d . checkChannel ( channelB ) ; err != nil {
return
return nil , err
}
}
if channelA == Channel0 && channelB == Channel1 {
if channelA == Channel0 && channelB == Channel1 {
@ -210,38 +155,34 @@ func (d *Dev) PinForDifferenceOfChannels(channelA int, channelB int, maxVoltage
} else if channelA == Channel2 && channelB == Channel3 {
} else if channelA == Channel2 && channelB == Channel3 {
mux = 3
mux = 3
} else {
} else {
err = errors . New ( "Only some differences of channels are allowed: 0 - 1, 0 - 3, 1 - 3 or 2 - 3" )
return nil , errors . New ( "only some differences of channels are allowed: 0 - 1, 0 - 3, 1 - 3 or 2 - 3" )
return
}
}
return d . prepareQuery ( mux , maxVoltage , requestedFrequency , conversionQuality )
return d . prepareQuery ( mux , maxVoltage , requestedFrequency , conversionQuality )
}
}
func ( d * Dev ) prepareQuery ( mux int , maxVoltage physic . ElectricPotential , requestedFrequency physic . Frequency , conversionQuality ConversionQuality ) ( pin AnalogPin, err error ) {
func ( d * Dev ) prepareQuery ( mux int , maxVoltage physic . ElectricPotential , requestedFrequency physic . Frequency , conversionQuality ConversionQuality ) ( AnalogPin, error ) {
// Determine the most appropriate gain
// Determine the most appropriate gain
gain , err := d . bestGainForElectricPotential ( maxVoltage )
gain , err := d . bestGainForElectricPotential ( maxVoltage )
if err != nil {
if err != nil {
return
return nil , err
}
}
// Validate the gain.
// Validate the gain.
gainConf , ok := d. gainConfig[ gain ]
gainConf , ok := gainConfig[ gain ]
if ! ok {
if ! ok {
err = errors . New ( "Gain must be one of: 2/3, 1, 2, 4, 8, 16" )
return nil , errors . New ( "gain must be one of: 2/3, 1, 2, 4, 8, 16" )
return
}
}
// Determine the voltage multiplier for this gain
// Determine the voltage multiplier for this gain .
voltageMultiplier , ok := d. gainVoltage[ gain ]
voltageMultiplier , ok := gainVoltage[ gain ]
if ! ok {
if ! ok {
err = errors . New ( "Gain must be one of: 2/3, 1, 2, 4, 8, 16" )
return nil , errors . New ( "gain must be one of: 2/3, 1, 2, 4, 8, 16" )
return
}
}
// Determine the most appropriate data rate
// Determine the most appropriate data rate
dataRate , err := d . bestDataRateForFrequency ( requestedFrequency , conversionQuality )
dataRate , err := d . bestDataRateForFrequency ( requestedFrequency , conversionQuality )
if err != nil {
if err != nil {
return
return nil , err
}
}
dataRateConf , ok := d . dataRates [ dataRate ]
dataRateConf , ok := d . dataRates [ dataRate ]
@ -252,9 +193,7 @@ func (d *Dev) prepareQuery(mux int, maxVoltage physic.ElectricPotential, request
for k := range d . dataRates {
for k := range d . dataRates {
keys = append ( keys , k )
keys = append ( keys , k )
}
}
return nil , fmt . Errorf ( "invalid data rate. Accepted values: %d" , keys )
err = fmt . Errorf ( "Invalid data rate. Accepted values: %d" , keys )
return
}
}
// Build the configuration value
// Build the configuration value
@ -281,26 +220,24 @@ func (d *Dev) prepareQuery(mux int, maxVoltage physic.ElectricPotential, request
// small offset to be sure (0.1 millisecond).
// small offset to be sure (0.1 millisecond).
waitTime := time . Second / time . Duration ( dataRate ) + 100 * time . Microsecond
waitTime := time . Second / time . Duration ( dataRate ) + 100 * time . Microsecond
pin = & ads1x15AnalogPin {
return & ads1x15AnalogPin {
adc : d ,
adc : d ,
query : query ,
query : query ,
voltageMultiplier : voltageMultiplier ,
voltageMultiplier : voltageMultiplier ,
waitTime : waitTime ,
waitTime : waitTime ,
requestedFrequency : requestedFrequency ,
requestedFrequency : requestedFrequency ,
}
} , nil
return
}
}
func ( d * Dev ) executePreparedQuery ( query [ ] byte , waitTime time . Duration , voltageMultiplier physic . ElectricPotential ) ( reading Reading, err error ) {
func ( d * Dev ) executePreparedQuery ( query [ ] byte , waitTime time . Duration , voltageMultiplier physic . ElectricPotential ) ( Reading, error ) {
// Lock the ADC converter to avoid multiple simultaneous readings.
// Lock the ADC converter to avoid multiple simultaneous readings.
d . mu tex . Lock ( )
d . mu . Lock ( )
defer d . mu tex . Unlock ( )
defer d . mu . Unlock ( )
// Send the config value to start the ADC conversion.
// Send the config value to start the ADC conversion.
// Explicitly break the 16-bit value down to a big endian pair of bytes.
// Explicitly break the 16-bit value down to a big endian pair of bytes.
if err = d . c . Tx ( query , nil ) ; err != nil {
if err : = d . c . Tx ( query , nil ) ; err != nil {
return
return Reading { } , err
}
}
// Wait for the ADC sample to finish.
// Wait for the ADC sample to finish.
@ -308,25 +245,26 @@ func (d *Dev) executePreparedQuery(query []byte, waitTime time.Duration, voltage
// Retrieve the result.
// Retrieve the result.
data := [ ] byte { 0 , 0 }
data := [ ] byte { 0 , 0 }
if err = d . c . Tx ( [ ] byte { ads1x15PointerConversion } , data ) ; err != nil {
if err : = d . c . Tx ( [ ] byte { ads1x15PointerConversion } , data ) ; err != nil {
return
return Reading { } , err
}
}
// Convert the raw data into physical value.
// Convert the raw data into physical value.
raw := int16 ( binary . BigEndian . Uint16 ( data ) )
raw := int16 ( binary . BigEndian . Uint16 ( data ) )
reading . Raw = int32 ( raw )
return Reading {
reading . V = physic . ElectricPotential ( reading . Raw ) * voltageMultiplier / physic . ElectricPotential ( 1 << 15 )
Raw : int32 ( raw ) ,
V : physic . ElectricPotential ( raw ) * voltageMultiplier / physic . ElectricPotential ( 1 << 15 ) ,
return
} , nil
}
}
// bestGainForElectricPotential returns the gain the most adapted to read up to the specified difference of potential.
// bestGainForElectricPotential returns the gain the most adapted to read up to
func ( d * Dev ) bestGainForElectricPotential ( voltage physic . ElectricPotential ) ( bestGain int , err error ) {
// the specified difference of potential.
func ( d * Dev ) bestGainForElectricPotential ( voltage physic . ElectricPotential ) ( int , error ) {
var max physic . ElectricPotential
var max physic . ElectricPotential
difference := physic . ElectricPotential ( math . MaxInt64 )
difference := physic . ElectricPotential ( math . MaxInt64 )
currentBestGain := - 1
currentBestGain := - 1
for key , value := range d. gainVoltage {
for key , value := range gainVoltage {
// We compute the maximum in case we need to display an error
// We compute the maximum in case we need to display an error
if value > max {
if value > max {
max = value
max = value
@ -339,22 +277,22 @@ func (d *Dev) bestGainForElectricPotential(voltage physic.ElectricPotential) (be
}
}
if currentBestGain < 0 {
if currentBestGain < 0 {
err = fmt . Errorf ( "The maximum voltage which can be read is %s" , max . String ( ) )
return 0 , fmt . Errorf ( "maximum voltage which can be read is %s" , max . String ( ) )
return
}
}
return currentBestGain , nil
bestGain = currentBestGain
return
}
}
// bestDataRateForFrequency returns the gain the most data rate to read samples at least at the requested frequency.
// bestDataRateForFrequency returns the gain the most data rate to read samples
func ( d * Dev ) bestDataRateForFrequency ( requestedFrequency physic . Frequency , conversionQuality ConversionQuality ) ( bestDataRate int , err error ) {
// at least at the requested frequency.
func ( d * Dev ) bestDataRateForFrequency ( requestedFrequency physic . Frequency , conversionQuality ConversionQuality ) ( int , error ) {
var max physic . Frequency
var max physic . Frequency
currentBestDataRate := - 1
currentBestDataRate := - 1
// In order to save energy, we are going to select the fastest conversion rate, as explained in the ADS1115 specifications:
// In order to save energy, we are going to select the fastest conversion
// 9.4.3 Duty Cycling For Low Power
// rate, as explained in the ADS1115 specifications: 9.4.3 Duty Cycling For
// When searching for the best quality, we will select the slowest conversion rate which is still faster than the requested frequency.
// Low Power.
// When searching for the best quality, we will select the slowest conversion
// rate which is still faster than the requested frequency.
var comparator func ( physic . Frequency , physic . Frequency ) bool
var comparator func ( physic . Frequency , physic . Frequency ) bool
var difference physic . Frequency
var difference physic . Frequency
@ -368,8 +306,7 @@ func (d *Dev) bestDataRateForFrequency(requestedFrequency physic.Frequency, conv
difference = physic . Frequency ( math . MaxInt64 )
difference = physic . Frequency ( math . MaxInt64 )
comparator = func ( newDiff physic . Frequency , difference physic . Frequency ) bool { return newDiff < difference }
comparator = func ( newDiff physic . Frequency , difference physic . Frequency ) bool { return newDiff < difference }
default :
default :
err = fmt . Errorf ( "unknown value for ConversionQuality" )
return 0 , fmt . Errorf ( "unknown value for ConversionQuality" )
return
}
}
for key := range d . dataRates {
for key := range d . dataRates {
@ -393,29 +330,73 @@ func (d *Dev) bestDataRateForFrequency(requestedFrequency physic.Frequency, conv
}
}
if currentBestDataRate < 0 {
if currentBestDataRate < 0 {
err = fmt . Errorf ( "The maximum frequency which can be read is %s" , max . String ( ) )
return 0 , fmt . Errorf ( "maximum frequency which can be read is %s" , max . String ( ) )
return
}
}
bestDataRate = currentBestDataRate
return currentBestDataRate , nil
return
}
}
func ( d * Dev ) checkChannel ( channel int ) ( err error ) {
func ( d * Dev ) checkChannel ( channel int ) error {
if channel < 0 || channel > 3 {
if channel < 0 || channel > 3 {
err = errors . New ( "I nvalid channel, must be between 0 and 3")
return errors . New ( "i nvalid channel, must be between 0 and 3")
}
}
return
return nil
}
}
// Range returns the maximum supported range [min, max] of the values.
//
func ( p * ads1x15AnalogPin ) Range ( ) ( minValue Reading , maxValue Reading ) {
maxValue . V = p . voltageMultiplier
maxValue . Raw = math . MaxInt16
minValue . V = - maxValue . V
minValue . Raw = math . MinInt16
return
const (
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
)
var (
// 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 ] physic . ElectricPotential {
2 / 3 : 6144 * physic . MilliVolt ,
1 : 4096 * physic . MilliVolt ,
2 : 2048 * physic . MilliVolt ,
4 : 1024 * physic . MilliVolt ,
8 : 512 * physic . MilliVolt ,
16 : 256 * physic . MilliVolt ,
}
)
type ads1x15AnalogPin struct {
adc * Dev
query [ ] byte
voltageMultiplier physic . ElectricPotential
waitTime time . Duration
requestedFrequency physic . Frequency
stop chan struct { }
}
// Range returns the maximum supported range [min, max] of the values.
func ( p * ads1x15AnalogPin ) Range ( ) ( Reading , Reading ) {
max := Reading { Raw : math . MaxInt16 , V : p . voltageMultiplier }
min := Reading { Raw : - math . MaxInt16 , V : - p . voltageMultiplier }
return min , max
}
}
// Read returns the current pin level.
// Read returns the current pin level.
@ -478,6 +459,4 @@ func (p *ads1x15AnalogPin) stopContinous() {
close ( p . stop )
close ( p . stop )
p . stop = nil
p . stop = nil
}
}
return
}
}