Median filter for US-100

I'm using an US-100 ultrasonic sensor for measuring water level. Whenever I did some testing with my hand or other solid objects everything in my code seemed to work correctly, yet when trying it out I would sometimes get an outlier reading that would shut down my valve (as if the set-point had been reached). I figured I could add a median filter to my code to prevent this from happening but I don't have much experience coding and I'm not really sure how to make it work.
The library I tried to use is GitHub - RobTillaart/RunningMedian: Arduino library to determine the running median by means of a circular buffer..
Below, my code:

//US-100
#include <RunningMedian.h>

RunningMedian samples = RunningMedian(10);

const int trigger_us100 = 10;
const int echo_us100 = 12;
int flag = 0;
int flag2 = 0;
int flag3 = 0;
float dist_end = 17;
int discharge_time = 32000;

void setup()
{
  Serial.begin(9600);

  pinMode(trigger_us100, OUTPUT);
  pinMode(echo_us100, INPUT);
  pinMode(11, INPUT);
  pinMode(13, INPUT);
  pinMode(5, OUTPUT);
  pinMode(7, OUTPUT);
}

void loop()
{
  float distance;
  float distance_filter;
  //digitalWrite(5,HIGH);
  digitalWrite(7, HIGH);

  distance = measure(trigger_us100, echo_us100);
  samples.add(distance);
  distance_filter = samples.getMedian();
  Serial.print("Dist_f: ");
  Serial.println(distance_filter);
  delay(10);

  int valor = digitalRead(11);  //for loading
  if (valor != 1) {
    delay(200);
    flag = 1;
  }

  if (flag == 1 && distance_filter > dist_end) {
    digitalWrite(5, LOW);
    flag3 = 1;
  }
  else {
    digitalWrite(5, HIGH);
    flag = 0;

  }

  if (distance_filter <= dist_end && flag3 == 1) { //for discharge
    flag2 = 1;
  }
  if (flag2 == 1) {
    digitalWrite(7, LOW);
    delay(discharge_time);
    digitalWrite(7, HIGH);
    flag2 = 0;
    flag3 = 0;

  }

}

float measure(int trigger, int echo) // function for US-100
{
  // Trigger inicial
  digitalWrite(trigger, LOW);
  delayMicroseconds(5);

  // 1
  digitalWrite(trigger, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigger, LOW);

  // 2
  float t = pulseIn(echo, HIGH);
  float distance = t * 0.01657;
  return distance;
}

I think I see the problem with pin 13 but that is only a guess as is the Arduino you are using and how you have it wired. Posting a labeled schematic not a frizzy picture will help us help you. Many of us do not read frizzy as it takes way to long and we can help others who post schematics even hand drawn ones. Also links to technical information on each of the hardware items. Links to sales outlets such as azon are useless.

The best way to familiarize yourself with the median filter code and how it works is to study one or more of the examples and see how they work.

It should be easy for you to modify the first example (below) and replace the analog input with readings from the distance sensor.

//
//    FILE: RunningMedian.ino
//  AUTHOR: Rob Tillaart ( kudos to Sembazuru)
// PURPOSE: demo basic usage
//    DATE: 2013-10-17
//     URL: https://github.com/RobTillaart/RunningMedian


#include <RunningMedian.h>

RunningMedian samples = RunningMedian(5);


void setup()
{
  Serial.begin(115200);
  Serial.print("Running Median Version: ");  
  Serial.println(RUNNING_MEDIAN_VERSION);
}


void loop()
{
  test1();
}


void test1()
{
  int x = analogRead(A0);  // replace this with a function call to measure()
  
  samples.add(x);
  long l = samples.getLowest();
  long m = samples.getMedian();
  long a = samples.getAverage();
  long h = samples.getHighest();
  
  Serial.print(millis());
  Serial.print("\t");  
  Serial.print(x);
  Serial.print("\t");
  Serial.print(l);
  Serial.print("\t");
  Serial.print(a);
  Serial.print("\t");
  Serial.print(m);
  Serial.print("\t");
  Serial.println(h);
  delay(100);
}

The median filter is really simple. It merely sorts the values and takes the middle one (if you choose an odd number of samples).

I did read it the first time, and thought I only needed these lines for it to work

#include <RunningMedian.h>
RunningMedian samples = RunningMedian(5)
int x = analogRead(A0)
samples.add(x);

Should I also use a function like test1 in order for the filter to run?

Pin 13 is empty, I planned on using it to connect a button but discarded that idea along the way. As I mentioned, I'm just missing a way to filter outliers from the sensor readings.

Slow your sampling down a bit.

Do you think that might be the source of the outliers? I fear that if I slow sampling down, the velocity of the rising water level might cause large deviation from the set-point, tho I admit I might be over worrying.

No, those lines are not sufficient to see the operation. You do need to have code similar to what is in the test1() function. At the very least you also need to calculate and display the median value (after a number of samples have been collected):

  long m = samples.getMedian();
  Serial.println(m);

Thx fren. Do you have any idea why, in the example, x is declared as int, but lowest, median, average and highest are declared as long? Would using long tipe variables mess up with other operations?

It could be causing the outliers.

Besides, if you slow things down say to every 500ms and you need it faster, try 100ms / 50ms / 10ms / 1ms . . .

It’s easy to do and it’s FREE to do !

Thx dude, appreciate it!

Not tested but something like this:

//US-100
#include <RunningMedian.h>

RunningMedian samples        = RunningMedian(10);

const int trigger_us100      = 10;
const int echo_us100         = 12;
int flag                     = 0;
int flag2                    = 0;
int flag3                    = 0;
float dist_end               = 17;
int discharge_time           = 32000;

//timing stuff
unsigned long lastMillis;


//********************************************^************************************************
void setup()
{
  Serial.begin(9600);

  pinMode(trigger_us100, OUTPUT);
  pinMode(echo_us100, INPUT);
  pinMode(11, INPUT);
  pinMode(13, INPUT);
  pinMode(5, OUTPUT);
  pinMode(7, OUTPUT);

} //END of   setup()

//********************************************^************************************************
void loop()
{
  float distance;
  float distance_filter;

  //digitalWrite(5,HIGH);
  digitalWrite(7, HIGH);

  //********************************
  //tune this TIMER by changing the interval
  //has the TIMER expired ?
  if (millis() - lastMillis >= 500ul)  // <-----<<<< change 500ul to whatever
  {
    //restart this TIMER
    lastMillis = millis();

    //get a reading
    getReading();
  }

  //********************************
  int valor = digitalRead(11);  //for loading

  if (valor != 1)
  {
    flag = 1;

    delay(200);
  }

  //********************************
  if (flag == 1 && distance_filter > dist_end)
  {
    flag3 = 1;

    digitalWrite(5, LOW);
  }

  else
  {
    flag = 0;

    digitalWrite(5, HIGH);
  }

  //********************************
  if (distance_filter <= dist_end && flag3 == 1)   //for discharge
  {
    flag2 = 1;
  }

  //********************************
  if (flag2 == 1)
  {
    digitalWrite(7, LOW);

    delay(discharge_time);

    digitalWrite(7, HIGH);

    flag2 = 0;
    flag3 = 0;
  }

} //END of   loop()


//********************************************^************************************************
float measure(int trigger, int echo) // function for US-100
{
  // Trigger inicial
  //  digitalWrite(trigger, LOW);
  //  delayMicroseconds(5);

  // 1
  digitalWrite(trigger, HIGH);
  
  delayMicroseconds(10);
  
  digitalWrite(trigger, LOW);

  // 2
  float t = pulseIn(echo, HIGH);
  
  float distance = t * 0.01657;
  
  return distance;

} //END of   measure()


//********************************************^************************************************
void getReading()
{
  distance = measure(trigger_us100, echo_us100);

  samples.add(distance);
  distance_filter = samples.getMedian();
  
  Serial.print("Dist_f: ");
  Serial.println(distance_filter);

} //END of  getReading()

No, the long declaration makes little to no difference, but will work best if only integer data are collected.

The running median library code is actually set up for and expects float values on both input and output, which is more useful for ranging examples.

I will try this out if I still dont get it right. Thx a lot, this is easier to read!