Problems with the millis() function

I constructed an Arduino-controlled camera slider for my granddaughter. The purpose of the slider is time-lapse videos. I wrote a sketch that controls the ride. The code works correctly, but contains a “delay”. I tried to eliminate this “delay” and replace it with a millis() function. Unfortunately, nothing works for me. I would appreciate your help in solving this problem.

#include <AccelStepper.h>
#include <MultiStepper.h>

float Dp = 50 ;     // Pass length (cm)     
const int ng = 0 ; // Recording time (hours)        
const int nm = 1 ; // Recording time (minutes)       
const int ns = 0 ; // Recording time (seconds)               
const int mo = 0 ; // Playback time (minutes)        
const int so = 1 ; // Playback time (seconds)   
const float Kog = 60 ; // Head rotation angle       
const long Lz = (mo*60+so)*25;  //Number of photos
const float Czp = ng*3600 + nm*60 + ns; //Travel time in sec
const float Omk = Czp*1000/Lz ; // Frame to frame spacing in millisec      
const long Lkp = Dp*10*80; // Number of steps for the entire ride
const long Lkk = Lkp/(Lz-1);        // Number of steps between frames
const long Lko = 3*(Kog/Lz)*8.8889;  // Number of head rotation steps between frames at 32 microsteps
const long Pred =2500;     
bool flag = true; 
  
AccelStepper stepper1(1, 2, 5); 
AccelStepper stepper2(1, 3, 6);
const int enablePin1 = 8;
const int enablePin2 = 8;

MultiStepper steppersControl;  
long gotoposition[2]; 

void setup() {  
  Serial.begin(9600);
  pinMode(enablePin1,OUTPUT);
  pinMode(enablePin2,OUTPUT);
  digitalWrite(enablePin1,LOW);
  digitalWrite(enablePin2,LOW);
  stepper1.setMaxSpeed(Pred); // Set maximum speed value for the stepper
  stepper2.setMaxSpeed(Pred);
  steppersControl.addStepper(stepper1);
  steppersControl.addStepper(stepper2);
}
void loop() {
  if(flag){
for(int i = 0; i <= (Lz-1); i++){
  Serial.print("pozostało: ");
  Serial.println(Lz-1-i);
  gotoposition[0] = Lkk;  
  gotoposition[1] = Lko;
  steppersControl.moveTo(gotoposition); // Calculates the required speed for all motors
  steppersControl.runSpeedToPosition(); // Blocks until all steppers are in position
   
 delay((Omk-Lkk*1000UL/Pred)); //I tried to replace it with millis()

stepper1.setCurrentPosition(0);
stepper2.setCurrentPosition(0);
 }
}
flag=false;
digitalWrite(enablePin1,HIGH);
digitalWrite(enablePin2,HIGH);
}

Welcome to the forum

Thank you for using code tags in your first post. Many new users don't do this

Please post your best effort at doing this

1 Like

Why? You would not do this without a reason.

2 Likes

I have tried many ways. Among other things:

#include <AccelStepper.h>
#include <MultiStepper.h>

float Dp = 50 ;     // Pass length (cm)     
const int ng = 0 ; // Recording time (hours)        
const int nm = 1 ; // Recording time (minutes)       
const int ns = 0 ; // Recording time (seconds)               
const int mo = 0 ; // Playback time (minutes)        
const int so = 1 ; // Playback time (seconds)   
const float Kog = 60 ; // Head rotation angle       
const long Lz = (mo*60+so)*25;  //Number of photos
const float Czp = ng*3600 + nm*60 + ns; //Travel time in sec
const float Omk = Czp*1000/Lz ; // Frame to frame spacing in millisec      
const long Lkp = Dp*10*80; // Number of steps for the entire ride
const long Lkk = Lkp/(Lz-1);        // Number of steps between frames
const long Lko = 3*(Kog/Lz)*8.8889;  // Number of head rotation steps between frames at 32 microsteps
const long Pred =2500;     
bool flag = true; 
unsigned long Ac = 0; // Current time
unsigned long Zc = 0; //Memorized time
unsigned long Rc = 0; // Time difference (Ac-Zc)
  
AccelStepper stepper1(1, 2, 5); 
AccelStepper stepper2(1, 3, 6);
const int enablePin1 = 8;
const int enablePin2 = 8;

MultiStepper steppersControl;  
long gotoposition[2]; 

void setup() {  
  Serial.begin(9600);
  pinMode(enablePin1,OUTPUT);
  pinMode(enablePin2,OUTPUT);
  digitalWrite(enablePin1,LOW);
  digitalWrite(enablePin2,LOW);
  stepper1.setMaxSpeed(Pred); // Set maximum speed value for the stepper
  stepper2.setMaxSpeed(Pred);
  steppersControl.addStepper(stepper1);
  steppersControl.addStepper(stepper2);
}
void loop() {
  if(flag){
for(int i = 0; i <= (Lz-1); i++){
  Serial.print("pozostało: ");
  Serial.println(Lz-1-i);
  gotoposition[0] = Lkk;  
  gotoposition[1] = Lko;
  steppersControl.moveTo(gotoposition); // Calculates the required speed for all motors
  steppersControl.runSpeedToPosition(); // Blocks until all steppers are in position
   
 //delay((Omk-Lkk*1000UL/Pred)); //I tried to replace it with millis()
Ac = millis();
Rc = Ac-Zc;
if (Rc >= (Omk-Lkk*1000UL/Pred)) {
stepper1.setCurrentPosition(0);
stepper2.setCurrentPosition(0);
Zc = Ac;
 }
}
}
flag=false;
digitalWrite(enablePin1,HIGH);
digitalWrite(enablePin2,HIGH);
}

OMG smabr idk wtm

understand it?
guess no.

Oh my god so many abbreviations i don't know what they mean.
I mean your variable-names are so cryptic and maybe even polish that it is very difficult to analyse your code.

Can you please describe in normal words with example-numbers
what each and every step of the calculation is doing.

gotoposition[0] = Lkk;

what is position 0? starting position?
what is lkk?? last kid kommanding?

gotoposition[1] = Lko;
what is position 1 ? end-position?
what is lko? last kid operating?

what is Zc? zeta-choice?

what is Ac? alternate current?

what is Rc? radio controlled?

You should rewrite the whole code with meaningful self-explainig variable names.

self-explaining variable-names make comments obsolete

1 Like

look this over

#include <AccelStepper.h>
#include <MultiStepper.h>

float Dp = 50 ;     // Pass length (cm)
const int ng = 0 ; // Recording time (hours)
const int nm = 1 ; // Recording time (minutes)
const int ns = 0 ; // Recording time (seconds)
const int mo = 0 ; // Playback time (minutes)
const int so = 1 ; // Playback time (seconds)
const float Kog = 60 ; // Head rotation angle
const long  Lz  = (mo*60+so)*25;  //Number of photos
const float Czp = ng*3600 + nm*60 + ns; //Travel time in sec
const float Omk = Czp*1000/Lz ; // Frame to frame spacing in millisec
const long  Lkp = Dp*10*80; // Number of steps for the entire ride
const long  Lkk = Lkp/(Lz-1);        // Number of steps between frames
const long  Lko = 3*(Kog/Lz)*8.8889;  // Number of head rotation steps between frames at 32 microsteps
const long Pred =2500;

unsigned long Ac = 0; // Current time
unsigned long Zc = 0; //Memorized time
unsigned long Rc = 0; // Time difference (Ac-Zc)

AccelStepper stepper1(1, 2, 5);
AccelStepper stepper2(1, 3, 6);

const int enablePin1 = 8;
const int enablePin2 = 8;

MultiStepper steppersControl;
long gotoposition[2];

// -------------------------------------
const unsigned long MsecPeriod = (Omk-Lkk*1000UL/Pred);
      unsigned long msec0;

bool run = true;
int  idx;

// -----------------------------------------------------------------------------
void loop()
{
    if (! run)
        return;

    unsigned long msec = millis ();
    
    if (msec - msec0 >= MsecPeriod)  {
        msec0 = msec;

        Serial.print("pozostało: ");
        Serial.println(Lz-1-idx);
        gotoposition[0] = Lkk;
        gotoposition[1] = Lko;
        steppersControl.moveTo(gotoposition); // Calculates the required speed for all motors
        steppersControl.runSpeedToPosition(); // Blocks until all steppers are in position

        // ???
        Ac = msec;
        Rc = Ac-Zc;
        if (Rc >= (Omk-Lkk*1000UL/Pred)) {
            stepper2.setCurrentPosition(0);
            Zc = Ac;
        }

        // done
        if (Lz-1 <= ++idx)  {
            run = false;
            digitalWrite(enablePin1,HIGH);
            digitalWrite(enablePin2,HIGH);
        }
    }
}

// -----------------------------------------------------------------------------
void setup() {
    Serial.begin(9600);
    pinMode(enablePin1,OUTPUT);
    pinMode(enablePin2,OUTPUT);
    digitalWrite(enablePin1,LOW);
    digitalWrite(enablePin2,LOW);
    stepper1.setMaxSpeed(Pred); // Set maximum speed value for the stepper
    stepper2.setMaxSpeed(Pred);
    steppersControl.addStepper(stepper1);
    steppersControl.addStepper(stepper2);
}

If it ain't broke, you're not fixing it hard enough, of course!

Leaps in steps of 64.

Indeed a bit of language barrier coming into play here. The time variables do seem to make sense in Polish, but would be rather cryptic to an English reader. Time in Polish is czas, so whereas in English 't' is used as an abbreviation, he uses 'c', so I suspect as follows:

Ac - aktualny czas = actual or current time
Zc - zapamientany czas = remembered/previous time
Rc - róznica czasu - time difference

Not figured out what Lkk or Lko are yet though....

Either way those abbreviations are still rather cryptic in either language and make for difficult reading. OP, the compiler converts the variable names to memory addresses anyway so it makes no difference to the compiler if you use descriptive variable names. The advantage of doing so is that it makes the code easier to read and understand.

One other observation and hint: learn to indent your code consistently so that for loops and if statements are clearly identified, or else use the Auto Format feature (Ctrl-T) in the IDE which will do this for you. It will make the code much easier to read.

k = steps.

Lol, ok.

But I can't see any reason why @andrzejn needs to replace delay(). Maybe they think it's important when, in this case, it isn't.

1 Like

isn't it a fixed time ?

There may indeed have been language problems. There are many variables, because on their basis I calculate the necessary parameters to perform the move.
ng,nm,ns is the time during which the camera will move. Respectively: ng- hours; nm - minutes; ns-seconds. Lkp is the number of steps the motor must make during the entire horizontal movement. Lkk - the number of steps that the engine must perform between individual frames of the film. Lko is the number of motor steps in rotation between frames. so is the time-lapse video playback time needed to calculate the number of photos-Lz.
BitSeeker correctly deciphered the abbreviations Ac, Zc and Rc.

Try each in the simulator...

diagram.json for wokwi
{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-nano", "id": "nano", "top": 43.2, "left": -38.9, "attrs": {} },
    {
      "type": "wokwi-stepper-motor",
      "id": "stepper1",
      "top": -230.41,
      "left": -121.38,
      "attrs": { "size": "8" }
    },
    { "type": "wokwi-a4988", "id": "drv1", "top": -100.8, "left": -177.6, "attrs": {} },
    { "type": "wokwi-vcc", "id": "vcc1", "top": -143.24, "left": -124.8, "attrs": {} },
    { "type": "wokwi-gnd", "id": "gnd1", "top": -9.6, "left": -115.8, "attrs": {} },
    {
      "type": "wokwi-stepper-motor",
      "id": "stepper2",
      "top": -230.41,
      "left": 22.62,
      "attrs": { "size": "8" }
    },
    { "type": "wokwi-a4988", "id": "drv2", "top": -100.8, "left": -33.6, "attrs": {} },
    { "type": "wokwi-vcc", "id": "vcc2", "top": -143.24, "left": 19.2, "attrs": {} },
    { "type": "wokwi-gnd", "id": "gnd2", "top": 0, "left": 76.2, "attrs": {} }
  ],
  "connections": [
    [ "vcc1:VCC", "drv1:VMOT", "red", [ "v0" ] ],
    [ "gnd1:GND", "drv1:GND.1", "black", [ "v0" ] ],
    [ "gnd1:GND", "drv1:GND.2", "black", [ "v0" ] ],
    [ "vcc1:VCC", "drv1:VDD", "red", [ "v0" ] ],
    [ "drv1:2B", "stepper1:A+", "green", [ "h0" ] ],
    [ "drv1:2A", "stepper1:A-", "green", [ "h0" ] ],
    [ "drv1:1A", "stepper1:B-", "green", [ "h0" ] ],
    [ "drv1:1B", "stepper1:B+", "green", [ "h0" ] ],
    [ "vcc2:VCC", "drv2:VMOT", "red", [ "v0" ] ],
    [ "gnd2:GND", "drv2:GND.1", "black", [ "v0" ] ],
    [ "gnd2:GND", "drv2:GND.2", "black", [ "v0" ] ],
    [ "vcc2:VCC", "drv2:VDD", "red", [ "v0" ] ],
    [ "drv2:2B", "stepper2:A+", "green", [ "h0" ] ],
    [ "drv2:2A", "stepper2:A-", "green", [ "h0" ] ],
    [ "drv2:1A", "stepper2:B-", "green", [ "h0" ] ],
    [ "drv2:1B", "stepper2:B+", "green", [ "h0" ] ],
    [ "nano:8", "drv2:ENABLE", "green", [ "v-48", "h-76.8", "v-96" ] ],
    [ "nano:8", "drv1:ENABLE", "green", [ "v-48", "h-220.8", "v-76.8" ] ],
    [ "drv1:SLEEP", "drv1:RESET", "green", [ "h-19.2", "v-9.6" ] ],
    [ "drv2:SLEEP", "drv2:RESET", "green", [ "h-19.2", "v-9.6" ] ],
    [ "nano:2", "drv1:STEP", "green", [ "v-9.6", "h-268.8", "v-76.8" ] ],
    [ "nano:5", "drv1:DIR", "green", [ "v-19.2", "h-230.4", "v-57.6" ] ],
    [ "nano:3", "drv2:STEP", "green", [ "v-57.6", "h-115.2", "v-28.8" ] ],
    [ "nano:6", "drv2:DIR", "green", [ "v-67.2", "h-76.8", "v-9.6" ] ]
  ],
  "dependencies": {}
}

Time on Arduino is done with unsigned integers only.

Proper formula that always works is to subtract start time from end time to get elapsed time. Subtract to get the difference.

If we have a start time and desired interval, we may keep checking if ( now - start >= interval ) where now is millis() or micros.

Think of a round clock, 12 hour. If the start hour is 8 and the end hour is 3... put the hand on 3 (end hour) and subtract 8 (start hour) by moving the hand left (counter clockwise) to subtract and it will go 3-2-1-12-11-10-9-8-7 is 8 to the left of 3... and the difference is 7 hours. From 7 to 5 is 5 then 7 to the left ends on 10, answer is 10 hours same as start 1 and end 11 also gets 10. Always right. The clock is unsigned numbers.

The millis() function uses unsigned long for counting up milliseconds.

Here is a small demo-code that simulates the rollover of the function millis() with a variable of the same type which is unsigned long

unsigned long start;
unsigned long simMillis;

void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  // 4 before max 4294967295
  simMillis     = 4294967291; 
  start = simMillis;

  for (byte i = 0; i <= 10; i++) {
    Serial.print("simMillis=");
    Serial.print(simMillis);

    Serial.print("  start=");
    Serial.println(start);

    Serial.println("simMillis - start");
    Serial.print(simMillis);
    Serial.print(" - ");
    Serial.print(start);
    Serial.print(" = ");
    Serial.println(simMillis - start);
    Serial.println();
    simMillis++;  
  }
}

void loop() {
}
1 Like

I applied gcjr's hint and also greatly simplified the code. It works very well. Thank you all very much for your help. Here is the code:

#include <AccelStepper.h>
#include <MultiStepper.h>

const long a = 1667; // number of motor steps in horizontal between frames
const long b = 64; // number of motor steps in rotation between frames
const long c = 25; // number of pictures
const long d = 2400; // interval between shots (milliseconds)
const long v = 5000;
bool flag = true;

AccelStepper stepper1(1, 2, 5);
AccelStepper stepper2(1, 3, 6);
const int enablePin1 = 8;
const int enablePin2 = 8;

MultiStepper steppersControl;
long gotoposition[2];
const unsigned long MsecPeriod = (d);
unsigned long msec0;

bool run = true;
int  idx;
void setup() {
  Serial.begin(9600);
  pinMode(enablePin1, OUTPUT);
  pinMode(enablePin2, OUTPUT);
  digitalWrite(enablePin1, LOW);
  digitalWrite(enablePin2, LOW);
  stepper1.setMaxSpeed(v); // Set maximum speed value for the stepper
  stepper2.setMaxSpeed(v);
  steppersControl.addStepper(stepper1);
  steppersControl.addStepper(stepper2);
}
void loop() {
 
    if (! run)
      return;
    unsigned long msec = millis ();
    if (msec - msec0 >= MsecPeriod)  {
      msec0 = msec;
      Serial.print("pozostalo: ");
      Serial.println(c - 1 - idx);
      gotoposition[0] = a;
      gotoposition[1] = b;
      steppersControl.moveTo(gotoposition); // Calculates the required speed for all motors
      steppersControl.runSpeedToPosition(); // Blocks until all steppers are in position

      stepper1.setCurrentPosition(0);
      stepper2.setCurrentPosition(0);

      if (c - 1 <= ++idx)  {
        run = false;
        digitalWrite(enablePin1, HIGH);
        digitalWrite(enablePin2, HIGH);
      }
    }
  }

I will input the parameters for the slider movement via bluetooth. So I put the same solution into the code, which takes into account this way of setting parameters. Unfortunately, the timelaps() function in this code does not work. But why?

//#include <String.h>
#include <AccelStepper.h>
#include <MultiStepper.h>
#define buffsize 64
void(* resetFunc) (void) = 0;
char input[buffsize];
uint32_t dane [5]; // jakie i ile  zmiennych odbieramy z UART

byte index = 0;
boolean stringComplete = false;  // gdy cale polecenie/napis odebrany
bool flag = true;

long a;
int b;
int c;
int d;
int x;
int v = 5000;

AccelStepper stepper1 (1, 2, 5); //shield V3
AccelStepper stepper2 (1, 3, 6);
const int enablePin1 = 8; // Steruje wyłączaniem sterowników
const int enablePin2 = 8;
MultiStepper steppersControl;
long gotoposition[2];
const unsigned long MsecPeriod = (d);
unsigned long msec0;
bool run = true;
int  idx;

void   parsujpolecenia()
{
  
  uint8_t index = 0;
  char * polecenie = input;
  Serial.print("Otrzymane polecenie: ");
  Serial.println(polecenie);
  digitalWrite(enablePin1, LOW);
  digitalWrite(enablePin2, LOW);
  char* command1 = strtok(polecenie, "=");

  if (strcmp(command1 , "cmd") == 0)
  {
    while ((command1 = strtok(NULL, ",")) != NULL)
    {
      
      dane[index++] = atol(command1); //konwersja napisu na INT i zapisanie do kolejnej pozycji w tablicy
      if (index == (sizeof(dane) / sizeof(dane[0])))
        break;
    }
    a = dane[0];
    b = dane[1];
    c = dane[2];
    d = dane[3];
    x = dane[4];
    ;
  }
  else
    Serial.println("Polecenie nieprawidlowe");
  Serial.println("Aktualne parametry:");
  Serial.print(" a = ");
  Serial.println(a);
  Serial.print(" b = ");
  Serial.println(b);
  Serial.print(" c = ");
  Serial.println(c);
  Serial.print(" d = ");
  Serial.println(d);
  Serial.print(" x = ");
  Serial.println(x);
  stringComplete = false;
}
// Arduino initialization function.
void setup()
{
  Serial.begin(9600);

  pinMode(enablePin1, OUTPUT);
  pinMode(enablePin2, OUTPUT);
  digitalWrite(enablePin1, LOW);
  digitalWrite(enablePin2, LOW);

  steppersControl.addStepper(stepper1);
  steppersControl.addStepper(stepper2);
}
void loop()
{
  while (stringComplete == 0)

    serialEvent();
  parsujpolecenia();

  // Teraz mozna cos zrobic ze zmiennymi

  switch (x)
  {
    case 0:

      pan();

      break;
    case 1:
      timelaps();
      break;

  }

 // resetFunc();
}
void serialEvent() {
  while (Serial.available() > 0) {
    char aChar = Serial.read();
    if (aChar == '\n')
    {
      // wykryto znak konca linii \n, koniec odbioru
      input[index] = 0;
      index = 0;
      stringComplete = true;
    }
    else
    {
      input[index] = aChar;
      if (index < buffsize) index++;
      input[index] = '\0'; // Na koncu napisu wstawiamy 0
    }
  }
}
void pan()
{

  stepper1.setMaxSpeed(c);
  stepper2.setMaxSpeed(d);
  gotoposition[0] = a;  // 800 steps - full rotation with quater-step resolution
  gotoposition[1] = b;

  steppersControl.moveTo(gotoposition); // Calculates the required speed for all motors
  steppersControl.runSpeedToPosition(); // Blocks until all steppers are in position
  digitalWrite(enablePin1, HIGH);
  digitalWrite(enablePin2, HIGH);

}
void timelaps()
{
  {
    if (! run)
      return;

    unsigned long msec = millis ();

    if (msec - msec0 >= MsecPeriod)  {
      msec0 = msec;

      Serial.print("pozostało: ");
      Serial.println(c - 1 - idx);
      stepper1.setMaxSpeed(v); // Set maximum speed value for the stepper
      stepper2.setMaxSpeed(v);
      
      gotoposition[0] = a;
      gotoposition[1] = b;
      steppersControl.moveTo(gotoposition); // Calculates the required speed for all motors
      steppersControl.runSpeedToPosition(); // Blocks until all steppers are in position
      stepper1.setCurrentPosition(0);
      stepper2.setCurrentPosition(0);

      if (c - 1 <= ++idx)  {
        run = false;
        digitalWrite(enablePin1, HIGH);
        digitalWrite(enablePin2, HIGH);
      }
    }
  }
}

Your code demonstrates that you don't care much about code-formatting.
Your code demonstrates that your knowledge about coding is limited.

You have some extra curly braces that are not nescessary.

You use too short to be understandable non-descriptive variable names.

You use foreign language comments which gave english speaking users extra-work to translate them.

post an example-string what you send over bluetooth.

What behaviour of your code do you observe?
You are the only one who can see your device.
what in detail does your device?
stand absolutely still and really nothing happends when sending a bluetooth command?
or something else?

Even the description "stands absolutely still and really nothing happends" is important information.

Add serial printing to your code to make visible in the serial monitor what lines of code where executed and where code-execution seems to stop.

You seem to expect that all details were made for you.

i added a Serial.println () with the function name at the beginning of each function and got the following output

loop
serialEvent
serialEvent
serialEvent
serialEvent
serialEvent
serialEvent
serialEvent
serialEvent
serialEvent

I agree that my programming knowledge is very small. I am almost 70 years old and it is not easy for me to learn .That is why I asked for help.
An example of a command sent via bluetooth:
cmd=1667,64,25,2400,1\n
The problem is that only one movement is made and then nothing happens.