Self Calibrating Potentiometer

Update: Code edited for clarity and make sure you read pbrook’s comment

So I have a dial connected to an Arduino, a potentiometer (pot), it’s the typical item you might use for volume or brightness control, turning it all the way up to 11. Here’s the problem though, you’re reading in the analogue values of the potentiometer and you think it goes from 0 to 1023, possibly higher if it glitches a bit (though this is dependent on how it is handled in your code).

However, what you’re controlling, only goes from 0 to 10, maybe. What do you do?

Well one idea, is that you convert it to a percentage before making the comparison, I think this is effectively ‘normalising‘ the values – statistically speaking. This is really useful. So in this code I’m doing two things, one is that I am normalising the values but the other is that I am calibrating the values that I am reading.

Why should I be calibrating? Well, this code is then portable (the mathematics could apply to anything similar, as I see it), not only that but if the ‘pot’ needs replacing, then if it is placed in the ‘maximum’ value position for a while, then it’ll be accurate (well enough so).

#include "math.h"

int sensorPin = A0;
int maxVal = 0;
float sensorValue = 0.0f;
float percentile = 0.0f;
float temp = 0.0f;

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

void loop() {
 sensorValue = analogRead(sensorPin);
 percentile = (sensorValue / maxVal) * 100;
 temp = fmod(percentile,10.0);
 if (temp > 0.5)
 {
     percentile += 0.5;
 }
 if (percentile > 100.4)
 {
     maxVal++;
 }
 else
 {
     Serial.println((int)percentile);
 }
}

The code utilises the C++ Math library, and rounds the value from the pot up (I think, I should probably double check it) using the function ‘fmod‘. If the pot is set at the maximum it is capable of being at, it will increase the ‘maxVal’ variable to work out the correct 100% value to calculate with.

If anyone has feedback or corrections on this, please comment!

This entry was posted in Arduino, Projects and tagged , , , , , , . Bookmark the permalink.

13 Responses to Self Calibrating Potentiometer

  1. Paul Brook says:

    Several points, in no particular order:
    – Division by zero is bad. Don’t do it.
    – The map() function does most of what you want.
    – You can do all this without using floating point. Floating point is excruciatingly slow on an 8-bit avr.
    – It’s unclear which bits are test code, and which bits are supposed to be reusable.
    – Chances are the lower bound will be nonzero.
    – fmod() doesn’t do what you think it does.
    – The “percentile_ > 100.4” is pretty obviously bogus.
    – I’m guessing the “maxVal++” is an attempt to filter out bogon values. Unfortunately it will result in both slow bound movement (reading out of expected range) during calibration and gradual bound creep due to glitches.

    The code at https://github.com/pbrook/arduino-autoscale (mostly untested) should fix all but the last issue.

  2. Greenaum says:

    Yup, as Paul above said, a few errors there. Obviously you’re a hardware guy, not software!

    For the basic code, you just need to set the minimum value the pot returns, call it min, and the maximum, call it max.

    Then we assume the user is twiddling the pot all the way left and right a few times.

    [I’ll use = and == the same way C does, not the way maths uses it].

    Read the pot. If the value is less than min, then min=potvalue; If value > max, max=potvalue.
    After a couple of goes of that, the min and maximum values will be set. To compute the range, use max-min. Then if you want a value between 0-10, scale=range / 10;

    This is all the init code. In use, just use the pot’s answer=potinput/scale ;

    All logical, I suppose I should make the effort of hashing it into real C. Untested real C!

    
    int scale=0;
    int min=1000, max=0;
    int potvalue=0;
    int range=0, scale=0;
    
    void calibrate();
    
    main()
      {
      calibrate();
      while(1)
        {
        potvalue=(ADC1read);  // whatever the ADC read is called!
        printf("Pot is on %d \n", potvalue / scale);
        }
      }
    
    void calibrate
      {
      while(button_not_pressed) // exit condition for the loop
        {
        potvalue=(ADC1read);  // whatever the ADC read is called!
        if(potvaluemax) max=potvalue;
        }
      range=max-min;
      scale=range/10;
      }

    The exit condition in the while loop in calibrate() can be whatever you like. Perhaps set a loop that takes 12 readings, every 1/4 second, giving 3 seconds for the user to twiddle from left to right. I wrote it to leave on a button press, assuming you have a button!

    As written, with suitable changes for the actual names of the ADC and button-detect procedures, this program should stream down the screen a constant list of values between 0-10. If you change the “range / 10” bit in the code, then you can have the range go from 0-25 or any other number.

    Also as Paul said, you should use int instead of float for all of this. Microcontrollers don’t usually have an FPU, so all floating-point stuff’s done in software and takes dozens of cycles. The input from the pot, and the scaled output you’re after, are all integers anyway. Dividing with ints just ignores any fractions in results.

    Hope this works, and it’s clear enough to instruct! All the variables I think I’ve given obvious names, tho you can obviously change them to whatever suits. If you have my email address from this board, feel free to email.

    I’m only sorry I moved from Bradford or I’d pop by one day!

    • stanto says:

      “Obviously you’re a hardware guy, not software!”

      I’m not sure what’s meant by this.

      • Greenaum says:

        Because of a few bugs in your code, that’s all. But the hardware stuff’s impressive. Just joshing, didn’t mean it as an insult!

        Could I email you this bit of code I keep trying to post? It’s just not having it, due to the greater / less-than tags disappearing. Feel free to wipe the attempts I’ve made that are clogging up your guest-book.

        • stanto says:

          I don’t understand what hardware you’re referring to in relation to my capability (or lack thereof).

          As for your difficulty in using the code tags and pre tags, I wonder if there’s some wordpress setting or browser plugin you’re using that’s stopping you. Because I can put them in after you’ve posted.

  3. Greenaum says:

    Shit! Where I typed

    “if(potvaluemax) max=potvalue;”

    Replace with

    if(potvalue>max) max=potvalue;
    if(potvalue and then an entire line! Sorry bout that!

    • Greenaum says:

      Ah crap. Just figured out this server takes out less than and greater than signs! That’s why my code went wobbly! I’ll mail you it as a text file if you like! Mail me, or post here if you can’t get my address for some reason. Sorry again!

      • midnigh2ker says:

        if you wrap you code in <code> blocks, it comes out ok.

        eg:

        <code>
        my code lives here
        if a > b:
           if b < c:
                print('code!')
        </code>
        

        edit: actually, you might have better luck wrapping things in <pre></pre> tags -it keeps the space indentation =)

        • Greenaum says:
          int scale=0;
          int min=1000, max=0;
          int potvalue=0;
          int range=0, scale=0;
          
          void calibrate();
          
          main()
            {
            calibrate();
            while(1)
              {
              potvalue=(ADC1read);  // whatever the ADC read is called!
              printf("Pot is on %d \n", potvalue / scale);
              }
            }
          
          void calibrate
            {
            while(button_not_pressed) // exit condition for the loop
              {
              potvalue=(ADC1read);  // whatever the ADC read is called!
              if(potvaluemax) max=potvalue;
              }
            range=max-min;
            scale=range/10;
            }
          

          Ok, tried it with pre tags. It didn’t mention them in the bit below the text box, or I would have. Anyway here goes! Hope it’s some use!

          • Greenaum says:

            Bollocks! Ok I’ll try it with code tag


            int scale=0;
            int min=1000, max=0;
            int potvalue=0;
            int range=0, scale=0;

            void calibrate();

            main()
            {
            calibrate();
            while(1)
            {
            potvalue=(ADC1read); // whatever the ADC read is called!
            printf("Pot is on %d \n", potvalue / scale);
            }
            }

            void calibrate
            {
            while(button_not_pressed) // exit condition for the loop
            {
            potvalue=(ADC1read); // whatever the ADC read is called!
            if(potvaluemax) max=potvalue;
            }
            range=max-min;
            scale=range/10;
            }

          • midnigh2ker says:

            I’m having massive problems getting it to display the quick edit codes (as it does for me when I’m logged in).
            /me expresses vague rage at wordpress.

            -you’ve made it through the mod queue on the mailing list, though :)

  4. Greenaum says:

    I posted it to the mailing list instead. Hope it helps.

  5. Vitaliy says:

    Noob here. Hopefully you can help me with the following. I’ve been trying for a few days now to wrap my head around the difference between a rheostat and a potentiometer but to no avail… I’ve tried to read about it on Wikipedia and I even found this article with a video about rheostats https://www.derf.com/rheostat-overview-article-and-video-explanation/ but they don’t really explain the difference in layman’s terms to say the least of it. Could you please explain the difference like I’m 5?

Leave a Reply

Your email address will not be published. Required fields are marked *