More potentiometer flickering issues (new project)

Hi folks

I have a new project I’m starting and I’m having some similar issues as in a previous project. I’ve designed the circuit based on the good advice I received on the other project but I’m having similar issues.

The main difference is that I’m now using a 4-digit 7-segment display (cathode) instead of a 2-digit. It’s still being controlled by a max7219 and the LedControl library.

I’m getting flickering reading from my pot by only about 1 digit, but I’m reading out the results on the display which caused flickering on the display as well which is no good.

attached is a fritz

Hysteresis ? See here: https://forum.arduino.cc/index.php?topic=526806.0

Incidentally, digital encoders are cheap, easy to use, and usually better that a potentiometer if the use case is the entry of a digit to an arduino.

6v6gt:
Hysteresis ? See here: https://forum.arduino.cc/index.php?topic=526806.0

Incidentally, digital encoders are cheap, easy to use, and usually better that a potentiometer if the use case is the entry of a digit to an arduino.

Interesting. I'm a little confused by the implementation and modification though. By default, it's taking the normal range of 0-1023 and converting it to output 0-9.

How would I change this to output, say, 0-99, or 0-999 etc..?

How would I change this to output, say, 0-99, or 0-999 etc..?

0 to 999 is not going to work as it is only slightly more than 1 step on the A2D for each change of 1. There will always be a bit of noise and imperfect boundaries between one step and the next, expecting to reliably resolve 999 steps when there are only 1024 to play with will never be reliable.

PerryBebbington:
0 to 999 is not going to work as it is only slightly more than 1 step on the A2D for each change of 1. There will always be a bit of noise and imperfect boundaries between one step and the next, expecting to reliably resolve 999 steps when there are only 1024 to play with will never be reliable.

So, if it were 0-99, do I need to manually add each of the 100 endpoints to the array??

flyagaricus:
So, if it were 0-99, do I need to manually add each of the 100 endpoints to the array??

I've not looked at the code (did you post any code??), I just looked at 0 to 999 and realised there was no way that would work with an A2D from 0 to 1023.
0 to 99 you might be in with a chance as that's slightly over 10 steps per increment. You still need hysteresis.

So, if it were 0-99, do I need to manually add each of the 100 endpoints to the array??

What array?
Just use the map function to change the range of the pot 0 to 1023 to the range 0 to 99.

But as others have said the act of performing an analogue to digital conversion is always going to change by the least significant digit. That is just the way they work.

Grumpy_Mike:
What array?
Just use the map function to change the range of the pot 0 to 1023 to the range 0 to 99.

But as others have said the act of performing an analogue to digital conversion is always going to change by the least significant digit. That is just the way they work.

yes, but I need the output increments by 1 all the way from 0-99, not 0-10

PerryBebbington:
I've not looked at the code (did you post any code??), I just looked at 0 to 999 and realised there was no way that would work with an A2D from 0 to 1023.
0 to 99 you might be in with a chance as that's slightly over 10 steps per increment. You still need hysteresis.

What I'm trying to do is make the pot range from 0-59 (seconds, minutes) and display those on the 7-seg display.

So, if I:

potValue = map(analogRead(A0),0,1023, 0,59);

Where do I go from there?

Or should I:

potValue = map(analogRead(A0),0,1023, 0,5900);

and then add:

const uint16_t endPointInput[ numberOfLevelsOutput + 1 ] = { 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000......... } ;

up to 5900?

First off this isn't exactly something I've done so my advice is limited, someone else's advice will be better. I've no idea where you go 5900 from or why you think it will help.

Map doesn't give you hystereses, it just maps some range of numbers to another range of numbers, the boundaries are still uncertain, so I don't think map is what you need. If you don't understand hystereses then find out now as it's important for this.

I don't know an easy way to do this as I've not done it, maybe someone else does.

Me, I'd use a rotary encoder.

I'm assuming I would need to add an endpoint value for each of the number of outputs desired? So in the case of 0-59, I would need to add 60 endpoints? Or maybe I'm not understanding how this function works...

The MIDI Controller library you used in your previous project implements a combination of digital low-pass filtering and hysteresis to filter potentiometer inputs. The successor of that library improves on this by oversampling and using a higher resolution internally.

You can use the FilteredAnalog class directly in your code, as shown in this example. If you want a value in the range [0-59], you can use something like this:

#include <Arduino_Helpers.h> // https://github.com/tttapa/Arduino-Helpers
#include <AH/Hardware/FilteredAnalog.hpp>
#include <AH/Timing/MillisMicrosTimer.hpp>

// Create a filtered analog object on pin A0, with a resolution of 6 bits [0,63]:
FilteredAnalog<6> analog = A0;
 
void setup() {
  Serial.begin(115200);
  while (!Serial);
  analog.setupADC(); // Select the correct ADC resolution
}
 
void loop() {
  // Read the analog input every millisecond, and print if the value has changed
  static Timer<millis> timer = 1; // ms
  if (timer && analog.update()) {
    // Map from [0,63] to [0,59]
    unsigned scaledvalue = 60 * analog.getValue() / 64;
    Serial.println(scaledvalue);
  }
}

Pieter

You could also try this sample code derived from the one I mentioned in post #1. You don’t have to explicitly list each position endpoint.

If you want to read 60 (0. . 59) discreet values on a potentiometer attached to pin A0, simply change this line in the .ino demonstration sketch:

potA.begin( 1024, 32, 7 ) ;  // 10 bit ADC = 1024, 32 (0..31) discrete output values required, margin = 7 units (of 1024)

to

potA.begin( 1024, 60, 5 ) ;  // 10 bit ADC = 1024, 60 (0..59) discrete output values required, margin = 5 units (of 1024)

However, for a 270 degree sweep potentiometer, 60 values is close to the limit and, as I said earlier, a digital encoder may be a better solution.

HystFilter.cpp (2.12 KB)

HystFilter.h (2.18 KB)

HystFilter_V1_01.ino (1.54 KB)

flyagaricus:
yes, but I need the output increments by 1 all the way from 0-99, not 0-10

Nothing I said in my reply remotely suggested the output would be from 0 to 10?

I'm assuming I would need to add an endpoint value for each of the number of outputs desired?

Nop.

Here’s a suggestion:

uint32_t dStart, dEnd = 500;
int val, oldVal, num;
byte win = 1;
bool dir, oldDir;

void setup()
{
  Serial.begin(9600);
  
}
void loop()
{
  if (millis() - dStart > dEnd)
  {
    dStart += dEnd;
    num = constrain(analogRead(A0), 18, 1022);
    Serial.print(num); Serial.print("  ");
    val = num / 17 - 1;
    // **************** anti dither
    if (val == oldVal)
      Serial.println(val);
    else
    {  
      dir = val < oldVal;
      if (dir == oldDir)
      {
        Serial.println(val);
        oldVal = val;
      }
      else if(abs(val - oldVal) > win)
      {
        Serial.println(val);
        oldVal = val;
        oldDir = dir;
      }
      else
        Serial.println(oldVal);
    }
  }
}

PieterP:
The MIDI Controller library you used in your previous project implements a combination of digital low-pass filtering and hysteresis to filter potentiometer inputs. The successor of that library improves on this by oversampling and using a higher resolution internally.

You can use the FilteredAnalog class directly in your code, as shown in this example. If you want a value in the range [0-59], you can use something like this:

#include <Arduino_Helpers.h> // https://github.com/tttapa/Arduino-Helpers

#include <AH/Hardware/FilteredAnalog.hpp>
#include <AH/Timing/MillisMicrosTimer.hpp>

// Create a filtered analog object on pin A0, with a resolution of 6 bits [0,63]:
FilteredAnalog<6> analog = A0;

void setup() {
  Serial.begin(115200);
  while (!Serial);
  analog.setupADC(); // Select the correct ADC resolution
}

void loop() {
  // Read the analog input every millisecond, and print if the value has changed
  static Timer timer = 1; // ms
  if (timer && analog.update()) {
    // Map from [0,63] to [0,59]
    unsigned scaledvalue = 60 * analog.getValue() / 64;
    Serial.println(scaledvalue);
  }
}



Pieter

I’m sorry, I used the wring link for my previous project - I corrected it.

Grumpy_Mike:
Nothing I said in my reply remotely suggested the output would be from 0 to 10?
Nop.

I didn't say that you said that - but that's what the function does by default, adn I'm having trouble figuring out how to modify it so that it ranges from 0-59.(or any other range other than 0-9

flyagaricus:
I'm sorry, I used the wring link for my previous project - I corrected it.

The code I posted should work either way.

JCA34F:
Here’s a suggestion:

uint32_t dStart, dEnd = 500;

int val, oldVal, num;
byte win = 1;
bool dir, oldDir;

void setup()
{
  Serial.begin(9600);
 
}
void loop()
{
  if (millis() - dStart > dEnd)
  {
    dStart += dEnd;
    num = constrain(analogRead(A0), 18, 1022);
    Serial.print(num); Serial.print("  ");
    val = num / 17 - 1;
    // **************** anti dither
    if (val == oldVal)
      Serial.println(val);
    else
    { 
      dir = val < oldVal;
      if (dir == oldDir)
      {
        Serial.println(val);
        oldVal = val;
      }
      else if(abs(val - oldVal) > win)
      {
        Serial.println(val);
        oldVal = val;
        oldDir = dir;
      }
      else
        Serial.println(oldVal);
    }
  }
}

Gives the right range, but not with enough resolution. Certain numbers are almost impossible to attain.

I went with my gut and just changed the array of endpoints to match the number of levels I required(0-59) and added 100 to each so there's a buffer zone for the 'stickiness'. I'm sure this number could be anything within the stickiness range, but for the sake of clean math, 100 is just fine:

const uint16_t endPointInput[ numberOfLevelsOutput + 1 ] = { 0, 100, 200, 300, 400, 500, 600,700,800,900,1000,1100,1200,1300,1400,1500,1600,1700,1800,19,2000,2100,2200,2300,2400,2500,2600,2700,2800,2900,3000,3100,3200,3300,3400,3500,3600,3700,3800,3900,4000,4100,4200,4300,4400,4500,4600,4700,4800,4900,5000,5100,5200,5300,5400,5500,5600,5700,5800,5900,6000 } ;

and changed:

const uint16_t numberOfLevelsOutput = 60 ; 

getOutputLevel(map(analogRead(A0),0,1023,0,6000) )

Seems to work just fine. I'm sure this can be done a lot more gracefully by dynamically generating the array's values instead of manually adding them for each application.

I will however be investing in some rotary encoders :wink: