|
| 1 | +# The MIT License (MIT) |
| 2 | +# |
| 3 | +# Copyright (c) 2016 Radomir Dopieralski (@deshipu), |
| 4 | +# 2017 Robert Hammelrath (@robert-hh), |
| 5 | +# 2020 STEMinds (@STEMinds) |
| 6 | +# |
| 7 | +# Permission is hereby granted, free of charge, to any person obtaining a copy |
| 8 | +# of this software and associated documentation files (the "Software"), to deal |
| 9 | +# in the Software without restriction, including without limitation the rights |
| 10 | +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 11 | +# copies of the Software, and to permit persons to whom the Software is |
| 12 | +# furnished to do so, subject to the following conditions: |
| 13 | +# |
| 14 | +# The above copyright notice and this permission notice shall be included in |
| 15 | +# all copies or substantial portions of the Software. |
| 16 | +# |
| 17 | +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 18 | +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 19 | +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 20 | +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 21 | +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 22 | +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 23 | +# THE SOFTWARE. |
| 24 | + |
| 25 | +import utime as time |
| 26 | +from eduponics import mcp23017 |
| 27 | + |
| 28 | +_REGISTER_CONVERT = const(0x00) |
| 29 | +_REGISTER_CONFIG = const(0x01) |
| 30 | +_REGISTER_LOWTHRESH = const(0x02) |
| 31 | +_REGISTER_HITHRESH = const(0x03) |
| 32 | + |
| 33 | +_OS_SINGLE = const(0x8000) # Write: Set to start a single-conversion |
| 34 | +_OS_NOTBUSY = const(0x8000) # Read: Bit=1 when no conversion is in progress |
| 35 | + |
| 36 | +_MUX_DIFF_0_1 = const(0x0000) # Differential P = AIN0, N = AIN1 (default) |
| 37 | +_MUX_DIFF_0_3 = const(0x1000) # Differential P = AIN0, N = AIN3 |
| 38 | +_MUX_DIFF_1_3 = const(0x2000) # Differential P = AIN1, N = AIN3 |
| 39 | +_MUX_DIFF_2_3 = const(0x3000) # Differential P = AIN2, N = AIN3 |
| 40 | +_MUX_SINGLE_0 = const(0x4000) # Single-ended AIN0 |
| 41 | +_MUX_SINGLE_1 = const(0x5000) # Single-ended AIN1 |
| 42 | +_MUX_SINGLE_2 = const(0x6000) # Single-ended AIN2 |
| 43 | +_MUX_SINGLE_3 = const(0x7000) # Single-ended AIN3 |
| 44 | + |
| 45 | +_PGA_6_144V = const(0x0000) # +/-6.144V range = Gain 2/3 |
| 46 | +_PGA_4_096V = const(0x0200) # +/-4.096V range = Gain 1 |
| 47 | +_PGA_2_048V = const(0x0400) # +/-2.048V range = Gain 2 (default) |
| 48 | +_PGA_1_024V = const(0x0600) # +/-1.024V range = Gain 4 |
| 49 | +_PGA_0_512V = const(0x0800) # +/-0.512V range = Gain 8 |
| 50 | +_PGA_0_256V = const(0x0A00) # +/-0.256V range = Gain 16 |
| 51 | + |
| 52 | +_MODE_CONTIN = const(0x0000) # Continuous conversion mode |
| 53 | +_MODE_SINGLE = const(0x0100) # Power-down single-shot mode (default) |
| 54 | + |
| 55 | +_DR_128SPS = const(0x0000) # 128 /8 samples per second |
| 56 | +_DR_250SPS = const(0x0020) # 250 /16 samples per second |
| 57 | +_DR_490SPS = const(0x0040) # 490 /32 samples per second |
| 58 | +_DR_920SPS = const(0x0060) # 920 /64 samples per second |
| 59 | +_DR_1600SPS = const(0x0080) # 1600/128 samples per second (default) |
| 60 | +_DR_2400SPS = const(0x00A0) # 2400/250 samples per second |
| 61 | +_DR_3300SPS = const(0x00C0) # 3300/475 samples per second |
| 62 | +_DR_860SPS = const(0x00E0) # - /860 samples per Second |
| 63 | + |
| 64 | +_CMODE_TRAD = const(0x0000) # Traditional comparator with hysteresis (default) |
| 65 | + |
| 66 | +_CPOL_ACTVLOW = const(0x0000) # ALERT/RDY pin is low when active (default) |
| 67 | + |
| 68 | +_CLAT_NONLAT = const(0x0000) # Non-latching comparator (default) |
| 69 | +_CLAT_LATCH = const(0x0004) # Latching comparator |
| 70 | + |
| 71 | +_CQUE_1CONV = const(0x0000) # Assert ALERT/RDY after one conversions |
| 72 | +# Disable the comparator and put ALERT/RDY in high state (default) |
| 73 | +_CQUE_NONE = const(0x0003) |
| 74 | + |
| 75 | +_GAINS = ( |
| 76 | + _PGA_6_144V, # 2/3x |
| 77 | + _PGA_4_096V, # 1x |
| 78 | + _PGA_2_048V, # 2x |
| 79 | + _PGA_1_024V, # 4x |
| 80 | + _PGA_0_512V, # 8x |
| 81 | + _PGA_0_256V # 16x |
| 82 | +) |
| 83 | + |
| 84 | +_GAINS_V = ( |
| 85 | + 6.144, # 2/3x |
| 86 | + 4.096, # 1x |
| 87 | + 2.048, # 2x |
| 88 | + 1.024, # 4x |
| 89 | + 0.512, # 8x |
| 90 | + 0.256 # 16x |
| 91 | +) |
| 92 | + |
| 93 | +_CHANNELS = { |
| 94 | + (0, None): _MUX_SINGLE_0, |
| 95 | + (1, None): _MUX_SINGLE_1, |
| 96 | + (2, None): _MUX_SINGLE_2, |
| 97 | + (3, None): _MUX_SINGLE_3, |
| 98 | + (0, 1): _MUX_DIFF_0_1, |
| 99 | + (0, 3): _MUX_DIFF_0_3, |
| 100 | + (1, 3): _MUX_DIFF_1_3, |
| 101 | + (2, 3): _MUX_DIFF_2_3, |
| 102 | +} |
| 103 | + |
| 104 | +_RATES = ( |
| 105 | + _DR_128SPS, # 128/8 samples per second |
| 106 | + _DR_250SPS, # 250/16 samples per second |
| 107 | + _DR_490SPS, # 490/32 samples per second |
| 108 | + _DR_920SPS, # 920/64 samples per second |
| 109 | + _DR_1600SPS, # 1600/128 samples per second (default) |
| 110 | + _DR_2400SPS, # 2400/250 samples per second |
| 111 | + _DR_3300SPS, # 3300/475 samples per second |
| 112 | + _DR_860SPS # - /860 samples per Second |
| 113 | +) |
| 114 | + |
| 115 | + |
| 116 | +class ADS1115: |
| 117 | + def __init__(self, i2c, address=0x48, gain=1, mcp_address=0x20): |
| 118 | + self.i2c = i2c |
| 119 | + self.address = address |
| 120 | + self.mcp_address = mcp_address |
| 121 | + self.gain = gain |
| 122 | + self.temp2 = bytearray(2) |
| 123 | + # define MCP for activating MOSFET pins |
| 124 | + self.mcp = mcp23017.MCP23017(i2c=self.i2c, address=mcp_address) |
| 125 | + # define all the pins for the mosfets |
| 126 | + self.mcp_pins_sheet = { |
| 127 | + 0:8, |
| 128 | + 1:9, |
| 129 | + 2:10, |
| 130 | + 3:11 |
| 131 | + } |
| 132 | + |
| 133 | + def _write_register(self, register, value): |
| 134 | + self.temp2[0] = value >> 8 |
| 135 | + self.temp2[1] = value & 0xff |
| 136 | + self.i2c.writeto_mem(self.address, register, self.temp2) |
| 137 | + |
| 138 | + def _read_register(self, register): |
| 139 | + self.i2c.readfrom_mem_into(self.address, register, self.temp2) |
| 140 | + return (self.temp2[0] << 8) | self.temp2[1] |
| 141 | + |
| 142 | + def raw_to_v(self, raw): |
| 143 | + v_p_b = _GAINS_V[self.gain] / 32767 |
| 144 | + return raw * v_p_b |
| 145 | + |
| 146 | + def set_conv(self, rate=4, channel1=0, channel2=None): |
| 147 | + """Set mode for read_rev""" |
| 148 | + self.mode = (_CQUE_NONE | _CLAT_NONLAT | |
| 149 | + _CPOL_ACTVLOW | _CMODE_TRAD | _RATES[rate] | |
| 150 | + _MODE_SINGLE | _OS_SINGLE | _GAINS[self.gain] | |
| 151 | + _CHANNELS[(channel1, channel2)]) |
| 152 | + |
| 153 | + def read_raw(self, rate=4, channel1=0, channel2=None): |
| 154 | + """Read voltage between a channel and GND. |
| 155 | + Time depends on conversion rate.""" |
| 156 | + self._write_register(_REGISTER_CONFIG, (_CQUE_NONE | _CLAT_NONLAT | |
| 157 | + _CPOL_ACTVLOW | _CMODE_TRAD | _RATES[rate] | |
| 158 | + _MODE_SINGLE | _OS_SINGLE | _GAINS[self.gain] | |
| 159 | + _CHANNELS[(channel1, channel2)])) |
| 160 | + while not self._read_register(_REGISTER_CONFIG) & _OS_NOTBUSY: |
| 161 | + time.sleep_ms(1) |
| 162 | + res = self._read_register(_REGISTER_CONVERT) |
| 163 | + return res if res < 32768 else res - 65536 |
| 164 | + |
| 165 | + def read_rev(self): |
| 166 | + """Read voltage between a channel and GND. and then start |
| 167 | + the next conversion.""" |
| 168 | + res = self._read_register(_REGISTER_CONVERT) |
| 169 | + self._write_register(_REGISTER_CONFIG, self.mode) |
| 170 | + return res if res < 32768 else res - 65536 |
| 171 | + |
| 172 | + def alert_start(self, rate=4, channel1=0, channel2=None, |
| 173 | + threshold_high=0x4000, threshold_low=0, latched=False) : |
| 174 | + """Start continuous measurement, set ALERT pin on threshold.""" |
| 175 | + self._write_register(_REGISTER_LOWTHRESH, threshold_low) |
| 176 | + self._write_register(_REGISTER_HITHRESH, threshold_high) |
| 177 | + self._write_register(_REGISTER_CONFIG, _CQUE_1CONV | |
| 178 | + _CLAT_LATCH if latched else _CLAT_NONLAT | |
| 179 | + _CPOL_ACTVLOW | _CMODE_TRAD | _RATES[rate] | |
| 180 | + _MODE_CONTIN | _GAINS[self.gain] | |
| 181 | + _CHANNELS[(channel1, channel2)]) |
| 182 | + |
| 183 | + def conversion_start(self, rate=4, channel1=0, channel2=None): |
| 184 | + """Start continuous measurement, trigger on ALERT/RDY pin.""" |
| 185 | + self._write_register(_REGISTER_LOWTHRESH, 0) |
| 186 | + self._write_register(_REGISTER_HITHRESH, 0x8000) |
| 187 | + self._write_register(_REGISTER_CONFIG, _CQUE_1CONV | _CLAT_NONLAT | |
| 188 | + _CPOL_ACTVLOW | _CMODE_TRAD | _RATES[rate] | |
| 189 | + _MODE_CONTIN | _GAINS[self.gain] | |
| 190 | + _CHANNELS[(channel1, channel2)]) |
| 191 | + |
| 192 | + def alert_read(self): |
| 193 | + """Get the last reading from the continuous measurement.""" |
| 194 | + res = self._read_register(_REGISTER_CONVERT) |
| 195 | + return res if res < 32768 else res - 65536 |
| 196 | + |
| 197 | + def read(self,pin): |
| 198 | + # activate the mosfet |
| 199 | + self.mcp.pin(self.mcp_pins_sheet[pin], mode=0, value=1) |
| 200 | + time.sleep(0.1) |
| 201 | + # read the data |
| 202 | + raw = self.read_raw(channel1=pin) |
| 203 | + voltage = self.raw_to_v(raw) |
| 204 | + # deactivate mosfet after use |
| 205 | + self.mcp.pin(self.mcp_pins_sheet[pin], mode=0, value=0) |
| 206 | + return {"raw":raw,"voltage":voltage} |
0 commit comments