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!