Help mapping analog value from sensor

I'm having trouble understanding how to get an analog value mapped to an actual value.

I'm testing with a MPX4250AP that I had from a previous project and this code below seems to work but it does not seem to work for
other sensors.

KPA = (analogRead(13)*(.00488) / (.02246)+20);

.00488 is 5/1024
.02246 is the range of the sensor divided by 1024 (230 kPa / 1024)
20 is the lowest kPa value

The data sheet says it has a range of 20 to 250 kPa.
http://www.freescale.com/files/sensors/doc/data_sheet/MPX4250A.pdf

I thought that I could use this code:

KPA = map(analogRead(13), 0, 1023, 20, 250);

But it seems to be off about 5% from actual values.

I can use:

VIN = .00488 * analogRead(13);

And the voltage is exactly what the mpx4250ap is putting out.

Can someone shed some light on the most accurate way to
map the incoming value to the actual value?

Looking at the graph in figure 4 of the linked datasheet I would suggest using mapping as: KPA = map(analogRead(13), 0, 1023, 0, 260);

The fact that the sensor does not continue to give linear responses below 20 or above 250 kPa does not change the fact that the response curve is shown to be a 0-5vdc = 0-260 kPa linear relationship. Try it and see if you don't get more accurate results for valid pressure inputs between 20-250 Kpa.

Lefty

Its always nice to use engineering units as values.
About the first example, you are almost right in the formula.
Here is the calc, a little bit cleaned, from my lib FloatRamp:

Out =  OutMin + ( OutMax - OutMin ) * ( float(In) - InMin ) / (InMax - InMin)  ;

Note the conversion of the input to float, just to stay accurate. All other variables are floats.

Looking at the graph in figure 4 of the linked datasheet I would suggest using mapping as: KPA = map(analogRead(13), 0, 1023, 0, 260);

That looks to give a reading about 9% low. I was going by page 2 of the datasheet that said the min was 20 and max was 250. I understand what your saying about the graph though.

Out = OutMin + ( OutMax - OutMin ) * ( float(In) - InMin ) / (InMax - InMin) ;

I tried to work this out but I guess I'm not clear on what is In and what is Out? Is In 0-1023 and out 20 to 250?

I tried this:

//Out = OutMin + ( OutMax - OutMin ) * ( float(In) - InMin ) / (InMax - InMin) ;

float RAW = analogRead();
KPA = 20 + ( 250 - 20 ) * ( RAW - 0 ) / (1023 - 0) ;

I left the zeros in just to make it easier to relate back to the formula given. The result seems to be about 5% high.

What if you try this:
20kPa = 0.204V = 41.8 as InMin.
250kpa = 4.896V = 1002.7 as InMax.
About zeros, I dont have the knowledge of Arduino to tell if it matters. I just love them. :wink:

Note that the datasheet gives you the formula:

Nominal Transfer Value: VOUT = VS (P × 0.004 - 0.04)

If Vs = 5v, and you use the default reference voltage (5v) on the analog inputs, the Vout/Vs = (P x 0.004 - 0.04).

The value you get from analogRead is equal to (Vout/Vs)*1023. (Let's call this IN).

Now just do some algebra:
IN/1023 = P * 0.004 - 0.04;
P = (IN/1023 + 0.04)/0.004;

Cleaning this up a bit:
1/0.004 = 250;
250 * 0.04 = 10;

P = 250 * (IN/1023 + 0.04)
P = 250 * IN / 1023 + 10

Be sure in your code to force this to be floating-point math:

float P;
int IN;
IN = analogRead(pin);
P = 250 * IN / 1023[glow].0[/glow] + 10; // the .0 forces floating-point math instead of integer math.

BTW:

.00488 is 5/1024

This is a common mistake because indeed a 1-bit ADC has 1024 possible values. But keep in mind:
0V = 0
5V = 1023, NOT 1024.
Granted the error is small enough that most people wouldn't notice but it is additional error in the calculations.

What if you try this:
20kPa = 0.204V = 41.8 as InMin.
250kpa = 4.896V = 1002.7 as InMax.

It's about 19% off if I did it correctly.

IN = analogRead(pin);
P = 250 * IN / 1023.0 + 10; // the .0 forces floating-point math instead of integer math.

I think that's it Dilbert! Seems to be very close, less than 1% off
in my inital tests. Simple to implement and understand too. I totally missed the formula in the datasheet.

I'll do more testing and report back.

I used the same technique with a MPX4115A. It has a similar formula, but the constants don't come out to such nice numbers.

I was just trying to work out the same thing for the MPX4115AP and the MPXV5004DP:

The formula for the:
MPXA5004DP VS * (P * .2 - .2)
MPX4115AP is VS * (P * .009 - .095)

I'm having a problem with this, can you explain that part a little more:

Cleaning this up a bit:
1/0.004 = 250;
250 * 0.04 = 10;

I may have been looking at this for too long but I'm not seeing
the relationship 1/0.004 = 250; :-?

Seems like for the MPXV5004DP it would be .784 but the pressure seems
to be off by that amount.

I was simplifying the constants a little bit.

P = (IN/1023 + 0.04)/0.004;

The divide by 0.004 is algebraically the same as multiplying by 1/0.004 which is 250. Then the 250 can be distributed across the equation so that 250 * 0.04 becomes 10. It's not necessary to do this. The equation above will work just fine too, but in the CPU, it's easier and faster to multiply than to divide.

The MXP4115's constants don't come out so nice, but the technique works the same.

P = (IN/1023.0 + 0.095) / 0.009;  // Optionally simplify constants as desired.

MPXA5004DP... left as an exercise for the reader. :wink:

P = (IN/1023 + 0.04)/0.004;

I don't think the formula works for the MPX5004AP,

Consider a reading of 1023 from the input pin:

P = (1023/1023 + .2) / .2;
P = 1.2 / .2;
P = 6; // Should be 3.92;

The formula below works but is different than your formula and I
don't understand the difference because it looks like
we should be subtracting .784 as yours does.

P = 3.92 * IN / 1023.0;
P = 3.92;

OK, I see now. The formula in the datasheet for the MPX5004 is slightly
different.

It should be P = (1023/1023 - .2) / .2;

And it works fine now. Thanks for the lesson! :slight_smile: :slight_smile: :slight_smile:

I went a little bit concerned over your statement:

It's about 19% off if I did it correctly.

because I use that formula in my working life to. Am I wrong?
But after checking the specs and consulted mr Excel, Im back on track.
Actually, my formula is just like the other, its called "straight-line equation". Its just another way of config it.
But: If you needs precision, the Vs is critical. Especially at lower pressures.
My values was at TypVs, 5.1V. MinVs gives 16,4 kPa instead of 20 at TypVs. 18%.
My suggestion is to read Vs on another pin and compensate for the diff.

That's probably what threw it off by such a high percentage. If the device is being powered by the Arduino's voltage regulator, then it is running on 5V.

BTW: It should be noted that the formulas that I gave are assuming that the Vs to the sensor, and the Vref to the ADC are equal, as is the case when using the Arduino's voltage regulator and the default reference voltage. As long as that is the case, it shouldn't be necessary to make any compensation for Vs.

The sensor is using 5v from the arduino and is actually 4.95 at the
sensor.

What is the best practice for the most accurate readings? Do I
need a separate voltage reg/circuit connected to the sensor and
AREF?

I've had a problem with the analog on mine reading 1023 not 1024... Maybe cause it wasn't 5v? Anyways I'm sure you can setup an equasion to calculate the different but hopefully some one will post.

What is the best practice for the most accurate readings? Do I
need a separate voltage reg/circuit connected to the sensor and
AREF?

It all comes to your power source for the Map.
If you trust the VS value, just calculate the corresponding pressures.
If you can spare an input, measure it.
To gain accuracy, I would personally measure instead of guessing. Actually, Im trying to decide if I want a Map or a Maf for a flow application. Its all about accuracy in my case.

IF you power the pressure sensor from the Arduino's voltage regulator AND use the "DEFAULT" voltage reference, which is the Arduino's 5v regulator, then there's no need to measure Vs.

The MXP series of pressure regulators put out a ratiometric output, not an absolute voltage/kPa. Again, look at the formula in the datasheet:

Nominal Transfer Value: VOUT = VS (P × 0.004 - 0.04)

This one is for the original poster's device. Other MXP series devices have similar formulas. But in all of them, the Vout is a percentage of the supply voltage. Rearrange the formula as:

Vout/Vs = (P x 0.004 - 0.04)

Vout/Vs ranges from 0.0 to 1.0 --- this is a percent of Vs.

Guess what, when you read any analog-to-digital converter, the digital value it gives you is the % of its reference voltage multiplied by the digital range. Ie. for 10-bit the digital range is 0...1023, so

ADC = analogRead(pin);
Then ADC = Vin/Vref * 1023 (converted to an integer).

So, when Vs = Vref, it doesn't really matter what the absolute value of Vs is. If you connect Vs to a second ADC input, the value it give you should always be 1023.

OK, I lie only a little.... if you are passing current through a long wire to your sensor, Vs at the sensor may be slightly less than Vref at the ADC.... remember that V=IR bit? But that's not likely to bite you with ~7mA supply current with 24 AWG copper wire with ~2.5ohm/100ft, that's about 2mV error for 5 ft. (I'm counting the power and ground wires.)

I think its a excellent solution íf you to do as dilbert98122 tells you.
In a tidy environment, with no risk for shortcuts or overload, the internal source always gives the right value.

I am an industrial guy, and never want to compromise my system voltage. I always use a different source for the field. But thats me. I am in a plant or in a car when I want to measure something.