Using qsort to average readings

Hello @Ric, I am trying to apply your example of a “median filter” to another smaller project:
I will install an ultrasonic sensor on top of a 20.000 liter rain water tank. I want to monitor the height of the water level in order to be warned when it’s near to empty.
Below is the “Portable Range finder” sketch I made today, to test on site tomorrow.
It works perfectly but it is quite “nervous”. So, it’s probably a perfect example to try out your filtering method.

I have integrated your above code already as far as I could, between comment lines (/* … */) because it is not ready for testing yet.

Could you have a look and show how to complete it?

/**************************************************************
 * Portable Distance sensor.ino (Ultrasonic HC-SR04 sensor)
 -------------------------------------------------------------
 This sketch reads the time between sent and received pulse and converts it to a distance.
 Connections:

  Module => Particle
  VCC => Vin (+5V)
  GND => GND
  TRIG => D1
  ECHO => D2

 We use the SSD1306 Mini OLED Display.
 Connections:

  OLED=>Particle
  GND=>GND
  Vcc=>3V3
  SCL=>D1
  SDA=>D0
  No need for pull-up resistors in this case!

*/

// Libraries for use in the "Particle DEV" program:
 #include "Adafruit_GFX.h"
 #include "Adafruit_SSD1306.h"

// #include "application.h" (Is this needed? Some include this with the PING program)

 #define OLED_RESET D4 // Is this needed for I2C version? (Seems not to work without it... Test!)
Adafruit_SSD1306 display1(OLED_RESET);

// I would prefer to create it in the function, but probably I have to create it in all functions
float duration, cm;

void setup()
{
 // For the wifi ON/OFF switch
 pinMode(D7, OUTPUT);// To use the blue LED as indicator that wifi is ON/OFF
 pinMode(D3, INPUT_PULLUP);// To switch wifi ON/OFF: Connected to GND: Wifi = OFF
 // OLED Display initialization
 display1.begin(SSD1306_SWITCHCAPVCC, 0x3C);
 display1.clearDisplay();
}

void loop()
{
  // We sometimes use this portable range finder in open air: Switch wifi ON/OFF.
  if (digitalRead(D3) == HIGH)
  {
   digitalWrite(D7, HIGH); // LED ON to show wifi = ON
   WiFi.on();
   WiFi.connect();
  }
  else
  {
   digitalWrite(D7, LOW); // LED OFF, wifi OFF
   WiFi.disconnect();
   WiFi.off();
  }

 ping(D6, D5, 0);  // Trigger pin, Echo pin, delay (ms) => Now 0 to sample as fast as possible
}


void ping(pin_t trig_pin, pin_t echo_pin, uint32_t wait)
{
 static bool init = false;
 int storedData[10]; // Array to store the 10 results

 if (!init) // In order to initialize pins only once...
 {
  pinMode(trig_pin, OUTPUT);
  digitalWriteFast(trig_pin, LOW);
  pinMode(echo_pin, INPUT);
  delay(50);
  init = true;
 }

/* Comment: We want to use a "median filter" (to normalize and smoothen the output):

  // Store 10 results in an array:

  for(int i=0; i<10; i=i+1) // Record 10 measurements
  {
    digitalWriteFast(trig_pin, HIGH);
    delayMicroseconds(10);
    digitalWriteFast(trig_pin, LOW);
    duration = pulseIn(echo_pin, HIGH);
    cm = duration / 58; // Convert the time into a distance
    storedData[i]=cm;
  }

  // Sort the array of 10 distances and take the average of the center 4 values:
  qsort(storedData, 10, sizeof(float), compare); // sort to put any outliers on either end of the array
  int center = 5;
  float avg = (storedData[center -2] + storedData[center-1] + storedData[center] + storedData[center+1])/ 4.0;  // take the average of the center 4 values

  int compare (const void * a, const void * b)
  {
    float fa = *(const float*) a;
    float fb = *(const float*) b;
    return (fa > fb) - (fa < fb);
  }

End of comment */

 display();
 delay(wait);
}



void display(void) // Display the distance on the Mini OLED display: Value + bargraph.
{
  display1.clearDisplay();
  display1.setTextColor(WHITE);
  display1.setTextSize(2);
  display1.setCursor(0,0);
  display1.println("Distance:");
  display1.setTextSize(3);
  display1.setCursor(0,30);
  display1.println(cm,0);
  display1.setCursor(60,30);
  display1.println(" cm");

  display1.drawLine(0,60, cm/2,60, WHITE); // Line1
  display1.drawLine(0,61, cm/2,61, WHITE); // Line2
  display1.drawLine(0,62, cm/2,62, WHITE); // Line3
  display1.drawLine(0,63, cm/2,63, WHITE); // Line4
  display1.drawLine(0,64, cm/2,64, WHITE); // Line5

  if(cm < 150)
  {
    display1.invertDisplay(false);
  }
  else if(cm >= 150)
  {
   display1.invertDisplay(true);
  }
  display1.display();
}

Thanks!
:older_man: