diff --git a/experimental/devices/ads1x15/ads1x15.go b/experimental/devices/ads1x15/ads1x15.go index 7f35d1f..5d9c34d 100644 --- a/experimental/devices/ads1x15/ads1x15.go +++ b/experimental/devices/ads1x15/ads1x15.go @@ -18,39 +18,25 @@ import ( ) const ( - - // I2CAddr is the default I2C address for the ADS1x15 components + // I2CAddr is the default I2C address for the ADS1x15 components. 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 Channel1 = 1 Channel2 = 2 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 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 - // 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 ) @@ -64,17 +50,12 @@ var DefaultOpts = Opts{ I2cAddress: I2CAddr, } -// Dev is the driver for the ADS1015/ADS1115 ADC +// Dev is an handle to an ADS1015/ADS1115 ADC. type Dev struct { - // I2C Communication - c i2c.Dev - - name string - - gainConfig map[int]uint16 - dataRates map[int]uint16 - gainVoltage map[int]physic.ElectricPotential - mutex sync.Mutex + c i2c.Dev + name string + dataRates map[int]uint16 + mu sync.Mutex } // Reading is the result of AnalogPin.Read() (obviously not the case right now but this could be) @@ -94,111 +75,75 @@ type AnalogPin interface { ReadContinuous() <-chan Reading } -type ads1x15AnalogPin struct { - adc *Dev - query []byte - voltageMultiplier physic.ElectricPotential - waitTime time.Duration - requestedFrequency physic.Frequency - 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, - 250: 0x0020, - 490: 0x0040, - 920: 0x0060, - 1600: 0x0080, - 2400: 0x00A0, - 3300: 0x00C0, - } - - l.name = "ADS1015" - - return -} - -// NewADS1115 creates a new driver for the ADS1115 (16-bit ADC) -func NewADS1115(i i2c.Bus, opts *Opts) (l *Dev, err error) { - l, err = newADS1x15(i, opts) - - l.dataRates = map[int]uint16{ - 8: 0x0000, - 16: 0x0020, - 32: 0x0040, - 64: 0x0060, - 128: 0x0080, - 250: 0x00A0, - 475: 0x00C0, - 860: 0x00E0, - } - - l.name = "ADS1115" - - return +// NewADS1015 creates a new driver for the ADS1015 (12-bit ADC). +func NewADS1015(i i2c.Bus, opts *Opts) (*Dev, error) { + return &Dev{ + c: i2c.Dev{Bus: i, Addr: opts.I2cAddress}, + name: "ADS1015", + dataRates: map[int]uint16{ + 128: 0x0000, + 250: 0x0020, + 490: 0x0040, + 920: 0x0060, + 1600: 0x0080, + 2400: 0x00A0, + 3300: 0x00C0, + }, + }, nil } -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, +// NewADS1115 creates a new driver for the ADS1115 (16-bit ADC). +func NewADS1115(i i2c.Bus, opts *Opts) (*Dev, error) { + return &Dev{ + c: i2c.Dev{Bus: i, Addr: opts.I2cAddress}, + name: "ADS1115", + dataRates: map[int]uint16{ + 8: 0x0000, + 16: 0x0020, + 32: 0x0040, + 64: 0x0060, + 128: 0x0080, + 250: 0x00A0, + 475: 0x00C0, + 860: 0x00E0, }, - } - - return + }, nil } +// String implements conn.Resource. func (d *Dev) String() string { return d.name } -// Halt returns true if devices is halted successfully -func (d *Dev) Halt() error { return nil } +// Halt implements conn.Resource. +func (d *Dev) Halt() error { + return nil +} -// PinForChannel returns a pin able to measure the electric potential at the given channel -func (d *Dev) PinForChannel(channel int, maxVoltage physic.ElectricPotential, requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (pin AnalogPin, err error) { - if err = d.checkChannel(channel); err != nil { - return +// PinForChannel returns a pin able to measure the electric potential at the +// given channel. +func (d *Dev) PinForChannel(channel int, maxVoltage physic.ElectricPotential, requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (AnalogPin, error) { + if err := d.checkChannel(channel); err != nil { + return nil, err } - mux := channel + 0x04 - - return d.prepareQuery(mux, maxVoltage, requestedFrequency, conversionQuality) + return d.prepareQuery(channel+0x04, 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: // * Channel 0 - channel 1 // * Channel 0 - channel 3 // * Channel 1 - 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 - if err = d.checkChannel(channelA); err != nil { - return + if err := d.checkChannel(channelA); err != nil { + return nil, err } - if err = d.checkChannel(channelB); err != nil { - return + if err := d.checkChannel(channelB); err != nil { + return nil, err } if channelA == Channel0 && channelB == Channel1 { @@ -210,38 +155,34 @@ func (d *Dev) PinForDifferenceOfChannels(channelA int, channelB int, maxVoltage } else if channelA == Channel2 && channelB == Channel3 { mux = 3 } else { - err = errors.New("Only some differences of channels are allowed: 0 - 1, 0 - 3, 1 - 3 or 2 - 3") - return + return nil, errors.New("only some differences of channels are allowed: 0 - 1, 0 - 3, 1 - 3 or 2 - 3") } - 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 gain, err := d.bestGainForElectricPotential(maxVoltage) if err != nil { - return + return nil, err } // Validate the gain. - gainConf, ok := d.gainConfig[gain] + gainConf, ok := gainConfig[gain] if !ok { - err = errors.New("Gain must be one of: 2/3, 1, 2, 4, 8, 16") - return + return nil, errors.New("gain must be one of: 2/3, 1, 2, 4, 8, 16") } - // Determine the voltage multiplier for this gain - voltageMultiplier, ok := d.gainVoltage[gain] + // Determine the voltage multiplier for this gain. + voltageMultiplier, ok := gainVoltage[gain] if !ok { - err = errors.New("Gain must be one of: 2/3, 1, 2, 4, 8, 16") - return + return nil, errors.New("gain must be one of: 2/3, 1, 2, 4, 8, 16") } // Determine the most appropriate data rate dataRate, err := d.bestDataRateForFrequency(requestedFrequency, conversionQuality) if err != nil { - return + return nil, err } dataRateConf, ok := d.dataRates[dataRate] @@ -252,9 +193,7 @@ func (d *Dev) prepareQuery(mux int, maxVoltage physic.ElectricPotential, request for k := range d.dataRates { keys = append(keys, k) } - - err = fmt.Errorf("Invalid data rate. Accepted values: %d", keys) - return + return nil, fmt.Errorf("invalid data rate. Accepted values: %d", keys) } // 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). waitTime := time.Second/time.Duration(dataRate) + 100*time.Microsecond - pin = &ads1x15AnalogPin{ + return &ads1x15AnalogPin{ adc: d, query: query, voltageMultiplier: voltageMultiplier, waitTime: waitTime, requestedFrequency: requestedFrequency, - } - - return + }, nil } -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. - d.mutex.Lock() - defer d.mutex.Unlock() + d.mu.Lock() + defer d.mu.Unlock() // 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.c.Tx(query, nil); err != nil { - return + if err := d.c.Tx(query, nil); err != nil { + return Reading{}, err } // Wait for the ADC sample to finish. @@ -308,25 +245,26 @@ func (d *Dev) executePreparedQuery(query []byte, waitTime time.Duration, voltage // Retrieve the result. data := []byte{0, 0} - if err = d.c.Tx([]byte{ads1x15PointerConversion}, data); err != nil { - return + if err := d.c.Tx([]byte{ads1x15PointerConversion}, data); err != nil { + return Reading{}, err } // Convert the raw data into physical value. raw := int16(binary.BigEndian.Uint16(data)) - reading.Raw = int32(raw) - reading.V = physic.ElectricPotential(reading.Raw) * voltageMultiplier / physic.ElectricPotential(1<<15) - - return + return Reading{ + Raw: int32(raw), + V: physic.ElectricPotential(raw) * voltageMultiplier / physic.ElectricPotential(1<<15), + }, nil } -// bestGainForElectricPotential returns the gain the most adapted to read up to the specified difference of potential. -func (d *Dev) bestGainForElectricPotential(voltage physic.ElectricPotential) (bestGain int, err error) { +// bestGainForElectricPotential returns the gain the most adapted to read up to +// the specified difference of potential. +func (d *Dev) bestGainForElectricPotential(voltage physic.ElectricPotential) (int, error) { var max physic.ElectricPotential difference := physic.ElectricPotential(math.MaxInt64) 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 if value > max { max = value @@ -339,22 +277,22 @@ func (d *Dev) bestGainForElectricPotential(voltage physic.ElectricPotential) (be } if currentBestGain < 0 { - err = fmt.Errorf("The maximum voltage which can be read is %s", max.String()) - return + return 0, fmt.Errorf("maximum voltage which can be read is %s", max.String()) } - - bestGain = currentBestGain - return + return currentBestGain, nil } -// bestDataRateForFrequency returns the gain the most data rate to read samples at least at the requested frequency. -func (d *Dev) bestDataRateForFrequency(requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (bestDataRate int, err error) { +// bestDataRateForFrequency returns the gain the most data rate to read samples +// at least at the requested frequency. +func (d *Dev) bestDataRateForFrequency(requestedFrequency physic.Frequency, conversionQuality ConversionQuality) (int, error) { var max physic.Frequency currentBestDataRate := -1 - // In order to save energy, we are going to select the fastest conversion rate, as explained in the ADS1115 specifications: - // 9.4.3 Duty Cycling For Low Power - // When searching for the best quality, we will select the slowest conversion rate which is still faster than the requested frequency. + // In order to save energy, we are going to select the fastest conversion + // rate, as explained in the ADS1115 specifications: 9.4.3 Duty Cycling For + // 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 difference physic.Frequency @@ -368,8 +306,7 @@ func (d *Dev) bestDataRateForFrequency(requestedFrequency physic.Frequency, conv difference = physic.Frequency(math.MaxInt64) comparator = func(newDiff physic.Frequency, difference physic.Frequency) bool { return newDiff < difference } default: - err = fmt.Errorf("unknown value for ConversionQuality") - return + return 0, fmt.Errorf("unknown value for ConversionQuality") } for key := range d.dataRates { @@ -393,29 +330,73 @@ func (d *Dev) bestDataRateForFrequency(requestedFrequency physic.Frequency, conv } if currentBestDataRate < 0 { - err = fmt.Errorf("The maximum frequency which can be read is %s", max.String()) - return + return 0, fmt.Errorf("maximum frequency which can be read is %s", max.String()) } - bestDataRate = currentBestDataRate - return + return currentBestDataRate, nil } -func (d *Dev) checkChannel(channel int) (err error) { +func (d *Dev) checkChannel(channel int) error { if channel < 0 || channel > 3 { - err = errors.New("Invalid channel, must be between 0 and 3") + return errors.New("invalid 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 +// + +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{} +} - return +// 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. @@ -478,6 +459,4 @@ func (p *ads1x15AnalogPin) stopContinous() { close(p.stop) p.stop = nil } - - return }