Needing better averaging code for pressure sensor

I am using a MPX5100 to read pressure on a needle valve. Two air pumps(pulsing type) provide the air needed for the back pressure reading I am using a filter network on the pressure sensor and this averaging code and works pretty good but seems way to slow. It also leaver a remainder which I wish it didn’t. The hardest thing is to overcome the pulsing on the pressure sensor because of the pulsing pumps.

// New code for averaging - On each pass, it subtracts 1/8 of the total rawAccumulator value, then adds in the current rawValue.
  // So over time, the rawAccumulator value will converge on 8x the average raw value.
  // Then it takes 1/8 of the rawAccumulator value as the filteredValue.
  rawAccumulator -= (rawAccumulator >> 3); // new average code. Can change 3 to 2 for less or 3 to 4 for more averaging.
  rawAccumulator += rawValue; // new average code
  filteredValue = rawAccumulator >> 3; // new average code. Can change 3 to 2 for less or 3 to 4 for more averaging.

Flow Meter 640x480.jpg

So are you trying to make it faster? not have a remainder?

Is that a software low pass filter ? Such a filter works better with 'float' variables, because then no bits will get lost.

I suggest to start with the average of a number of samples with analogRead(). For example the average of 10 or 100 analogRead().

Is that an Arduino Nano ? The sensor is ratiometric, that means you have to power it with the 5V pin of the Arduino board. Can you confirm that you have that ? Can you measure the voltage of the 5V pin of the Arduino ?

Are those two 6V pumps for blood pressure meters ? You have them in series to a 12V power supply ? A DC motor does not have a steady current, that means they influence each other when used in series. I don't like that.

When the pump is running, the vibration from the pump travels through the air inside the tubes to the pressure sensor. Will the tube be longer for the final project ? The longer the tubes, the less air vibration. You could even add a damper chamber.

What kind of hardware filter do you use ?

I think that a hardware filter or damper chamber is not needed. Noisy measurements can be eliminated with the average of a number of samples and pump vibrations can be filtered with a moving average. Depending on the number of samples per second and on the window of the moving average, that can be faster than a software low pass filter.

I see a Nano on USB supply (VCC = ~4.6volt). But also a pot and a display. If you don't want us to guess, then it's wise to show us the full circuit diagram and the full code.

Koepel: I suggest to start with the average of a number of samples with analogRead().

Agreed. Just averaging a bunch of consecutive samples that bridge the time between one pump ripple cycle could be enough. Leo..

I see two pumps, the means pump ripple interference :o
When the pumps are indeed in series, they electrically influence each other an cause even more air ripple :o :o
The air ripple of those pumps is not very bad. Perhaps there is something else going on.

ScoobyDoo: So are you trying to make it faster? not have a remainder?

Yes make it faster and no remainder.

Koepel: Is that a software low pass filter ? Such a filter works better with 'float' variables, because then no bits will get lost.

I suggest to start with the average of a number of samples with analogRead(). For example the average of 10 or 100 analogRead().

Is that an Arduino Nano ? The sensor is ratiometric, that means you have to power it with the 5V pin of the Arduino board. Can you confirm that you have that ? Can you measure the voltage of the 5V pin of the Arduino ?

Are those two 6V pumps for blood pressure meters ? You have them in series to a 12V power supply ? A DC motor does not have a steady current, that means they influence each other when used in series. I don't like that.

When the pump is running, the vibration from the pump travels through the air inside the tubes to the pressure sensor. Will the tube be longer for the final project ? The longer the tubes, the less air vibration. You could even add a damper chamber.

What kind of hardware filter do you use ?

I think that a hardware filter or damper chamber is not needed. Noisy measurements can be eliminated with the average of a number of samples and pump vibrations can be filtered with a moving average. Depending on the number of samples per second and on the window of the moving average, that can be faster than a software low pass filter.

Yes I am using a nano and yes they are blood pump mini air pumps in parallel running on 9 volts. The pressure sensor is getting 5 volts from the nano. The hardware filter is a 3.3k resistor and a 1uf capacitor.

Koepel: I see two pumps, the means pump ripple interference :o When the pumps are indeed in series, they electrically influence each other an cause even more air ripple :o :o The air ripple of those pumps is not very bad. Perhaps there is something else going on.

No it is the pumps causing the fluctuation and the code I am using is ok but not very fast on the reading and seems it has to settle in a bit for the reading to stablise.

Here is the code and schematic. The code also includes a lot of weather calculations and I am displaying temperature and relative air density as well as the flow pressure.

#include "U8glib.h" //LCD 128x64 ST7920 Display Library
#include "SparkFunBME280.h" // BME280 Library
#include "Wire.h" // I2C Library For Sensor

// Flow Meter Set Integers Start
//int rawValue; // A/D readings
int offset = 46; // zero flow pressure adjust (~30-72)
int fullScale = 819; // max pressure adjust (~798-840)
float flowpressure; // final pressure in mmHg
int battery;
int pressurezero;
uint32_t rawValue; // new average code
int rawAccumulator; // new average code
int filteredValue; // new average code
// Flow Meter Set Integers End

// Weather Integers Start
BME280 Sensor;
float kelvin = 0;
float dewptk = 0;
float dewptc = 0;
float dewptf = 0;
float baromb; //pressure millibar
float baropascal; //pressure in Pascals
float gconstdry = 287.058; //gas constant for dry air
float gconstwaterv = 461.495; //gas constant, water vapor
double psatmb; //pressure saturated millibar
double psatnum; //pressure saturated numerator
double psatden; //pressure saturated denominator
double psatfract; //pressure saturated fraction
double psatexpo; //exponent for pressure saturated
double pvapwater; //pressure, water vapor component
double pdryair; //pressure, dry air
double psatpascals; //pressure saturated in Pascals
double phumidA; //part A of equation
double phumidB; //part B of equation
double rho; //variable for air density in Kg/m3
int analogPin = 2; //
int val = 0; //initialize value to 0
float pottopress; //from (pot*0.0017578)+29.92
float pressalt; //from (SLP -Baro) *994
float degc;
float degf;
float pressure;
float h;
float inhg;
double rad; // relative air density(rad) = 100 * (current air density) / (reference air density) standard automotive reference 1.1568 kg/m3
// Weather Integers End

// constructor call for display
U8GLIB_ST7920_128X64 u8g(13, 11, 10, U8G_PIN_NONE); // SPI Com: SCK = en = 13, MOSI = rw = 11, CS = di = 0

void draw(void)
{
 // graphic commands to redraw the complete screen should be placed here
}


void setup(void)
{
 u8g.setRot180(); // flip screen / rotated for current display
 // set SPI backup if required / not required with current display
 //u8g.setHardwareBackup(u8g_backup_avr_spi);

 //***Set up sensor******************************//
 //commInterface I2C_MODE
 Sensor.settings.commInterface = I2C_MODE;
 Sensor.settings.I2CAddress = 0x76;
 Sensor.settings.runMode = 3; //  3, Normal mode
 Sensor.settings.tStandby = 0; //  0, 0.5ms
 Sensor.settings.filter = 0; //  0, filter off
 //tempOverSample can be:
 //  0, skipped
 //  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
 Sensor.settings.tempOverSample = 1;
 //pressOverSample can be:
 //  0, skipped
 //  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
 Sensor.settings.pressOverSample = 1;
 //humidOverSample can be:
 //  0, skipped
 //  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
 Sensor.settings.humidOverSample = 1;

 //***Initialize Sensor**************************//
 Serial.begin(57600);
 Serial.print("Program Started\n");
 Serial.println("Starting BME280... result of .begin():");
 delay(10);  //Make sure sensor had enough time to turn on. BME280 requires 2ms to start up.
 //Calling .begin() causes the settings to be loaded
 Serial.print("Sensor: 0x");
 Serial.println(Sensor.begin(), HEX);
}

void loop(void)
{
 //pressurezero = analogRead(A2) / 8;
 rawValue = analogRead(A0);
 flowpressure = (filteredValue - offset) * 650.0 / (fullScale - offset); // offset or pressurezero for trimmer pot Replaced rawValue with filteredValue. 650.0 for calibrated pressure conversion
 battery = analogRead(A1) / 8.9; // Display battery percentage at max 100%

 if (flowpressure < 0) // If pressure is below zero have display show zero
 {
   flowpressure = 0;
 }

 if (battery > 100) // If battery tries to display over 100% have display show 100%
 {
   battery = 100;
 }

 // New code for averaging - On each pass, it subtracts 1/8 of the total rawAccumulator value, then adds in the current rawValue.
 // So over time, the rawAccumulator value will converge on 8x the average raw value.
 // Then it takes 1/8 of the rawAccumulator value as the filteredValue.
 rawAccumulator -= (rawAccumulator >> 3); // new average code. Can change 3 to 2 for less or 3 to 4 for more averaging.
 rawAccumulator += rawValue; // new average code
 filteredValue = rawAccumulator >> 3; // new average code. Can change 3 to 2 for less or 3 to 4 for more averaging.

 val = analogRead(analogPin);
 pottopress = (val * 0.0017578) + 29.92; //"Kollsman Window" value calculation

 //Start with temperature, as that data is needed for accurate compensation.
 //Reading the temperature updates the compensators of the other functions
 //in the background.
 Serial.print("***************** DIRECTLY FROM SENSOR START *********************");
 Serial.println("");
 Serial.print("Temperature: ");
 Serial.print(Sensor.readTempC(), 2);
 Serial.print(", ");
 Serial.println("");

 Serial.print("Temperature: ");
 Serial.print(Sensor.readTempF(), 2);
 Serial.print(", ");
 Serial.println("");

 Serial.print("Pressure: ");
 Serial.print(Sensor.readFloatPressure(), 2);
 Serial.print(", ");
 Serial.println("");

 Serial.print("Altitude: ");
 Serial.print(Sensor.readFloatAltitudeMeters(), 2);
 Serial.print(", ");
 Serial.println("");

 Serial.print("Altitude: ");
 Serial.print(Sensor.readFloatAltitudeFeet(), 2);
 Serial.print(", ");
 Serial.println("");

 Serial.print("%RH: ");
 Serial.print(Sensor.readFloatHumidity(), 2);
 Serial.print(", ");
 Serial.println();
 Serial.print("***************** DIRECTLY FROM SENSOR END *********************");
 Serial.println("");

 //read and calibrate TempF from TempC
 float degc = Sensor.readTempC() - 1.0; // read and calibrate temp c
 float degf = Sensor.readTempF() - 2.5; // read and calibrate temp f
 //degf = (degc * 1.8) + 32-1; // was calculated off of Celcius, but to much variation with Faranheit after calculation.
 float h = Sensor.readFloatHumidity() - 2; // read and calibrate humidity
 float pressure = Sensor.readFloatPressure();
 inhg = pressure / 3386;
 baromb = inhg * 33.86;
 baropascal = inhg * 3386;
 kelvin = degc + 273.15;
 dewptk = kelvin - ((100 - h) / 5);
 dewptc = dewptk - 273.15;
 dewptf = ((dewptc * 1.8) + 32);
 pressalt = (pottopress - inhg) * 994; //calculate feet
 psatnum = 7.5 * degc;          //calculate the numerator of the psat exponent
 psatfract = psatnum / psatden; //calculate the psat exponent fraction
 psatden = degc + 237.3;        //calculate the denominator of the psat exponent psatfract = psatnum / psatden;  //calculate the psat exponent fraction
 psatexpo = pow(10, psatfract);   //calculate exponent for psat
 psatmb = 6.1087 * psatexpo;      //calculate psat in millibars
 psatpascals = psatmb * 100;    //convert psat millibars (hectopascals) to pascals
 pvapwater = (h / 100 * psatmb) * 100; //calculate water vapor pressure component
 pdryair = baropascal - pvapwater;    //calculate partial pressure dry air component
 phumidB = pdryair / (gconstdry * kelvin);  //part B of the pressure humid air calculation
 phumidA = pvapwater / (gconstwaterv * kelvin);  //part A of humid air pressure
 rho = phumidA + phumidB;  //calculate RHO in Kg/m3!
 rad = (rho / 1.225) * 100;

 // picture loop
 u8g.firstPage();

 do
 {
   draw();

   {
     u8g.setFont(u8g_font_unifontr); //Set font
     u8g.drawStr( 0, 10, "FLOW");

     u8g.drawStr( 80, 10, "TEMP'C");
     u8g.setPrintPos(85, 22);
     u8g.print(degc, 1);
     //u8g.drawStr( 100, 22, "'F");

     u8g.drawStr( 80, 37, "RAD %");
     u8g.setPrintPos(85, 50);
     u8g.print(rad, 1);
     //u8g.drawStr( 105, 50, "%");

     u8g.setFont(u8g_font_fub30r); //Set font
     u8g.setPrintPos(0, 48);
     u8g.print(flowpressure, 0);

     u8g.setFont(u8g_font_unifontr); //Set font
     u8g.drawStr( 18, 64, "BATTERY %");
     u8g.setPrintPos(89, 64);
     u8g.print(battery);
   }
   else
   {
     u8g.setFont(u8g_font_unifontr); //Set font
     u8g.drawStr( 7, 38, "CHARGE BATTERY");
   }
 }
 while ( u8g.nextPage() );

 // rebuild the picture after some delay
 delay(200);
}

[/code]

The 3k3 with 1µF is a 48 Hz filter. When that is a electrolytic capacitor, it might leak. Are the pump 12V and used in parallel at 9V ? That is okay. Is it possible that the (ground) current of the pumps might influence the sensor ?

I would remove the RC filter and try an other filter in software. But every filter will make it slower. When that does not help, perhaps a damper chamber might help.

A pressure sensor is ratiometric, meaning it's output is a ratio of the power supply. If the power supply of the Nano varies (USB ripple, etc.), then the output of the sensor also varies, and Aref of the A/D varies. But all those variations cancel each other out , so the returned A/D value stays the same.

Unless you stop only one of those variations (with a capacitor/filter). Then the variations are not cancelled out anymore.

So the R3/C5 filter you added here is a bad thing, that makes A/D variations worse. Leo..

Why does it look better on the oscilloscope? It also helps with some of the fluctuation of the readings.

I can post scope pictures tomorrow when I have time.

Is it possible to invert the pulsulting off of the pressure sensor then mix that signal back in with the original for 180 degrees out of phase and cancel the pulsating, or will it cancel out the voltage from the sensor?

KevinRoach:
Why does it look better on the oscilloscope?

A scope looks at voltage, not at ratio.
A smoother scope voltage is therefore useless.

Better to take a bunch of readings for averaging.
unsigned int total = 0; // reset
for (int x = 0; x < 64; x++) total += analogRead(A0);
Leo…

Thanks Wawa. When I wrote "I would remove the RC filter" it was because the RC filter didn't feel right. But with a ratiometric sensor, the RC filter works against the ratiometric match between the sensor and the Arduino. Thanks.

KevinRoach, don't try a electronic solution. The sensor should be connected directly to the Arduino for best results. Also connect all three wires of the pressure sensor directly to the Arduino board. Do not, for example, connect the ground wire to other ground wires.

You can do a lot in software, but I have already mentioned that a few times.

The damper chamber that I mentioned will really help a lot. For example a plastic jar of 0.5 liter with a screw cap. Fill it with polyester fibers that can be found in stuffed animals. Make sure the inlet and outlet are sturdy and don't leak.

KevinRoach: No it is the pumps causing the fluctuation and the code I am using is ok but not very fast on the reading and seems it has to settle in a bit for the reading to stablise.

Sounds like you need a Bessel digital filter, has the best settling properties (low phase distortion, no overshoot or ringing).

Try the York uni app: https://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html - it generates C code for you

Wawa:
A scope looks at voltage, not at ratio.
A smoother scope voltage is therefore useless.

Better to take a bunch of readings for averaging.
unsigned int total = 0; // reset
for (int x = 0; x < 64; x++) total += analogRead(A0);
Leo…

WAWA, How do I implement the averaging code into mine and do away with the other?

Guys! the low pass filter I got from the spec sheet on the MXP5100DP and just changed the values a little to go with the frequency I was getting on the scope from the pumps pulsing. They also suggest possibly using a rail to rail buffer.

AN1646.pdf (256 KB)

Manufacturer page of the MXP5100: https://www.nxp.com/products/sensors/pressure-sensors/differential-gauge-up-to-115-kpa/0-to-100kpa-differential-gauge-and-absolute-integrated-pressure-sensor:MPX5100.

Datasheet MXP5100DP: https://www.nxp.com/docs/en/data-sheet/MPX5100.pdf. In the datasheet a 470 pF is mentioned to reduce the noise of the output signal.

In that datasheet, the AN1646 application note is mentioned: https://www.nxp.com/docs/en/application-note/AN1646.pdf.

AN1646 is about noise considerations with these sensors. It is only about the noise that the sensor creates itself and electronic noise from the circuit or power supply. It is not about reducing the noise from changes in the measured pressure.

The recommendation is a RC filter with 750 Ω and 0.33 µF. There is also a section about the power supply and they recommend a linear regulator like the 7805.

The Arduino Nano has a diode when power via USB, that will change the VCC and therefor the RC filter is not a good idea.

KevinRoach, the pressure sensor measures the pressure. If the pressure changes, and the sensor measures that, then the sensor is working well. The datasheet and application note do not mention a situation when the pressure changes. The application note also assumes that the 5V is good and is a steady 5.0 V, but the Arduino Nano does not have a steady 5.0 V. The 470 pF mentioned in the datasheet can be used and will reduce noise from the sensor. However, since you need a filter in software anyway, the 470 pF is not needed.

Why is it not a good stable 5 volts?

What part# 5v regulator are you referring. According to the spec sheet I looked at it is a high efficiency 5v regulator.

The Arduino Nano uses a diode from the USB 5V to the VCC for the microcontroller. The 5V pin is therefor lower than 5.0 V if you use the USB cable to power a Arduino Nano. Is the display with backlight also powered from the USB cable ? then you have more voltage drop in the USB cable.

I only use the USB for programming. The circuit either runs from battery or a power supply. The 9 volt regulator runs the pumps and the nsno. It is a 7809, so in retrospect the nano is very well regulated and in turn should be a great 5volts regulated as well as good 3.3v.

I don't really have a power issue, it is just the pulses from the pumps which in reality does offer theoretical noise in what the senser sees. Anything other than a constant pressure could be consider noise in my opinion. That is why the low pass filter does work. No matter what anyone says it's proven on the circuit and on the scope.

The pulses from the pumps cause the voltage out from the pressor sensor to waver, voltage up and down a bit, but the low pass filter takes most of that out. I was will to post it but nobody was interested.

Sometimes theory and what is on paper isn't always the fix. Sometimes introducing issues like this requires looking outside the box. I am not doubting anyone but the proof is in the operation.

Right now even though it is a little slow the accuracy is within 1 mmhg. That is way better then what the tolerance is on the pressure sensor. It is just a little slow with the averaging code I have and it leaves a remainder.