Continuous average of last 10 floating numbers to get smooth serial print result

Hello to All,

I am using the attached Code for measuring the capacitance of a capacitor. The results are in floating number like “50.231 pF”. The Sketch is working fine, EXCEPT at low capacitance values (in Picco Farads) its output is varying /changing very fast.

As the measuring process of capacitance is based on the timings (charging/discharging Time of Capacitor/voltage) so I want to avoid Delay in the Sketch.

I have tried to use the Smooting code, which is given at https://www.arduino.cc/en/tutorial/smoothing , but unable to get proper output.

So I need your kind guidance regarding the code, to take the Average/Mean of around last 10 measured values of Serial.print(capacitance, 3); (a Floating Number) to get a smooth output Continuously.

Please guide me, how to take average of last floating values of ‘say last 10 or 20 serial print results’ i.e. Serial.print(capacitance, 3).

Thanks in Advance,

const int OUT_PIN = A2;
const int IN_PIN = A0;
const float IN_STRAY_CAP_TO_GND = 24.48;
const float IN_CAP_TO_GND  = IN_STRAY_CAP_TO_GND;
const float R_PULLUP = 34.8;
const int MAX_ADC_VALUE = 1023;

void setup()
{
  pinMode(OUT_PIN, OUTPUT);
  pinMode(IN_PIN, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  pinMode(IN_PIN, INPUT);
  digitalWrite(OUT_PIN, HIGH);
  int val = analogRead(IN_PIN);
  digitalWrite(OUT_PIN, LOW);

  if (val < 1000)
  {
    pinMode(IN_PIN, OUTPUT);

    float capacitance = (float)val * IN_CAP_TO_GND / (float)(MAX_ADC_VALUE - val);

    Serial.print(F("Capacitance Value = "));
    Serial.print(capacitance, 3);
    Serial.print(F(" pF ("));
    Serial.print(val);
    Serial.println(F(") "));
  }

  else
  {
    pinMode(IN_PIN, OUTPUT);
    delay(1);
    pinMode(OUT_PIN, INPUT_PULLUP);
    unsigned long u1 = micros();
    unsigned long t;
    int digVal;

    do
    {
      digVal = digitalRead(OUT_PIN);
      unsigned long u2 = micros();
      t = u2 > u1 ? u2 - u1 : u1 - u2;
    } while ((digVal < 1) && (t < 400000L));

    pinMode(OUT_PIN, INPUT);
    val = analogRead(OUT_PIN);
    digitalWrite(IN_PIN, HIGH);
    int dischargeTime = (int)(t / 1000L) * 5;
    delay(dischargeTime);
    pinMode(OUT_PIN, OUTPUT);
    digitalWrite(OUT_PIN, LOW);
    digitalWrite(IN_PIN, LOW);

    float capacitance = -(float)t / R_PULLUP / log(1.0 - (float)val / (float)MAX_ADC_VALUE);

    Serial.print(F("Capacitance Value = "));

    if (capacitance > 1000.0)
    {
      Serial.print(capacitance / 1000.0, 2);
      Serial.print(F(" uF"));

    }
    else
    {
      Serial.print(capacitance, 2);
      Serial.print(F(" nF"));
    }

    Serial.print(F(" ("));
    Serial.print(digVal == 1 ? F("Normal") : F("HighVal"));
    Serial.print(F(", t= "));
    Serial.print(t);
    Serial.print(F(" us, ADC= "));
    Serial.print(val);
    Serial.println(F(")"));
  }
  while (millis() % 1000 != 0)
    ;
}

all you have to do is store 10 values in an array, keep track where you put the last one, and to make sure you have at least the 10 values initially keep track of that, so globally declare

#define NR_CAP_VAL 10
float capVal[NR_CAP_VAL];
uint8_t capValPtr = 0;
bool fullArray = false;

then within loop

if (capValPtr == NR_CAP_VAL) {
  fullArray = true;
  capValPtr = 0;
}
capVal[capValPtr] = capacitance;
capValPtr++;

and take calculate the average

uint8_t nr_cap_val;
if (fullArray) nr_cap_val = NR_CAP_VAL;
else nr_cap_val = capValPtr;
float totalCapVal = 0;
for (uint8_t i = 0; i < nr_cap_val; i++;) {
  totalCapVal += capVal[i];
}
total = total / nr_cap_val;

don't understand why you should need to average

why do you keep reconfiguring OUT_PIN? are you relying on the pull-up resistor to provide current to the cap?

why not use a resistance of known value (measured with ohmmeter) driven from an output pin. use a button to trigger a sweep, setting the output high, measure the voltage and capture a timestamp (micros()) for each measurement until two measurements are the same. Then dump the results to the Serial monitor

@Deva_rishi - simpler/quicker to keep a running total.
Subtract the oldest reading and add the newest.
No need for a per-sample summing loop.

Show us what the prints show you.

if (val < 1000)
  {
    pinMode(IN_PIN, OUTPUT);

    float capacitance = (float)val * IN_CAP_TO_GND / (float)(MAX_ADC_VALUE - val);

    Serial.print(F("Capacitance Value = "));

what happens to the capacitance value here ?
actually it would be better if

void currentCapValue()

would return the capacitance value

float currentCapValue()

and

smoothCapValue ();

would take it as an argument, that way you can keep the variables within smoothCapValue() (static) - local.

simpler/quicker to keep a running total.
Subtract the oldest reading and add the newest.
No need for a per-sample summing loop.

absolutely true ! more efficient.

Use the Simple Kalman Filter GitHub - denyssene/SimpleKalmanFilter: A basic implementation of Kalman Filter for single variable models. to smooth the result set.

It is important to not just use any value for the p factor. Initial setup the advice to use a small number is valid but long term. Not so much.

I use micros() to determine the length of time passed between readings and update the p factor before doing the actual calculation. As the filter runs, it gets better at smoothing.

Use the serial.plotter to view the data.

Download 284k of picture, when you could have posted a few hundred bytes of text?

No thanks.