Array of actuators/sensors + millis

Hi folks!
I have been trying to properly format my code without using delay() and while statements. Almost got there, but now having some problems on replacing delay with millis. It seems I am not putting millis equation in the right place. My code is quite long, so I am pasting only the part I need to solve . I need to all the motors to go up, check their value, go down and check the value again (all at the seemingly same time). Hopefully my lousy program will be understandable :slight_smile: Cheers!

unsigned long previousMillis = 0;        // will store last time LED was updated
const long interval = 250;           // interval at which to act (milliseconds)


//I am intentionally leaving out setup and loop not to have 500 lines of code here :)

void calibrateFader() {
  unsigned long currentMillis = millis();

  for (int i = 0; i < NumberOfSliders; i++) {
    digitalWrite(motorUp[i], HIGH); //Arduino sends the sliders up
    if (currentMillis - previousMillis >= interval) { //Timer checks for the interval to pass
      previousMillis = currentMillis; //Remember the time
      digitalWrite(motorUp[i], LOW); //Arduino stops the sliders
      faderMax[i] = analogRead(wiper[i]); //Reads the maximum values
    }

    digitalWrite(motorDown[i], HIGH); //Sends the sliders down
    if (currentMillis - previousMillis >= interval) { //Timer checks for the interval to pass
      previousMillis = currentMillis; //Remember the time
      digitalWrite(motorDown[i], LOW); //Stops the sliders
      faderMin[i] = analogRead(wiper[i]); //Reads the maximum values
    }
  }

This is how the code looked before the cleaning:

void calibrateFader() {
  //Send fader to the top and read max position
  digitalWrite(motorUp, HIGH);
  digitalWrite(motorUp2, HIGH);
  digitalWrite(motorUp3, HIGH);
  digitalWrite(motorUp4, HIGH);
  digitalWrite(motorUp5, HIGH);
  digitalWrite(motorUp6, HIGH);
  digitalWrite(motorUp7, HIGH);
  digitalWrite(motorUp8, HIGH);
  digitalWrite(motorUp9, HIGH);

  delay(250);
  digitalWrite(motorUp, LOW);
  digitalWrite(motorUp2, LOW);
  digitalWrite(motorUp3, LOW);
  digitalWrite(motorUp4, LOW);
  digitalWrite(motorUp5, LOW);
  digitalWrite(motorUp6, LOW);
  digitalWrite(motorUp7, LOW);
  digitalWrite(motorUp8, LOW);
  digitalWrite(motorUp9, LOW);

  faderMax = analogRead(wiper);
  faderMax2 = analogRead(wiper2);
  faderMax3 = analogRead(wiper3);
  faderMax4 = analogRead(wiper4);
  faderMax5 = analogRead(wiper5);
  faderMax6 = analogRead(wiper6);
  faderMax7 = analogRead(wiper7);
  faderMax8 = analogRead(wiper8);
  faderMax9 = analogRead(wiper9);
  
  //Send fader to the bottom and read max position
    digitalWrite(motorDown, HIGH);
    digitalWrite(motorDown2, HIGH);
    digitalWrite(motorDown3, HIGH);
    digitalWrite(motorDown4, HIGH);
    digitalWrite(motorDown5, HIGH);
    digitalWrite(motorDown6, HIGH);
    digitalWrite(motorDown7, HIGH);
    digitalWrite(motorDown8, HIGH);
    digitalWrite(motorDown9, HIGH);

  delay(250);
    digitalWrite(motorDown, LOW);
    digitalWrite(motorDown2, LOW);
    digitalWrite(motorDown3, LOW);
    digitalWrite(motorDown4, LOW);
    digitalWrite(motorDown5, LOW);
    digitalWrite(motorDown6, LOW);
    digitalWrite(motorDown7, LOW);
    digitalWrite(motorDown8, LOW);
    digitalWrite(motorDown9, LOW);


  faderMin = analogRead(wiper);
  faderMin2 = analogRead(wiper2);
  faderMin3 = analogRead(wiper3);
  faderMin4 = analogRead(wiper4);
  faderMin5 = analogRead(wiper5);
  faderMin6 = analogRead(wiper6);
  faderMin7 = analogRead(wiper7);
  faderMin8 = analogRead(wiper8);
  faderMin9 = analogRead(wiper9);
}

It looks to me that you need a previousMillis array as each slider will presumably move at its own speed.

Look at this much simpler program to see what I mean

unsigned long startTimes[] = {0, 0};
unsigned long periods[] = {1001, 1005};    //examples
const byte ledPins[] = {10, 11};
const int numberOfLeds = sizeof(ledPins) / sizeof(ledPins[0]);

void setup()
{
  Serial.begin(115200);
  Serial.print("Number of LEDs : ");
  Serial.println(numberOfLeds);
  for (int pin = 0; pin < numberOfLeds; pin++)
  {
    pinMode(ledPins[pin], OUTPUT);
  }
}

void loop()
{
  unsigned long currentTime = millis();
  for (int led = 0; led < numberOfLeds; led++)
  {
    if (currentTime - startTimes[led] > periods[led])
    {
      digitalWrite(ledPins[led], !digitalRead(ledPins[led]));
      startTimes[led] = startTimes[led] + periods[led];
    }
  }
}

Thinking about it, how will the program know that each slider is at its max or min position and what happens when the slider motors continue to run when the sliders are at their end positions ?

UKHeliBob:
Thinking about it, how will the program know that each slider is at its max or min position and what happens when the slider motors continue to run when the sliders are at their end positions ?

(come back for more help!)

It looks like he has an encoder that he is reading the max and min position of the actuators in calibrateFader() in order to be able to answer that question.

Your pointing out the power of the array is Key, I suspect that all his code would fit on the forum if he were using arrays. Seeing more code would certainly help to see where improvements could be made.

An object oriented approach would be another scheme to investigate (even though I risk being castigated by @Robin2 for even mentioning it before OP is vetted for his readiness for that approach). :zipper_mouth_face:

Hi! Thanks for suggestions!

Thinking about it, how will the program know that each slider is at its max or min position and what happens when the slider motors continue to run when the sliders are at their end positions ?

Indeed I have a reading potentiometer to know when actuators reach the highest value. I am using an array of ALPS motorised sliders as vertical actuators.

Yes, I think I need an array of Millis assign to each of the sliders so all of them could move separately, which I don't mind, since the difference of start will be very slight (probably?) Sadly, I had no time to try it out yet - will do it tonight.

When sliders hit their maximum position their motors will turn in place (axles are not rigidly connected) - so no damage there. And those 250ms is just enough to hit maximum.

And this is my full code:

//Arduino Pin Assignments
const int motorDown[]    = {2, 4, 6, 8, 10, 12, 22, 26, 30};   //H-Bridge control to make the motor go down
const int motorUp[]      = {3, 5, 7, 9, 11, 13, 24, 28, 32};   //H-Bridge control to make the motor go up

//Inputs
const int wiper[]        = {0, 1, 2, 3, 4, 5, 6, 7, 8};   //Position of fader relative to GND (Analog 0)

//Variables
int NumberOfSliders = 9;
double faderMax[9];
double faderMin[9];

int state[9];
int reading;

unsigned long previousMillis = 0;        // will store time 
const long interval = 250;           // interval at which to act (milliseconds)


void setup() {

  Serial.begin(9600);
  for (int i = 0; i < NumberOfSliders; i++) {
    pinMode (motorUp[i], OUTPUT);
  }
  for (int i = 0; i < NumberOfSliders; i++) {
    pinMode (motorDown[i], OUTPUT);
  }
  calibrateFader();
}

void loop() {
  reading = Serial.read();
  //patterns forming from an array of sliders
  switch (reading) { 
    case 0:
      for (int i = 0; i < NumberOfSliders; i++) {
        state[i] = 30;
      }
      break;

    case 1:
      for (int i = 0; i < NumberOfSliders; i++) {
        state[i] = 30;
        if (i == 1 || i == 3 || i == 4 || i == 5)
          state[i] = 500;
      }
      break;

    case 2:
      for (int i = 0; i < NumberOfSliders; i++) {
        state[i] = 30;
        if (i == 3 || i == 5)
          state[i] = 500;
      }
      break;

    case 3:
      for (int i = 0; i < NumberOfSliders; i++) {
        state[i] = 30;
        if (i == 0 || i == 2 || i == 3 || i == 5 || i == 7)
          state[i] = 500;
      }
      break;
  }

  for (int i = 0; i < NumberOfSliders; i++) {
//slider controller: if the given state is not what potmeter (wiper) is reading - move it there
    if (state[i] < analogRead(wiper[i]) - 20 && state[i] > faderMin[i]) { 
      digitalWrite(motorDown[i], HIGH);
    }
    else {
      digitalWrite(motorDown[i], LOW);
    }

    if (state[i] > analogRead(wiper[i]) + 20 && state[i] < faderMax[i]) {
      digitalWrite(motorUp[i], HIGH);
    }
    else {
      digitalWrite(motorUp[i], LOW);
    }
  }
}

//Calibrates the min and max position of the faders
void calibrateFader() {
  unsigned long currentMillis = millis();

  for (int i = 0; i < NumberOfSliders; i++) {
    digitalWrite(motorUp[i], HIGH); //Arduino sends the sliders up
    if (currentMillis - previousMillis >= interval) { //Timer checks for the interval to pass
      previousMillis = currentMillis; //Remember the time
      digitalWrite(motorUp[i], LOW); //Arduino stops the sliders
      faderMax[i] = analogRead(wiper[i]); //Reads the maximum values
    }

    digitalWrite(motorDown[i], HIGH); //Sends the sliders down
    if (currentMillis - previousMillis >= interval) { //Timer checks for the interval to pass
      previousMillis = currentMillis; //Remember the time
      digitalWrite(motorDown[i], LOW); //Stops the sliders
      faderMin[i] = analogRead(wiper[i]); //Reads the maximum values
    }
  }
}

Hi guys :slight_smile:
I have looked at many different examples how to use millis with an array, but none of them seem to work for now. The weird part is that in calibrateFader(); we don't even go to the first action:

digitalWrite(motorUp[i], HIGH); //Arduino sends the sliders up

which is before all millis statements.
Now the code looks like that:

unsigned long startMillis[9];
unsigned long previousMillis = 0;        // will store time
const long interval[] = {250, 250, 250, 250, 250, 250, 250, 250, 250};           // interval at which to act (milliseconds)

void calibrateFader() {

  unsigned long currentMillis = millis();

  for (int i = 0; i < NumberOfSliders; i++) {
    digitalWrite(motorUp[i], HIGH); //Arduino sends the sliders up
    if (currentMillis - startMillis[i] >= interval[i]) { //Timer checks for the interval to pass
      digitalWrite(motorUp[i], LOW); //Arduino stops the sliders
      faderMax[i] = analogRead(wiper[i]); //Reads the maximum values
      startMillis[i] = startMillis[i] + interval[i];
    }

    digitalWrite(motorDown[i], HIGH); //Sends the sliders down
    if (currentMillis - startMillis[i] >= interval[i]) { //Timer checks for the interval to pass
      digitalWrite(motorDown[i], LOW); //Stops the sliders
      faderMin[i] = analogRead(wiper[i]); //Reads the maximum values
      startMillis[i] = startMillis[i] + interval[i];
    }
  }
}

So, I'll go back to my recommendation on an object-oriented approach.

Start with all of the functions you need to read/move exactly one ALPS slider (without using any delay())

Once you have that, it is easy to take you through building a class that will allow you to manage several of them.

The weird part is that in calibrateFader(); we don't even go to the first action:

If the interval has not passed then the code is effectively

    digitalWrite(motorUp[i], HIGH); //Arduino sends the sliders up
    digitalWrite(motorDown[i], HIGH); //Sends the sliders down

Would you expect the sliders to move ?

UKHeliBob:
If the interval has not passed then the code is effectively

    digitalWrite(motorUp[i], HIGH); //Arduino sends the sliders up

digitalWrite(motorDown[i], HIGH); //Sends the sliders down



Would you expect the sliders to move ?

Yes, that was foolish of me. Of course they would not move since both motors are pulling in different directions. But I couldn't find an easy way to make it work step-by-step. Now with only one slider I have this code:

int counter;
void calibrateFader() {
  unsigned long currentMillis = millis();
  if (counter == 0) {
    digitalWrite(motorUp, HIGH);
    counter ++;
    Serial.println("HIGH");
  }
  if (currentMillis - previousMillis >= interval && counter == 1) {
    previousMillis = currentMillis;
    digitalWrite(motorUp, LOW);
    faderMax = analogRead(wiper);
    Serial.println("LOW");
    counter++;
  }
  if (counter == 2) {
    digitalWrite(motorDown, HIGH);
    counter ++;
  }
  if (currentMillis - previousMillis >= interval && counter == 3) {
    previousMillis = currentMillis;
    digitalWrite(motorDown, LOW);
    faderMin = analogRead(wiper);
  }
}

It works fine in loop(), but not in calibrateFader() - which only runs once from the setup.

currentMillis and previousMillis are dumb names. Try using names that make sense, like now and then.

The now variable is obviously assigned a value that really is now.

But, the then variable is not assigned a value anywhere in the snippet you posted, before the function is called. What, really, does then mean?

Assuming that it means something, make a chart showing now, then, and counter. Play computer, starting from when calibrateFader() is called. Make reasonable guesses as to what now and then contain, and work out what the code should do.

Add Serial.print() statements, if needed, to print out what is actually in now, then, and counter at various places in the function, to confirm that your assumptions about what is in now and then are accurate, or to see just where your assumptions are wrong.

Now, I rarely advocate using delay(), but calibrateFader() looks like a function that should only be called once, from setup(), and it doesn't look like setup() should end until the fader(s) is(are) calibrated, so using delay() might just be appropriate, instead of adding a while loop and calling calibrateFader() over and over again, in a blocking while loop.