diff --git a/README.rst b/README.rst index 8981f45..aaab788 100644 --- a/README.rst +++ b/README.rst @@ -62,31 +62,29 @@ Single Ended .. code-block:: python - import board - import busio - from adafruit_ads1x15.single_ended import ADS1015 + import time + import board + import busio + import adafruit_ads1x15.ads1015 as ADS + from adafruit_ads1x15.analog_in import AnalogIn - i2c = busio.I2C(board.SCL, board.SDA) - adc = ADS1015(i2c) - while True: - # channel 0 - print(adc[0].value, adc[0].volts) + # Create the I2C bus + i2c = busio.I2C(board.SCL, board.SDA) -Differential ------------- + # Create the ADC object using the I2C bus + ads = ADS.ADS1015(i2c) -.. code-block:: python + # Create single-ended input on channel 0 + chan = AnalogIn(ads, ADS.P0) - import board - import busio - from adafruit_ads1x15.differential import ADS1015 + # Create differential input between channel 0 and 1 + #chan = AnalogIn(ads, ADS.P0, ADS.P1) - i2c = busio.I2C(board.SCL, board.SDA) - adc = ADS1015(i2c) - while True: - # channel 0 - channel 1 - print(adc[(0,1)].value, adc[(0,1)].volts) + print("{:>5}\t{:>5}".format('raw', 'v')) + while True: + print("{:>5}\t{:>5.3f}".format(chan.value, chan.voltage)) + time.sleep(0.5) Contributing ============ diff --git a/adafruit_ads1x15/adafruit_ads1x15.py b/adafruit_ads1x15/adafruit_ads1x15.py deleted file mode 100644 index 1eaa675..0000000 --- a/adafruit_ads1x15/adafruit_ads1x15.py +++ /dev/null @@ -1,233 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Carter Nelson for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -""" -`adafruit_ads1x15` -==================================================== - -CircuitPython driver for ADS1015/1115 ADCs. - -* Author(s): Carter Nelson -""" - -__version__ = "0.0.0-auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ADS1x15.git" - -import time -from micropython import const -from adafruit_bus_device.i2c_device import I2CDevice - -# Register and other configuration values: -# pylint: disable=bad-whitespace -ADS1X15_DEFAULT_ADDRESS = const(0x48) -ADS1X15_POINTER_CONVERSION = const(0x00) -ADS1X15_POINTER_CONFIG = const(0x01) -ADS1X15_POINTER_LOW_THRESHOLD = const(0x02) -ADS1X15_POINTER_HIGH_THRESHOLD = const(0x03) -ADS1X15_CONFIG_OS_SINGLE = const(0x8000) -ADS1X15_CONFIG_MUX_OFFSET = const(12) -# Maping of gain values to config register values. -ADS1X15_CONFIG_GAIN = { - 2/3: 0x0000, - 1: 0x0200, - 2: 0x0400, - 4: 0x0600, - 8: 0x0800, - 16: 0x0A00 -} -ADS1X15_PGA_RANGE = { - 2/3: 6.144, - 1: 4.096, - 2: 2.048, - 4: 1.024, - 8: 0.512, - 16: 0.256 -} -ADS1X15_CONFIG_MODE_CONTINUOUS = const(0x0000) -ADS1X15_CONFIG_MODE_SINGLE = const(0x0100) -# Mapping of data/sample rate to config register values for ADS1015 (faster). -ADS1015_CONFIG_DR = { - 128: 0x0000, - 250: 0x0020, - 490: 0x0040, - 920: 0x0060, - 1600: 0x0080, - 2400: 0x00A0, - 3300: 0x00C0 -} -# Mapping of data/sample rate to config register values for ADS1115 (slower). -ADS1115_CONFIG_DR = { - 8: 0x0000, - 16: 0x0020, - 32: 0x0040, - 64: 0x0060, - 128: 0x0080, - 250: 0x00A0, - 475: 0x00C0, - 860: 0x00E0 -} -ADS1X15_CONFIG_COMP_WINDOW = const(0x0010) -ADS1X15_CONFIG_COMP_ACTIVE_HIGH = const(0x0008) -ADS1X15_CONFIG_COMP_LATCHING = const(0x0004) -ADS1X15_CONFIG_COMP_QUE = { - 1: 0x0000, - 2: 0x0001, - 4: 0x0002 -} -ADS1X15_CONFIG_COMP_QUE_DISABLE = const(0x0003) -ADS1X15_DIFF_CHANNELS = { - (0,1): 0, - (0,3): 1, - (1,3): 2, - (2,3): 3 -} -# pylint: enable=bad-whitespace - -class ADC_Channel(object): - """Provides per channel access to ADC readings.""" - - def __init__(self, adc, channel): - self._adc = adc - self._channel = channel - - @property - def value(self, ): - """ADC reading in raw counts.""" - return self._adc._read_channel(self._channel) # pylint: disable=protected-access - - @property - def volts(self, ): - """ADC reading in volts.""" - return self._adc._read_channel_volts(self._channel) # pylint: disable=protected-access - - -class ADS1x15(object): - """Base functionality for ADS1x15 analog to digital converters.""" - - def __init__(self, i2c, address=ADS1X15_DEFAULT_ADDRESS): - self.buf = bytearray(3) - self.i2c_device = I2CDevice(i2c, address) - self.bits = None - self._channels = [ADC_Channel(self, 0), - ADC_Channel(self, 1), - ADC_Channel(self, 2), - ADC_Channel(self, 3)] - - def _write_register(self, reg, value): - """Write 16 bit value to register.""" - self.buf[0] = reg - self.buf[1] = (value >> 8) & 0xFF - self.buf[2] = value & 0xFF - with self.i2c_device as i2c: - i2c.write(self.buf) - - def _read_register(self, reg): - """Return 16 bit register value as tuple of (LSB, MSB).""" - self.buf[0] = reg - with self.i2c_device as i2c: - i2c.write(self.buf, end=1, stop=False) - i2c.readinto(self.buf, start=1) - return self.buf[1] << 8 | self.buf[2] - - def _data_rate_default(self): - """Retrieve the default data rate for this ADC (in samples per second). - Should be implemented by subclasses. - """ - raise NotImplementedError('Subclasses must implement _data_rate_default!') - - def _data_rate_config(self, data_rate): - """Subclasses should override this function and return a 16-bit value - that can be OR'ed with the config register to set the specified - data rate. If a value of None is specified then a default data_rate - setting should be returned. If an invalid or unsupported data_rate is - provided then an exception should be thrown. - """ - raise NotImplementedError('Subclass must implement _data_rate_config function!') - - def _conversion_value(self, low, high): - """Subclasses should override this function that takes the low and high - byte of a conversion result and returns a signed integer value. - """ - raise NotImplementedError('Subclass must implement _conversion_value function!') - - def _read_channel(self, channel): - """Subclasses should override this function to return a value for the - requested channels as a signed integer value. - """ - raise NotImplementedError('Subclass must implement _read_channel function!') - - def _read_channel_volts(self, channel): - """Subclasses should override this function to return a value for the - requested channels as a float value. - """ - raise NotImplementedError('Subclass must implement _read_channel_volts function!') - - def _conversion_complete(self): - """Return status of ADC conversion.""" - # OS is bit 15 - # OS = 0: Device is currently performing a conversion - # OS = 1: Device is not currently performing a conversion - return self._read_register(ADS1X15_POINTER_CONFIG) & 0x8000 - - def _read(self, mux, gain, data_rate, mode): - """Perform an ADC read with the provided mux, gain, data_rate, and mode - values. Returns the signed integer result of the read. - """ - config = ADS1X15_CONFIG_OS_SINGLE # Go out of power-down mode for conversion. - # Specify mux value. - config |= (mux & 0x07) << ADS1X15_CONFIG_MUX_OFFSET - # Validate the passed in gain and then set it in the config. - if gain not in ADS1X15_CONFIG_GAIN: - raise ValueError('Gain must be one of: 2/3, 1, 2, 4, 8, 16') - config |= ADS1X15_CONFIG_GAIN[gain] - # Set the mode (continuous or single shot). - config |= mode - # Get the default data rate if none is specified (default differs between - # ADS1015 and ADS1115). - if data_rate is None: - data_rate = self._data_rate_default() - # Set the data rate (this is controlled by the subclass as it differs - # between ADS1015 and ADS1115). - config |= self._data_rate_config(data_rate) - config |= ADS1X15_CONFIG_COMP_QUE_DISABLE # Disble comparator mode. - # Send the config value to start the ADC conversion. - self._write_register(ADS1X15_POINTER_CONFIG, config) - # Wait for conversion to complete - while not self._conversion_complete(): - time.sleep(0.01) - # Return the result - return self.get_last_result() - - def stop_adc(self): - """Stop all continuous ADC conversions (either normal or difference mode). - """ - # Set the config register to its default value of 0x8583 to stop - # continuous conversions. - self._write_register(ADS1X15_POINTER_CONFIG, 0x8583) - - def get_last_result(self): - """Read the last conversion result when in continuous conversion mode. - Will return a signed integer value. - """ - # Retrieve the conversion register value, convert to a signed int, and - # return it. - result = self._read_register(ADS1X15_POINTER_CONVERSION) - return self._conversion_value(result & 0xff, result >> 8) diff --git a/adafruit_ads1x15/ads1015.py b/adafruit_ads1x15/ads1015.py new file mode 100644 index 0000000..de7573d --- /dev/null +++ b/adafruit_ads1x15/ads1015.py @@ -0,0 +1,76 @@ +# The MIT License (MIT) +# +# Copyright (c) 2018 Carter Nelson for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`ads1015` +==================================================== + +CircuitPython driver for ADS1015 ADCs. + +* Author(s): Carter Nelson +""" +import struct +from .ads1x15 import ADS1x15 + +# Data sample rates +_ADS1015_CONFIG_DR = { + 128: 0x0000, + 250: 0x0020, + 490: 0x0040, + 920: 0x0060, + 1600: 0x0080, + 2400: 0x00A0, + 3300: 0x00C0 +} + +# Pins +P0 = 0 +P1 = 1 +P2 = 2 +P3 = 3 + +class ADS1015(ADS1x15): + """Class for the ADS1015 12 bit ADC.""" + + @property + def bits(self): + """The ADC bit resolution.""" + return 12 + + @property + def rates(self): + """Possible data rate settings.""" + r = list(_ADS1015_CONFIG_DR.keys()) + r.sort() + return r + + @property + def rate_config(self): + """Rate configuration masks.""" + return _ADS1015_CONFIG_DR + + def _data_rate_default(self): + return 1600 + + def _conversion_value(self, raw_adc): + raw_adc = raw_adc.to_bytes(2, "big") + value = struct.unpack(">h", raw_adc)[0] + return value >> 4 diff --git a/adafruit_ads1x15/ads1115.py b/adafruit_ads1x15/ads1115.py new file mode 100644 index 0000000..f2bb21d --- /dev/null +++ b/adafruit_ads1x15/ads1115.py @@ -0,0 +1,77 @@ +# The MIT License (MIT) +# +# Copyright (c) 2018 Carter Nelson for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`ads1115` +==================================================== + +CircuitPython driver for 1115 ADCs. + +* Author(s): Carter Nelson +""" +import struct +from .ads1x15 import ADS1x15 + +# Data sample rates +_ADS1115_CONFIG_DR = { + 8: 0x0000, + 16: 0x0020, + 32: 0x0040, + 64: 0x0060, + 128: 0x0080, + 250: 0x00A0, + 475: 0x00C0, + 860: 0x00E0 +} + +# Pins +P0 = 0 +P1 = 1 +P2 = 2 +P3 = 3 + +class ADS1115(ADS1x15): + """Class for the ADS1115 16 bit ADC.""" + + @property + def bits(self): + """The ADC bit resolution.""" + return 16 + + @property + def rates(self): + """Possible data rate settings.""" + r = list(_ADS1115_CONFIG_DR.keys()) + r.sort() + return r + + @property + def rate_config(self): + """Rate configuration masks.""" + return _ADS1115_CONFIG_DR + + def _data_rate_default(self): + return 128 + + def _conversion_value(self, raw_adc): + raw_adc = raw_adc.to_bytes(2, "big") + value = struct.unpack(">h", raw_adc)[0] + return value diff --git a/adafruit_ads1x15/ads1x15.py b/adafruit_ads1x15/ads1x15.py new file mode 100644 index 0000000..12fac09 --- /dev/null +++ b/adafruit_ads1x15/ads1x15.py @@ -0,0 +1,186 @@ +# The MIT License (MIT) +# +# Copyright (c) 2018 Carter Nelson for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`ads1x15` +==================================================== + +CircuitPython base class driver for ADS1015/1115 ADCs. + +* Author(s): Carter Nelson +""" + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ADS1x15.git" + +import time +from micropython import const +from adafruit_bus_device.i2c_device import I2CDevice + +# pylint: disable=bad-whitespace +_ADS1X15_DEFAULT_ADDRESS = const(0x48) +_ADS1X15_POINTER_CONVERSION = const(0x00) +_ADS1X15_POINTER_CONFIG = const(0x01) +_ADS1X15_CONFIG_OS_SINGLE = const(0x8000) +_ADS1X15_CONFIG_MUX_OFFSET = const(12) +_ADS1X15_CONFIG_MODE_CONTINUOUS = const(0x0000) +_ADS1X15_CONFIG_MODE_SINGLE = const(0x0100) +_ADS1X15_CONFIG_COMP_QUE_DISABLE = const(0x0003) +_ADS1X15_CONFIG_GAIN = { + 2/3: 0x0000, + 1: 0x0200, + 2: 0x0400, + 4: 0x0600, + 8: 0x0800, + 16: 0x0A00 +} +# pylint: enable=bad-whitespace + +class ADS1x15(object): + """Base functionality for ADS1x15 analog to digital converters.""" + + def __init__(self, i2c, gain=1, data_rate=None, mode=_ADS1X15_CONFIG_MODE_SINGLE, + address=_ADS1X15_DEFAULT_ADDRESS): + #pylint: disable=too-many-arguments + self.buf = bytearray(3) + self._data_rate = self._gain = self._mode = None + self.gain = gain + self.data_rate = self._data_rate_default() if data_rate is None else data_rate + self.mode = mode + self.i2c_device = I2CDevice(i2c, address) + + @property + def data_rate(self): + """The data rate for ADC conversion in samples per second.""" + return self._data_rate + + @data_rate.setter + def data_rate(self, rate): + possible_rates = self.rates + if rate not in possible_rates: + raise ValueError("Data rate must be one of: {}".format(possible_rates)) + self._data_rate = rate + + @property + def rates(self): + """Possible data rate settings.""" + raise NotImplementedError('Subclass must implement rates property.') + + @property + def rate_config(self): + """Rate configuration masks.""" + raise NotImplementedError('Subclass must implement rate_config property.') + + @property + def gain(self): + """The ADC gain.""" + return self._gain + + @gain.setter + def gain(self, gain): + possible_gains = self.gains + if gain not in possible_gains: + raise ValueError("Gain must be one of: {}".format(possible_gains)) + self._gain = gain + + @property + def gains(self): + """Possible gain settings.""" + g = list(_ADS1X15_CONFIG_GAIN.keys()) + g.sort() + return g + + @property + def mode(self): + """The ADC conversion mode.""" + return self._mode + + @mode.setter + def mode(self, mode): + if mode != _ADS1X15_CONFIG_MODE_CONTINUOUS and mode != _ADS1X15_CONFIG_MODE_SINGLE: + raise ValueError("Unsupported mode.") + self._mode = mode + + def read(self, pin, is_differential=False): + """I2C Interface for ADS1x15-based ADCs reads. + + params: + :param pin: individual or differential pin. + :param bool is_differential: single-ended or differential read. + """ + pin = pin if is_differential else pin + 0x04 + return self._read(pin) + + def _data_rate_default(self): + """Retrieve the default data rate for this ADC (in samples per second). + Should be implemented by subclasses. + """ + raise NotImplementedError('Subclasses must implement _data_rate_default!') + + def _conversion_value(self, raw_adc): + """Subclasses should override this function that takes the 16 raw ADC + values of a conversion result and returns a signed integer value. + """ + raise NotImplementedError('Subclass must implement _conversion_value function!') + + def _read(self, pin): + """Perform an ADC read. Returns the signed integer result of the read.""" + config = _ADS1X15_CONFIG_OS_SINGLE + config |= (pin & 0x07) << _ADS1X15_CONFIG_MUX_OFFSET + config |= _ADS1X15_CONFIG_GAIN[self.gain] + config |= self.mode + config |= self.rate_config[self.data_rate] + config |= _ADS1X15_CONFIG_COMP_QUE_DISABLE + self._write_register(_ADS1X15_POINTER_CONFIG, config) + + while not self._conversion_complete(): + time.sleep(0.01) + + return self.get_last_result() + + def _conversion_complete(self): + """Return status of ADC conversion.""" + # OS is bit 15 + # OS = 0: Device is currently performing a conversion + # OS = 1: Device is not currently performing a conversion + return self._read_register(_ADS1X15_POINTER_CONFIG) & 0x8000 + + def get_last_result(self): + """Read the last conversion result when in continuous conversion mode. + Will return a signed integer value. + """ + return self._conversion_value(self._read_register(_ADS1X15_POINTER_CONVERSION)) + + def _write_register(self, reg, value): + """Write 16 bit value to register.""" + self.buf[0] = reg + self.buf[1] = (value >> 8) & 0xFF + self.buf[2] = value & 0xFF + with self.i2c_device as i2c: + i2c.write(self.buf) + + def _read_register(self, reg): + """Read 16 bit register value.""" + self.buf[0] = reg + with self.i2c_device as i2c: + i2c.write(self.buf, end=1, stop=False) + i2c.readinto(self.buf, end=2) + return self.buf[0] << 8 | self.buf[1] diff --git a/adafruit_ads1x15/analog_in.py b/adafruit_ads1x15/analog_in.py new file mode 100644 index 0000000..8e2cc35 --- /dev/null +++ b/adafruit_ads1x15/analog_in.py @@ -0,0 +1,80 @@ +# The MIT License (MIT) +# +# Copyright (c) 2018 Carter Nelson for Adafruit +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`analog_in` +============================== +AnalogIn for single-ended and +differential ADC readings. + +* Author(s): Carter Nelson, adapted from MCP3xxx original by Brent Rubell +""" + +# pylint: disable=bad-whitespace +_ADS1X15_DIFF_CHANNELS = { + (0, 1): 0, + (0, 3): 1, + (1, 3): 2, + (2, 3): 3 +} +_ADS1X15_PGA_RANGE = { + 2/3: 6.144, + 1: 4.096, + 2: 2.048, + 4: 1.024, + 8: 0.512, + 16: 0.256 +} +# pylint: enable=bad-whitespace + +class AnalogIn(): + """AnalogIn Mock Implementation for ADC Reads.""" + + def __init__(self, ads, positive_pin, negative_pin=None): + """AnalogIn + + :param ads: The ads object. + :param ~digitalio.DigitalInOut positive_pin: Required pin for single-ended. + :param ~digitalio.DigitalInOut negative_pin: Optional pin for differential reads. + """ + self._ads = ads + self._pin_setting = positive_pin + self._negative_pin = negative_pin + self.is_differential = False + if negative_pin is not None: + pins = (self._pin_setting, self._negative_pin) + if pins not in _ADS1X15_DIFF_CHANNELS: + raise ValueError("Differential channels must be one of: {}" + .format(list(_ADS1X15_DIFF_CHANNELS.keys()))) + self._pin_setting = _ADS1X15_DIFF_CHANNELS[pins] + self.is_differential = True + + @property + def value(self): + """Returns the value of an ADC pin as an integer.""" + return self._ads.read(self._pin_setting, is_differential=self.is_differential) + + @property + def voltage(self): + """Returns the voltage from the ADC pin as a floating point value.""" + raw = self.value + volts = raw * (_ADS1X15_PGA_RANGE[self._ads.gain] / (2**(self._ads.bits-1) - 1)) + return volts diff --git a/adafruit_ads1x15/differential.py b/adafruit_ads1x15/differential.py deleted file mode 100644 index fb0ae56..0000000 --- a/adafruit_ads1x15/differential.py +++ /dev/null @@ -1,151 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Carter Nelson for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -""" -`adafruit_ads1x15.differential` -==================================================== - -Differential driver for ADS1015/1115 ADCs. - -* Author(s): Carter Nelson -""" - -__version__ = "0.0.0-auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ADS1x15.git" - -from .adafruit_ads1x15 import ADS1x15, ADS1X15_DIFF_CHANNELS -from .adafruit_ads1x15 import ADS1X15_CONFIG_MODE_SINGLE, ADS1X15_CONFIG_MODE_CONTINUOUS -from .adafruit_ads1x15 import ADS1X15_PGA_RANGE, ADS1015_CONFIG_DR, ADS1115_CONFIG_DR - -# pylint: disable=abstract-method -class ADS1x15_Differential(ADS1x15): - """Base functionality for ADS1x15 analog to digital converters operating - in differential mode.""" - - def __getitem__(self, key): - return self._channels[ADS1X15_DIFF_CHANNELS[key]] - - def read_adc_difference(self, differential, gain=1, data_rate=None): - """Read the difference between two ADC channels and return the ADC value - as a signed integer result. Differential must be one of: - - 0 = Channel 0 minus channel 1 - - 1 = Channel 0 minus channel 3 - - 2 = Channel 1 minus channel 3 - - 3 = Channel 2 minus channel 3 - """ - if not 0 <= differential <= 3: - raise ValueError('Differential must be a value within 0-3!') - # Perform a single shot read using the provided differential value - # as the mux value (which will enable differential mode). - return self._read(differential, gain, data_rate, ADS1X15_CONFIG_MODE_SINGLE) - - def read_volts_difference(self, differential, gain=1, data_rate=None): - """Read the difference between two ADC channels and return the voltage value - as a floating point result. Differential must be one of: - - 0 = Channel 0 minus channel 1 - - 1 = Channel 0 minus channel 3 - - 2 = Channel 1 minus channel 3 - - 3 = Channel 2 minus channel 3 - """ - if not 0 <= differential <= 3: - raise ValueError('Differential must be a value within 0-3!') - raw = self.read_adc_difference(differential, gain, data_rate) - volts = raw * (ADS1X15_PGA_RANGE[gain] / (2**(self.bits-1) - 1)) - return volts - - def start_adc_difference(self, differential, gain=1, data_rate=None): - """Start continuous ADC conversions between two ADC channels. Differential - must be one of: - - 0 = Channel 0 minus channel 1 - - 1 = Channel 0 minus channel 3 - - 2 = Channel 1 minus channel 3 - - 3 = Channel 2 minus channel 3 - Will return an initial conversion result, then call the get_last_result() - function continuously to read the most recent conversion result. Call - stop_adc() to stop conversions. - """ - if not 0 <= differential <= 3: - raise ValueError('Differential must be a value within 0-3!') - # Perform a single shot read using the provided differential value - # as the mux value (which will enable differential mode). - return self._read(differential, gain, data_rate, ADS1X15_CONFIG_MODE_CONTINUOUS) -# pylint: enable=abstract-method - -class ADS1015(ADS1x15_Differential): - """ADS1015 12-bit differential analog to digital converter instance.""" - - def __init__(self, *args, **kwargs): - super(ADS1015, self).__init__(*args, **kwargs) - self.bits = 12 - - def _data_rate_default(self): - # Default from datasheet page 19, config register DR bit default. - return 1600 - - def _data_rate_config(self, data_rate): - if data_rate not in ADS1015_CONFIG_DR: - raise ValueError('Data rate must be one of: 128, 250, 490, 920, 1600, 2400, 3300') - return ADS1015_CONFIG_DR[data_rate] - - def _conversion_value(self, low, high): - # Convert to 12-bit signed value. - value = ((high & 0xFF) << 4) | ((low & 0xFF) >> 4) - # Check for sign bit and turn into a negative value if set. - if value & 0x800 != 0: - value -= 1 << 12 - return value - - def _read_channel(self, channel): - return self.read_adc_difference(channel) - - def _read_channel_volts(self, channel): - return self.read_volts_difference(channel) - - -class ADS1115(ADS1x15_Differential): - """ADS1115 16-bit differential analog to digital converter instance.""" - - def __init__(self, *args, **kwargs): - super(ADS1115, self).__init__(*args, **kwargs) - self.bits = 16 - - def _data_rate_default(self): - # Default from datasheet page 16, config register DR bit default. - return 128 - - def _data_rate_config(self, data_rate): - if data_rate not in ADS1115_CONFIG_DR: - raise ValueError('Data rate must be one of: 8, 16, 32, 64, 128, 250, 475, 860') - return ADS1115_CONFIG_DR[data_rate] - - def _conversion_value(self, low, high): - # Convert to 16-bit signed value. - value = ((high & 0xFF) << 8) | (low & 0xFF) - # Check for sign bit and turn into a negative value if set. - if value & 0x8000 != 0: - value -= 1 << 16 - return value - - def _read_channel(self, channel): - return self.read_adc_difference(channel) - - def _read_channel_volts(self, channel): - return self.read_volts_difference(channel) diff --git a/adafruit_ads1x15/single_ended.py b/adafruit_ads1x15/single_ended.py deleted file mode 100644 index 1318a46..0000000 --- a/adafruit_ads1x15/single_ended.py +++ /dev/null @@ -1,138 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Carter Nelson for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -""" -`adafruit_ads1x15.single_ended` -==================================================== - -Single-ended driver for ADS1015/1115 ADCs. - -* Author(s): Carter Nelson -""" - -__version__ = "0.0.0-auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ADS1x15.git" - -from .adafruit_ads1x15 import ADS1x15 -from .adafruit_ads1x15 import ADS1X15_CONFIG_MODE_SINGLE, ADS1X15_CONFIG_MODE_CONTINUOUS -from .adafruit_ads1x15 import ADS1X15_PGA_RANGE, ADS1015_CONFIG_DR, ADS1115_CONFIG_DR - -# pylint: disable=abstract-method -class ADS1x15_SingleEnded(ADS1x15): - """Base functionality for ADS1x15 analog to digital converters operating - in single ended mode.""" - - def __getitem__(self, key): - return self._channels[key] - - def read_adc(self, channel, gain=1, data_rate=None): - """Read a single ADC channel and return the ADC value as a signed integer - result. Channel must be a value within 0-3. - """ - if not 0 <= channel <= 3: - raise ValueError('Channel must be a value within 0-3!') - # Perform a single shot read and set the mux value to the channel plus - # the highest bit (bit 3) set. - return self._read(channel + 0x04, gain, data_rate, ADS1X15_CONFIG_MODE_SINGLE) - - def read_volts(self, channel, gain=1, data_rate=None): - """Read a single ADC channel and return the voltage value as a floating point - result. Channel must be a value within 0-3. - """ - if not 0 <= channel <= 3: - raise ValueError('Channel must be a value within 0-3!') - raw = self.read_adc(channel, gain, data_rate) - volts = raw * (ADS1X15_PGA_RANGE[gain] / (2**(self.bits-1) - 1)) - return volts - - def start_adc(self, channel, gain=1, data_rate=None): - """Start continuous ADC conversions on the specified channel (0-3). Will - return an initial conversion result, then call the get_last_result() - function to read the most recent conversion result. Call stop_adc() to - stop conversions. - """ - if not 0 <= channel <= 3: - raise ValueError('Channel must be a value within 0-3!') - # Start continuous reads and set the mux value to the channel plus - # the highest bit (bit 3) set. - return self._read(channel + 0x04, gain, data_rate, ADS1X15_CONFIG_MODE_CONTINUOUS) -# pylint: enable=abstract-method - -class ADS1015(ADS1x15_SingleEnded): - """ADS1015 12-bit single ended analog to digital converter instance.""" - - def __init__(self, *args, **kwargs): - super(ADS1015, self).__init__(*args, **kwargs) - self.bits = 12 - - def _data_rate_default(self): - # Default from datasheet page 19, config register DR bit default. - return 1600 - - def _data_rate_config(self, data_rate): - if data_rate not in ADS1015_CONFIG_DR: - raise ValueError('Data rate must be one of: 128, 250, 490, 920, 1600, 2400, 3300') - return ADS1015_CONFIG_DR[data_rate] - - def _conversion_value(self, low, high): - # Convert to 12-bit signed value. - value = ((high & 0xFF) << 4) | ((low & 0xFF) >> 4) - # Check for sign bit and turn into a negative value if set. - if value & 0x800 != 0: - value -= 1 << 12 - return value - - def _read_channel(self, channel): - return self.read_adc(channel) - - def _read_channel_volts(self, channel): - return self.read_volts(channel) - - -class ADS1115(ADS1x15_SingleEnded): - """ADS1115 16-bit single ended analog to digital converter instance.""" - - def __init__(self, *args, **kwargs): - super(ADS1115, self).__init__(*args, **kwargs) - self.bits = 16 - - def _data_rate_default(self): - # Default from datasheet page 16, config register DR bit default. - return 128 - - def _data_rate_config(self, data_rate): - if data_rate not in ADS1115_CONFIG_DR: - raise ValueError('Data rate must be one of: 8, 16, 32, 64, 128, 250, 475, 860') - return ADS1115_CONFIG_DR[data_rate] - - def _conversion_value(self, low, high): - # Convert to 16-bit signed value. - value = ((high & 0xFF) << 8) | (low & 0xFF) - # Check for sign bit and turn into a negative value if set. - if value & 0x8000 != 0: - value -= 1 << 16 - return value - - def _read_channel(self, channel): - return self.read_adc(channel) - - def _read_channel_volts(self, channel): - return self.read_volts(channel) diff --git a/docs/api.rst b/docs/api.rst index 0583855..0f94c7c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,11 +1,14 @@ .. If you created a package, create one automodule per module in the package. -.. automodule:: adafruit_ads1x15.adafruit_ads1x15 +.. automodule:: adafruit_ads1x15.ads1x15 :members: -.. automodule:: adafruit_ads1x15.differential +.. automodule:: adafruit_ads1x15.ads1015 :members: -.. automodule:: adafruit_ads1x15.single_ended +.. automodule:: adafruit_ads1x15.ads1115 + :members: + +.. automodule:: adafruit_ads1x15.analog_in :members: \ No newline at end of file diff --git a/docs/examples.rst b/docs/examples.rst index fb6f01b..ea05ff5 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -3,10 +3,10 @@ Simple test Ensure your device works with this simple test. -.. literalinclude:: ../examples/ads1115_single_ended_simpletest.py - :caption: examples/ads1115_single_ended_simpletest.py +.. literalinclude:: ../examples/ads1015_simpletest.py + :caption: examples/ads1015_simpletest.py :linenos: -.. literalinclude:: ../examples/ads1115_differential_simpletest.py - :caption: examples/ads1115_differential_simpletest.py +.. literalinclude:: ../examples/ads1115_simpletest.py + :caption: examples/ads1115_simpletest.py :linenos: diff --git a/examples/ads1015_simpletest.py b/examples/ads1015_simpletest.py new file mode 100644 index 0000000..27269af --- /dev/null +++ b/examples/ads1015_simpletest.py @@ -0,0 +1,23 @@ +import time +import board +import busio +import adafruit_ads1x15.ads1015 as ADS +from adafruit_ads1x15.analog_in import AnalogIn + +# Create the I2C bus +i2c = busio.I2C(board.SCL, board.SDA) + +# Create the ADC object using the I2C bus +ads = ADS.ADS1015(i2c) + +# Create single-ended input on channel 0 +chan = AnalogIn(ads, ADS.P0) + +# Create differential input between channel 0 and 1 +#chan = AnalogIn(ads, ADS.P0, ADS.P1) + +print("{:>5}\t{:>5}".format('raw', 'v')) + +while True: + print("{:>5}\t{:>5.3f}".format(chan.value, chan.voltage)) + time.sleep(0.5) diff --git a/examples/ads1115_differential_simpletest.py b/examples/ads1115_differential_simpletest.py deleted file mode 100644 index e9ff411..0000000 --- a/examples/ads1115_differential_simpletest.py +++ /dev/null @@ -1,27 +0,0 @@ -import time -import board -import busio -from adafruit_ads1x15.differential import ADS1115 - -# Create the I2C bus -i2c = busio.I2C(board.SCL, board.SDA) - -# Create the ADC object using the I2C bus -adc = ADS1115(i2c) - -# Print header -print("CHAN 0 - CHAN 1") -print("{:>5}\t{:>5}".format('raw', 'v')) - -while True: - # Get raw reading for differential input between channel 0 and 1 - raw = adc[(0, 1)].value - - # Get voltage reading for differential input between channel 0 and 1 - volts = adc[(0, 1)].volts - - # Print results - print("{:>5}\t{:>5.3f}".format(raw, volts)) - - # Sleep for a bit - time.sleep(0.5) diff --git a/examples/ads1115_simpletest.py b/examples/ads1115_simpletest.py new file mode 100644 index 0000000..3df19ff --- /dev/null +++ b/examples/ads1115_simpletest.py @@ -0,0 +1,23 @@ +import time +import board +import busio +import adafruit_ads1x15.ads1115 as ADS +from adafruit_ads1x15.analog_in import AnalogIn + +# Create the I2C bus +i2c = busio.I2C(board.SCL, board.SDA) + +# Create the ADC object using the I2C bus +ads = ADS.ADS1115(i2c) + +# Create single-ended input on channel 0 +chan = AnalogIn(ads, ADS.P0) + +# Create differential input between channel 0 and 1 +#chan = AnalogIn(ads, ADS.P0, ADS.P1) + +print("{:>5}\t{:>5}".format('raw', 'v')) + +while True: + print("{:>5}\t{:>5.3f}".format(chan.value, chan.voltage)) + time.sleep(0.5) diff --git a/examples/ads1115_single_ended_simpletest.py b/examples/ads1115_single_ended_simpletest.py deleted file mode 100644 index 8d6534a..0000000 --- a/examples/ads1115_single_ended_simpletest.py +++ /dev/null @@ -1,35 +0,0 @@ -import time -import board -import busio -from adafruit_ads1x15.single_ended import ADS1115 - -# Create the I2C bus -i2c = busio.I2C(board.SCL, board.SDA) - -# Create the ADC object using the I2C bus -adc = ADS1115(i2c) - -# Print header -print(" CHAN 0 CHAN 1 CHAN 2 CHAN 3") -print("{:>5}\t{:>5}\t{:>5}\t{:>5}\t{:>5}\t{:>5}\t{:>5}\t{:>5}" - .format('raw', 'v', 'raw', 'v', 'raw', 'v', 'raw', 'v')) - -while True: - # Get raw readings for each channel - r0 = adc[0].value - r1 = adc[1].value - r2 = adc[2].value - r3 = adc[3].value - - # Get voltage readings for each channel - v0 = adc[0].volts - v1 = adc[1].volts - v2 = adc[2].volts - v3 = adc[3].volts - - # Print results - print("{:>5}\t{:>5.3f}\t{:>5}\t{:>5.3f}\t{:>5}\t{:>5.3f}\t{:>5}\t{:>5.3f}" - .format(r0, v0, r1, v1, r2, v2, r3, v3)) - - # Sleep for a bit - time.sleep(0.5)