I am using a potentiometer to choose between four options, visualised with indicator LEDs, using switch case. This works well. However, any function (later, a motor shall run for a certain time, for example) called from a case will be triggered ad infinitum, when the potentiometer knob isn't turned. For example, here, another LED isn't flashed 23 or 37 times, but keeps flashing, because the case has not changed when the potentiometer knob hasn't been turned, or stops flashing when the potentiometer knob is turned before the function finished what it was doing. What should happen is this:
Potentiometer knob not turned: Function that was called is executed exactly once
Potentiometer knob turned: Function previously called shall finish. New function called is executed exactly once
What would I need to change in order to achieve this functionality?
// Variables that remain constant
const byte pinPotentiometer = A0;
const byte pinsLED[] = {3, 4, 5, 6};
const size_t numLEDs = sizeof pinsLED / sizeof pinsLED[0];
// Variables that can change
bool stateLED = LOW;
void setup()
{
for (int i = 0; i < numLEDs; i++)
{
pinMode(pinsLED[i], OUTPUT);
}
}
void loop()
{
int readingPotentiometer = analogRead(A0);
switch (readingPotentiometer)
{
case 0 ... 255:
// functionOne(); that does someting only once when called
turnOffIndicatorLEDs();
digitalWrite(3, HIGH);
break;
case 256 ... 511:
flashLED(250, 750, 23); // Something to illustrate the problem
turnOffIndicatorLEDs();
digitalWrite(4, HIGH);
break;
case 512 ... 767:
// functionOne(); that does someting only once when called
turnOffIndicatorLEDs();
digitalWrite(5, HIGH);
break;
case 768 ...1023:
flashLED(125, 125, 37); // Something to illustrate the problem
turnOffIndicatorLEDs();
digitalWrite(6, HIGH);
break;
}
}
void turnOffIndicatorLEDs()
{
for (int i = 0; i < numLEDs; i++)
{
digitalWrite(pinsLED[i], LOW);
}
}
void flashLED(int timeon, int timeoff, byte flashes)
{
static unsigned long timeNowLED = 0;
static unsigned long timeIntervalLED = 0;
static byte counter = 0;
if (millis() - timeNowLED > timeIntervalLED)
{
timeNowLED = millis();
if (stateLED == LOW)
{
timeIntervalLED = timeon;
stateLED = HIGH;
digitalWrite(13, stateLED);
}
else
{
timeIntervalLED = timeoff;
stateLED = LOW;
counter++;
digitalWrite(13, stateLED);
}
}
if (counter >= flashes)
{
counter = 0;
}
}
Using an analog means he'll have to maintain local flags I think, as equality comparison is going to be flaky due to noise. Look at the case ranges. @Lagom I hope you realise that if the pot is turned to, for example, 255, you'll have enough noise to get some values 254/255/256 to flicker between your cases, which may be irrelevant, or a disaster, depending on the purpose of the functions.
C
Perhaps they can use a percentage of deviation so that exact analog readings do not need to be matched but say within 10 percent of the previous. That's what I'd do and have done in the past and will do so when needed.
That could work, but in the general case, I think a rotary selector that can be set by an inexperienced operator to flicker between functions is a poor design choice. If the pot were a circuit board pot, set once and forget, okay, but as a user knob, sooner or later... But that's up to the OP.
C
Yeah, I could leave a deadband between the four ranges, but that's not my problem at hand. The potentiometer is very reliable and has four linear valley detents.
I have the feeling that the four functions should not be called directly from a case. To run "independently/concurrently", they must be millis() timed, like the flashLED example, and thus they must be called continuously until time's up, independent of which case triggered them to run.
switch (readingPotentiometer)
{
case 0 ... 250:
// functionOne(); that does someting only once when called
turnOffIndicatorLEDs();
digitalWrite(3, HIGH);
break;
case 261 ... 506:
flashLED(250, 750, 23); // Something to illustrate the problem
turnOffIndicatorLEDs();
digitalWrite(4, HIGH);
break;
case 517 ... 762:
// functionOne(); that does someting only once when called
turnOffIndicatorLEDs();
digitalWrite(5, HIGH);
break;
case 773 ...1023:
flashLED(125, 125, 37); // Something to illustrate the problem
turnOffIndicatorLEDs();
digitalWrite(6, HIGH);
break;
}
They can be repeatedly called. I have four motors that need to run, each for a different time duration, after one of the four cases have been selected. To keep the code simple, I put in a LED flashing function that runs for a set time duration. As I have it now, the function is called all the time for as long as the potentiometer knob isn't turned.
It's very much like spinning plates (or spinning tops). You start case one (spinning plate one), move on to case two, then case three, then four. You either let them all spin, until they fall off the stick (time duration), or you decide when it's getting hairy and exciting, to then go back and spin them all up again. Because you don't know how long each motor runs (time duration can be randomises a bit), things should get out of whack quite nicely after a while.
Maybe I should better draw a graphic, a concurrency graph.
so there are start/stop times for multiple motors and the start/stop times of each motor are not synchronized with each other, meaning one can be start/stopped while another is stopped or running?
if so, the pot sets the mode or start/stop time or periods and the timer start/stops multiple motors when their start/stop times occur
Indeed. Rotating the potentiometer, motors can be started in sequence, as shown in the concurrency graph, and they are not synchronised; each one shall run on its own, and could be re-started before their running time has expired.
Each case shall trigger a motor starting (for now LED flashing) to run for a certain time. So I am thinking I cannot call the four motor functions from the case statements directly, but rather use the case statements to set "motorStart" flags.
How long each motor runs cannot be adjusted by the performer, it shall be randomised for each motor.
i think it would be better to describe how you would like things to work rather than how to do it
what exactly does the pot do? does it select some action? does that action occur once or repeatedly if the pot is in the same position?
what does each line in your drawing represent? the behavior of 4 different motors or four possible sequences? what does each colored area represent? what does the height of the colored arro represent? why does the height get larger before going to zero?
@alto777 I will trigger in between cases; have a look at the graph. There's no jumping from case 0 to case 2, as a potentiometer is continuous. It is not like four momentary switches where cases could be triggered independently. That is not desirable here, hence the use of a potentiometer.
@gcjr I thought I had it described sufficiently, adding the graph. The potentiometer, as it is rotated, triggers four motors where each shall run independently for different lengths of time, the duration randomised within limits. If you reverse the potentiometer rotation, a motor that is already running, but in the slowing down phase, will be re-triggered. The black lines going left to right represent an arbitrary timeline after start-up. Each motor is represented with a colour. When a motor is triggered, it runs at full speed and slows down logarithmically over time, until its run-time is over. Then it either sits there doing nothing or it is retriggered. It really is like spinning plates, where you either accelerate the stick's rotation every now and then, or you let it peter out, until the plate almost falls off.
so the pot has ranges that can be called positions (or tom, dick, harry). each position corresponds to a motor. a motor is started when a pot "enters" a position. the motor slows and stops automatically unless restarted. a position can't be skipped.
looks like the code needs to recognize the position of the pot and a change in position to start a motor and capture its timestamp
a timing loop can determine when a motor period has expired and stop it
That's right. And instead of all that motor code, etc. I'm using a simple flashLED function that expires after a certain time as a "stand in", to keep the posted code to a minimum.
Depending on the selected case, LED starts flashing (motor starts running) until its time is up, no matter if the potentiometer has been turned or not. The LED can only start flashing again when that case is re-entered from another case.
I suppose that I have to take a timestamp in each case statement and use a flag somewhere, so when the case does not change, the function pertaining to that case is not re-triggered.