Getting a number from ADS1015


#1

I have got one of these https://shop.pimoroni.com/products/adafruit-ga1a12s202-log-scale-analog-light-sensor

I am having no trouble reading the value but it is coming out as a voltage.

with

from envirophat import analog 
analog.read(0)

but is there a way to get a number like on an arduino analog pin? (between 0 and 1023)
if not how could I map the voltage to a number.
Could I change something in the library?

cheers in advance!

William


#2

This is a weird question. I’m sorry :D

What’s your use-case? As-in- why do you want to take a perfectly sane voltage reading and map it to a seemingly arbitrary numerical range?

Assuming a target range of 0-1023 (which is what you’d get from a 10bit ADC) and a source voltage of 0-5v (it rarely hits the extremes) then you can just figure out a scale value and simply multiply the value back up:

>>> scale = 1023/5.0
204.6
>>> int(analog.read(0) * scale)

But that’s a little ugly, so let’s talk about the guts of the system :D

###ADS1015

The ADS1015 on Enviro pHAT is configured with a 6.144v “full scale range”, this means its value maxes out at 6.144v, with a granularity of 3 mV.

The ADS1015 is 12bit double-ended (it can measure a positive voltage or a negative voltage) and 11bit single-ended (that 12th bit is the sign bit to denote positive or negative). So that’s a possible range of 0-2047. (6.144v / 2047 = 0.003v)

You can work out how big a number you can store in a given number of bits like so: 2^11-1 = 2047

To convert it to the 10bit value you’re after (`2^10-1 = 1023) you just shift this value down once, like so:

>>> 2047 >> 1
1023

This gives you a precision of 1 == 6mV. So a value of 500 would equal 3V.

The Code

Now we dive into the library itself. Fortunately it’s not complex code and you need only worry about lines 72 to 78 of ads1015.py: https://github.com/pimoroni/enviro-phat/blob/master/library/envirophat/ads1015.py#L72-L78

value /= 2047.0
value *= float(programmable_gain)

if value > self.max_voltage:
    self._over_voltage[channel] = True

    return round(value / 1000.0,3)

At a full scale range of 6.144v the programmable_gain value is simply 6144, the max voltage stuff is irrelevant and the over_voltage stuff is cruft you probably don’t care about, so let’s simplify this to:

value /= 2047.0 # At this point the value is 0 to 2047, this converts it to: 0.0 to 1.0
value *= 6144 # Now we scale back up our range to: 0.0 to 6144.0

return round(value / 1000.0,3) # And divide by 1000 to convert the mV value to V resulting in 0.0 to 6.144
                               #  (actually 0.0 to 5.3 since you're not supposed to put more than VDD + 0.3v into the ADC!).

# ( note, precision is also lost here because we round to 3 decimal places, 
# so the multiply up answer given above is not optimal )

So to get your 0-1023 value, you can change all of this to:

return value >> 1

And that’s a good example of a complex explanation for a simple 1-line answer :D Whew.

The Problem

If you haven’t noticed already, your 0-1023 range would now be mapped to the full-scale range of the ADC. IE: 0.0v to 6.144v, but you can only input up to 5.3v. This means the biggest number you’ll ever get out of it is about 904. If you can live with that, great, otherwise… well it’s probably just easier to use the first answer :D

Alternatively, you could scale the value slightly differently.

Given that you want 0V to 5V to equate to 0 to 1023 you could come up with a new scale factor to figure out what the 6.144v overvoltage would equal under this scheme.

>>> 6144/5000
1.2288
>>> 1023 * 1.2288
1257.0624

When you input 5v (5000mV) at a precision of 1=3mV you get: 1666.66666667

If we divide this by the full scale range, and then multiply it by our new scale factor, we get the new range:

>>> 5000/3.0
1666.66666667
>>> 1666.66666667 / 2047
0.81419964175
>>> 0.81419964175 * 1257.0624
1023.49975574

Damn it, that’s close enough! Let’s sweep some of that under the rug…

>>> int(1023.49975574)
1023

So the new code snippet becomes:

value /= 2047.0 # At this point the value is 0 to 2047, this converts it to: 0.0 to 1.0
value *= 1257.0624

return int(value)

#3

Man what an answer! The use case is just displaying it on an led bargraph which I thought would be easier to display with a number. I agree the voltage would definitely be more sane otherwise!


#4

Errr… yes… apologies for getting maybe a tad carried away ;) And good luck!