Smoothing analog data

I'm reading an expression pedal and want to check the value has changed. I've made an average of the readings, but want it to check the value has changed by more than 1 before doing anything.

I'd hoped the line

if ( (tempAnalog >= lastVal+1) || (tempAnalog <= lastVal-1) )

would do this, but it still show 126 / 127 etc

const int numReadings = 10;
int readings[numReadings];  // the readings from the analog inpu
int readIndex = 0;          // the index of the current reading
int total = 0;              // the running total
int average = 0;            // the average
int inputPin = A0;
int tempAnalog = 0;
int lastVal = 0;

void setup() {
  Serial.begin(9600);
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {    // initialize all the readings to 0:
    readings[thisReading] = 0;
  }
}

void loop() {
  total = total - readings[readIndex];    // subtract the last reading:
  readings[readIndex] = analogRead(inputPin);  // read from the sensor:
  total = total + readings[readIndex]; // add the reading to the total:
  readIndex = readIndex + 1; // advance to the next position in the array:
  if (readIndex >= numReadings) {    // if we're at the end of the array...
    readIndex = 0;     // ...wrap around to the beginning:
  }
  average = total / numReadings;   // calculate the average:
  tempAnalog = map(average, 850, 1023, 0, 127);   // Convert 10 bit to 7 bit
  tempAnalog = constrain(tempAnalog, 0, 127);
  if ( (tempAnalog >= lastVal+1) || (tempAnalog <= lastVal-1) )
  {
    Serial.println(tempAnalog);
  }
  lastVal = tempAnalog;   // Store current value to be used laters
  delay(1);  // delay in between reads for stability
}

what about taking the absolute value of the difference?, e.g.

if(abs(tempAnalog - lastVal)>1) 

consider leaky integration

int   inputPin = A0;
float average;
int   tempAnlg;
int   lastVal;

// -----------------------------------------------------------------------------
void loop ()
{
#define K   0.125
    int samp = analogRead (inputPin);
    average += (samp - average) * K;

    tempAnlg = average / 4;   // Convert 10 bit to 7 bit

#if 0
    if (abs (tempAnlg - lastVal) >= 1)
        Serial.print (tempAnlg);
#else
    Serial.print ("  "); Serial.print (samp);
    Serial.print ("  "); Serial.print (average, 2);
    if (abs (tempAnlg - lastVal) >= 1)
        Serial.print ("  "); Serial.print (tempAnlg);
    Serial.println ();
#endif

    lastVal = tempAnlg;   // Store current value to be used laters

    delay (100);  // delay in between reads for stability
}


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

Hello nickrobinson

Take a view here.

that's what I am using but even with a Pullup on the pin, odd spurious data gets through and I was concerned that increasing the average readings would make the foot-switches less sensitive and timing is vital for looping. Essentially, I want it to ignore values + or - 2 of the actual reading.

thanks - will try this & then try to understand it!

Normally people avoid using a pullup resistor on an analog input pin, as that tends to distort readings from a voltage source. Why are you doing so?

Please post the details of the pedal (product page, data sheet or wiring diagram).

Much analog data is inherently noisy, especially in the presence of relays, motors, inductors and other noise sources. They require filtering, not just averaging, to reduce the noise. First line of attack is a capacitor on the analog signal, to reduce noise before it reaches the A/D converter. Often, after that, a median filter is helpful, to eliminate any large spikes that get through the capacitor, followed by one, or more, poles of digital low-pass filtering, to smooth the resulting data. There is a good median filter library for Arduino, and a good single-pole low-pass filter can be implemented with a single line of code. With those three steps, and appropriate coefficients in the filters, most analog signals can be brought under control, with acceptable delay for most real-world applications.

The circuit seemed open to "noise" (even when I shook the enclosure) and the resistor seemed to help with that. I don't particularly need accuracy, I need stability, so when the pedal is not moving, no data is accepted. Currently there are "sweet points" where the pedal sends (for example) alternating 71/72.

The pedal is a standard TRG and the circuit part works...

If the resistor helps with "noise", that suggests a shielding problem, poor circuit design and construction, or poor connections.

This code is giving me almost what I want, but it shows all changes transmitted - if each one has a midi send attached to it, will it need a longer delay? As I say, the exact value isn't the issue. Or can I tell it to wait until the changes have stopped?

Maybe I need the analog read calling a subroutine only if changes are detected, so it focusses on monitoring the switches?

Thanks for your answers, all much appreciated.

const int numReadings = 10;
int readings[numReadings];  // the readings from the analog inpu
int readIndex = 0;          // the index of the current reading
int total = 0;              // the running total
int average = 0;            // the average
int inputPin = A0;
int tempAnalog = 0;
int lastVal = 0;

void setup() {
  Serial.begin(9600);
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {    // initialize all the readings to 0:
    readings[thisReading] = 0;
  }
}

void loop() {
  total = total - readings[readIndex];    // subtract the last reading:
  readings[readIndex] = analogRead(inputPin);  // read from the sensor:
  total = total + readings[readIndex]; // add the reading to the total:
  readIndex = readIndex + 1; // advance to the next position in the array:
  if (readIndex >= numReadings) {    // if we're at the end of the array...
    readIndex = 0;     // ...wrap around to the beginning:
  }
  average = total / numReadings;   // calculate the average:
  tempAnalog = map(average, 850, 1023, 0, 127);   // Convert 10 bit to 7 bit
  tempAnalog = constrain(tempAnalog, 0, 127);
  if ( (tempAnalog >= lastVal+2) || (tempAnalog <= lastVal-2) )
  {
    Serial.println(tempAnalog);
  }
  lastVal = tempAnalog;   // Store current value to be used laters

  delay(5);  // delay in between reads for stability
}

I'd you're looking to get absolutely stable readings, down to the units digit, good luck! The real world, and the low quality ADCs built into Arduinos, don't work that way. There will nearly ALWAYS be a few bits of dither unless you do some serious filtering.

Just averaging is not enough. You need some kind of nonlinear operation like hysteresis or median filtering (although the latter is overkill for an application like this).

A simple moving average is a waste of resources as well, consider using an exponential moving average filter instead.

But software alone won't fix poor hardware design, of course.

· Filtered analog input with hysteresis and MIDI Control Change output:
Control Surface: Control-Change-Potentiometer.ino

This looks ideal – I will try it!

Many thanks.

This looks ideal, will start again!

doesn't it depend on the characteristics of the signal and noise?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.