Introduction
This code snippet contains some C/C++ code and a C++ class that can be used to read a value from a potentiometer. Furthermore it shows some examples on how to use the code.
Potentiometer code snippet(s)
I will start approaching this code snippet from the view of the user who wants to apply it in his main loop. I’m assuming an arduino nano setup and will write everything following the standard setup(){ ... }
and loop() { ... }
framework.
Furthermore I’m assuming we use the arduino IDE.
Let’s start with a simple version of the code.
// example program 1
// Include libprintf which makes printing to the serial port SOOOO much easier.
#include <LibPrintf.h>
// Include the potentiometer class
#include "Potentiometer.hpp"
// Here are some constants and global variables (beware of the latter!).
#define ANA_IN_PIN A0
#define SERIAL_SPEED 312500
Potentiometer pot;
void setup() {
Serial.begin(SERIAL_SPEED);
// We instantiate the potentiometer and link it to an analog input.
pot = Potentiometer(ANA_IN_PIN);
}
void loop() {
pot.tick();
if (pot.hasChanged()) {
printf("current value: %d\n", pot.getValue());
}
}
What does this all mean?
We start the serial port and give it a certain baud rate. You will need to set the output monitor window of the arduino IDE to the same speed.
The pot must be accessible in the setup()
and in the loop()
subroutine, therefore it must be defined outside of them: ‘Potentiometer pot;’. But before we do we include the file Potentiometer.hpp because that contains the pot’s class definition.
In the setup(){}
we instantiate the pot and link it to the analog input pin.
In the main program we can now read the pot’s value, but in order to do so we use a ‘tick’ function. This function should be called as often as makes sense. The pot is not likely to change its value more than a few times a second. I’ll get back to this later. Note, I have found that several libraries that deal with input devices use a tick() or similar function. They inspired me to use one in this code.
If the pot has changed, we want to know its value and in this case print the new value.
The loop() will then restart, tick the pot and print something if it has changed. If the pot has remained the same, nothing will be printed.
I’ve not measured this but depending on the speed of the arduino you are using, the loop will cylce tens of thousands of times per second. If you do not like the idea that the pot is read that often, you could use a delay between successive ticks. There are at least 2 methods of implementing a delay.
Method one: use delay(number_of_milliseconds)
Here a 100 milli seconds delay is added after the commands, which allows for 10 changes to be detected per second at max.
void loop() {
pot.tick();
if (pot.hasChanged()) {
printf("current value: %d\n", pot.getValue());
}
delay(100);
}
This works well, but using delay has one drawback that may not be immediately obvious here. delay() will stop the CPU from doing anything during the delay period. So if you want other stuff to be done as well, this also by consequence is stopped for the duration of the delay time. Have a look at this example of using delay and some other function.
void doSomeOtherStuff() {
printf("I'm doing other stuff now\n");
}
void loop() {
int value = 0;
pot.tick();
if (pot.hasChanged()) {
value = pot.getValue();
printf("current value: %d\n", value);
}
delay(100);
doSomeOtherStuff();
}
Method two: use a counter
Increment the static counter each loop and whenever this reaches a large number, check the pot, otherwise skip checking the pot and continue with the rest of the code.
#define DELAY_CNT 1000000
void doSomeOtherStuff() {
printf("I'm doing other stuff now\n");
}
void loop() {
static int cnt = 0;
int value = 0;
if ((cnt % DELAY_CNT) == 0) {
pot.tick();
if (pot.hasChanged()) {
value = pot.getValue();
printf("current value: %d\n", value);
}
}
cnt += 1;
doSomeOtherStuff();
}
The static counter cnt will keep its value whenever the loop is repeated, so we can use this to count to a large number before we tick the pot. Because we initialize cnt to 0 the pot is checked right away because (0 % DELAY_CNT) will evaluate to 0. If you do not want this to happen on the first run, simply initialize cnt to 1. Then it will take (DELAY_CNT - 1) loops before the pot is ticked. The code ((cnt % DELAY_CNT) == 0) does a so called modulo division. (cnt % DELAY_CNT) will return 0 whenever cnt is a multiple of DELAY_CNT and otherwise it will return cnt. The code ((cnt % DELAY_CNT) == 0) does a so called modulo division.
Because doSomeOtherStuff() is outside of the if-statement that deals with the value of cnt, this subroutine is called every time the loop repeats while the pot is only checked every DELAY_CNT times the loop restarts.
To recap, here is the whole program once more:
// example program 2
// Include libprintf which makes printing to the serial port SOOOO much easier.
#include <LibPrintf.h>
// Include the potentiometer class
#include "Potentiometer.hpp"
// Here are some constants and global variables (beware of the latter!).
#define ANA_IN_PIN A0
#define SERIAL_SPEED 312500
#define DELAY_CNT 1000000
Potentiometer pot;
void doSomeOtherStuff() {
printf("I'm doing other stuff now\n");
}
void setup() {
Serial.begin(SERIAL_SPEED);
// Instantiate the potentiometer and link it to an analog input.
pot = Potentiometer(ANA_IN_PIN);
}
void loop() {
static int cnt = 0;
int value = 0;
if ((cnt % DELAY_CNT) == 0) {
pot.tick();
if (pot.hasChanged()) {
value = pot.getValue();
printf("current value: %d\n", value);
}
}
cnt += 1;
doSomeOtherStuff();
}
If you run the loop containing the delay and compare that to the loop containing the cnt, you will see that the former will run the doSomeOtherStuff() much less frequently than the latter. You will find a usable value for DELAY_CNT by experimenting with a few values.
Let’s have a look at the Potentiometer class now. This bundles all the functions we need for the potentiometer in one place. This is the content of the file Potentiometer.hpp
#ifndef _POTENTIOMETER
#define _POTENTIOMETER
class Potentiometer {
private:
int pinNr;
int currentPotValue = 0;
int oldPotValue = -1;
int errorValue = 5;
bool valueHasChanged = false;
public:
Potentiometer() {};
Potentiometer(int pin_nr) {
pinNr = pin_nr;
}
void tick() {
// Read and update pot value,
int newPotValue = analogRead(pinNr);
if (abs((oldPotValue - newPotValue)) >= errorValue) {
currentPotValue = newPotValue;
oldPotValue = newPotValue;
valueHasChanged = true;
}
}
bool hasChanged() { // Has the value changed since checked last time?
bool returnValue = valueHasChanged;
valueHasChanged = false;
return(returnValue);
}
int getValue() { // Get the current value of the potentiometer.
return(currentPotValue);
}
};
#endif
The nice thing about using a class is, that it bundles the whole functionality of the potentiometer in one place. Whenever we need to change something about the potentiometer, we can do this in the class and often leave the main program parts setup() and loop() untouched. Furthermore there are no variables in the loop() and setup() that could implicitely affect the potentiometer once it is initialized. So we do not need to worry about side effects which cause programs to misbehave. Global variables (they are the PITS!) often are the source of problems. The only thing we need to remember is to tick the pot now and then.
Especially when using external devices like ADC or analog switches or multiplexers etc in my programs, I’ve found that doing that part of the programming the C++ way (making a class for them) makes the software more modular, more readable and easier to debug. And writing general classes makes it easier to reuse code you built, tried and found to work to your liking in new projects.
The class contains some private variables. They are not directly accessible from the outside. They are used to keep track of the state of the potentiometer. The public functions (also called methods) are the ones you as a user can use to query (and sometimes set) the state of the class.
The Potentiometer class contains a getValue() function. This does not read the analog value from the ANA_IN_PIN immediately but just returns the variable current_value that was updated by the tick() function. The tick function reads the analog value as an integer number. Given a 12 bit AD-converter this value will vary from 0 to 1023. Tick stores the newly read value in currentPotValue whenever this value differs from the prior value (oldPotValue) by a certain errorValue. Once there is a large enough difference (the function abs() makes that a higher or lower value will do) the boolean valueHasChanged will be set to true.
Often in a program we only want to do something if the user changed some value of the controls so we want the program to be able to check whether something has changed. The function hasChanged() will tell us whether this is the case. Obviously the boolean has to be reset after a change and this could be done whenever the value is read, or whenever the boolean status is queried. I’ve chosen for the latter. So, as soon as if(pot.hasChanged()) is executed we find out that the boolean is true or false and when true it is set to false immediately. in that way the class does its own book keeping.
What is this #ifndef _POTENTIOMETER #define _POTENTIOMETER #endif at the beginning and end of the file containing the class all about?, you may ask.
The Potentiometer.hpp needs to be included in the main program.
Especially if the program grows in complexity and there are other classes which are included in the main program that themselves may include Potentiometer.hpp as well, we run the risk of including it multiple times. That may in some cases lead to problems when compiling the code (classes would appear to be defined multiple times). It is good practise to exclude this from happening by adding the #ifndef constructs to each .hpp file. Typically for the define a name is used that is not likely to be found elsewhere in your program so often a underscore is added to the class name. The #define _POTENTIOMETER will prevent a second inclusion of the code.
What is the errorValue all about? Analog inputs may sometimes pick up some noise. If you do not want this to be seen as a change of the pot value, the errorValue will help to ignore the noise. Only if the change is large enough, the valuehasChanged boolean will be set. I emperically found that 5 is ok. Note that the errorValue introduces what is also called hysteresis.
If in your program, you use more potentiometers, the advantage of using a class is that each pot gets its own object. Each object keeps track of its own current and old value. You do not need to add any additional code for this. Just remember to tick each pot once in a while in the main loop! In the next example you find 2 potentiometers being used.
// example program 3
// Include libprintf which makes printing to the serial port SOOOO much easier.
#include <LibPrintf.h>
// Include the potentiometer class
#include "Potentiometer.hpp"
// Here are some constants and global variables (beware of the latter!).
#define ANA_IN1_PIN A0
#define ANA_IN2_PIN A1
#define SERIAL_SPEED 312500
#define DELAY_CNT 1000000
Potentiometer pot1;
Potentiometer pot2;
void doSomeOtherStuff() {
printf("I'm doing other stuff now.\n");
}
void setup() {
Serial.begin(SERIAL_SPEED);
// We instantiate the potentiometers and link them to an analog input.
pot1 = Potentiometer(ANA_IN1_PIN);
pot2 = Potentiometer(ANA_IN2_PIN);
}
void loop() {
static int cnt = 0;
int value = 0;
if ((cnt % DELAY_CNT) == 0) {
pot1.tick();
if (pot1.hasChanged()) {
value = pot1.getValue();
printf("current value of pot 1: %d\n", value);
}
pot2.tick();
if (pot2.hasChanged()) {
value = pot2.getValue();
printf("current value of pot 2: %d\n", value);
}
}
cnt += 1;
doSomeOtherStuff();
}
To run any of the code, you may need to install LibPrintf using the library manager of the arduino IDE.
I’ve made some derived classes that implement a potentiometer with a zero center value, a potentiometer that is quantized so that it will only return one of e.g. 5 values. This can come in handy if you want to use a potentiometer as a control of the menu system of a device and make a choice out of a discrete number. These other potentiometers are based on the one presented here. In fact they inherit its base functionality and add some more functions. I will leave these code snippets for another post.
For those who want to comment on the examples, remember, this is not stackoverflow.com, most of us are not computer scientists, so BEHAVE !