ARDUINO ADC 1/3: TRIMMED MEAN
Here are some notes on how to properly scale and read an analog value; the Arduino code on the right is used as an example and is taken from a test sketch i had written to try out some MPXV7025 pressure sensors.
#define samples 7
#define adcchannel A0
#define del 2
#define s_offset -.298
int adc=0;
int mi=0;
int ma=0;
uint8_t cnt=0;
float trimmedmean;
void setup() {Serial.begin(115200);}
void loop() {
mi=2000;
ma=0;
trimmedmean=0
for (cnt=0; cnt<samples; cnt++){
adc=analogRead(adcchannel);
mi=min(mi,adc);
ma=max(ma,adc);
trimmedmean=trimmedmean+adc;
delay(del);}
trimmedmean=(trimmedmean-mi-ma)/(samples-2);
Serial.print(" ");
Serial.println((((trimmedmean+0.5)/1024.0)-0.5)/0.018 + s_offset ,3);
}
#define samples_no 7
#define analog_ch 0
#define readings_delay 50
#define baudrate 9600
#define ref_select INTERNAL
#define ref_voltage 1.1
#define precision 3
#define sensor1_samples_no 7
#define sensor1_ch 0
#define sensor1_delay 50
int sample_buf[samples_no];
void setup() {
analogReference(ref_select);
Serial.begin(baudrate);}
void loop() {
update_buf(sample_buf,samples_no,analog_ch);
Serial.println(read_mv(sample_buf,samples_no), precision);
delay(readings_delay);}
void update_buf(int buf[], int smpls, uint8_t ch) {
for (int cnt=0; cnt<smpls-1; cnt++) {buf[cnt]=buf[cnt+1];}
buf[smpls-1]=analogRead(ch);
}
float read_mv(int buf[], int smpls) {
float mean=0;
for (uint8_t cnt=0; cnt<smpls; cnt++) {mean=mean+sample_buf[cnt];}
mean=mean/samples_no;
return ((mean+0.5)/1024)*ref_voltage;
}
ARDUINO ADC 2/3: MOVING AVERAGE
The code on the left performs a moving average using the buffer sample_buf.
ARDUINO 3/3: EMA
On the right a function extracted from a project in which a PID is used to control the heater of a thermostated flow cell. The temperature is measured using an LM35, ADC and an EMA which weights the new input with a 1/samples_no constant and the previous average with the complimentary (samples_no-1)/samples_no weight.
The exponential moving average is of simple implementation and is also an effective filter, yielding a weighted combination of the previous output (in this case the average value of the sensor) with the last input value, the sum of the weights being 1 so that the steady state output matches the input:
out = a * out_previous + (1-a) * val
where val represents the currently measured value, or in this case the last ADC reading and a is a constant between 0 and 1 (generally around 0.8-0.99). If the function is called (and the ADC is sampled) at fixed intervals (temp_reading_interval) the constant a can be calculated once and hard coded using the following:
a = exp (-T/t)
where t is the filter time constant.
Let's assume samples_no=10 and thus a=0.9. If the function is called every 800 ms then the time constant of the filter is around 7500 ms.
void Update_temperature() {
if (millis()-temp_last_time<temp_reading_interval) {return;}
adc=((samples_no-1)*adc+analogRead(lm35_pin))/samples_no;
temp=(100*a_ref*((adc+0.5)/adc_fullscale_counts)-lm35_offset);
}