Using millis() to count

I’ve been trying to find a way to solve this problem for a while. I remember seeing a solution somewhere on the internet a while back but I can’t find it now.

I was hoping to be able to use a modulo condition to check for an exact second, tenth second, or hundredth second, and this method comes close; but more often than not, the result is off by +/- 0.000000002 or something like that which prevents the condition from being true.

Can someone please help me understand how to nudge these aberrations to 0, or let me know if there is a better way of going about it?

In the following code, the value of s1 looks better in the console, but its condition doesn’t ever seem to be true. The value of s2 appears to be off most of the time, but does have a fair amount of good days when it decides to draw the rectangle.

The overall goal is to use millis() for two purposes, to display time on the screen to hundredths of a second, and also to use the exact moments where seconds, tenths, and hundredths are “right on” to trigger events.

I’ve been able to use nfs() to make the time display nicely in a text() string, but the other part of the problem eludes me.

int m;
int s1; 
int s2;
void setup() {
   size(100, 100);
}
void draw() {
   background(0);
   m = millis();
   s1 = m / 1000;
   s2 = round(m / 10) * 0.01;
   if (s1 % 1 == 0) {
      fill(126);
      rect(0, 0, width, height);
   }
   if (s2 % 1 == 0) {
      fill(255);
      rect(0, 0, width, height);
   }
   println(s1);
   println(s2);
}
1 Like

Maybe check out Inaccurate time intervals for a Metronome although using millis() instead of System.nanoTime(). In general, you can’t rely on your code hitting the exact time required. You need to maintain variables with the target times, and trigger events / schedule the next time when that time gets exceeded.

1 Like

Thanks, I’m sure that will work fine, I’ll give it a try.

This sounds like floating point rounding error.

Recommended reading: What Every Computer Scientist Should Know About Floating-Point Arithmetic

That’s a long read, but very basically: computers don’t have infinite precision. For example, 1/3 in real life is 0.333333..., with an infinite number of 3s repeating. Compare that to this code:

float x = 1.0 / 3.0;
println(x);

Run this code, and you’ll see that 0.33333334 is printed out.

Also note that your code does not compile. Can you please post an example that compiles, so we can run it on our own computers?

2 Likes

It doesn’t work on my computer either, maybe that’s a big part of the problem. I wrote it in Processing for IOS app, and it behaves quite a bit differently there, I guess I need to just work on the Processing IDE for a while and see if I can figure it out. Maybe some of the things I was trying over the past few days would actually work fine, I don’t know.

This seems to work fine, maybe I’ll just stop using the iOS app since it apparently lets things run when there should be an error.

I ran this for a few minutes and didn’t see any skipped values, so I guess it is fine. I don’t need guaranteed precision over long periods of time, I’m trying to make a game and I wanted there to be a stopwatch display and to be able to synchronize animations to a common time frame.

If you know of some reason this method is going to fail at some point, please let me know.

Thanks for the link about floating point numbers, I will get through as much as I can, but I can tell I’m not going to get very far into it before I can no longer comprehend it.

long interval = 1;
long nextTime;
int tens;
int hundreds;
float timeResult;
String tempText;

void setup() {
 size(500, 500);
 nextTime = millis() + interval;
 tens = 10;
 hundreds = 100;
}

void draw() {
 background(0);
 if (millis() >= nextTime) {
   nextTime = nextTime + interval;
 }

 timeResult = nextTime / 100.0;
 tempText = nfs(timeResult, 1, 2);

 if (timeResult * hundreds % 1 == 0) {
   fill(255, 0, 0);
   textSize(64);
   textAlign(RIGHT, CENTER);
   text(tempText, width - 25, 50);
 }

   if (timeResult * tens % 1 == 0) {
   fill(255, 0, 0);
   textSize(64);
   textAlign(RIGHT, CENTER);
   text(tempText, width - 25, 120);
 }

 if (nextTime % 1 == 0) {
   fill(255);
   textSize(24);
   textAlign(CENTER, CENTER);
   text(tempText, width/2, height/2);
 }
}

I just realized that this is running slow, not real time.

Thats where I started out with this whole thing, trying to do something to divide second() into tenths and hundredths.

So I guess I’m going to have to do something different.

Another thing to consider: by default, the draw() function is called 60 times per second. So even if you have microsecond precision, your logic is still not going to fire at the exact time you’re looking for.

You’re probably better off using frameCount to perform your calculations.

I’ll look into that also. I’m somewhat new to programming, so I’m on a self directed course of trying to learn some of these pretty basic things.

The thought that led me to this problem was something like “I want to make a basic toy-like gear train work together to a believable degree of realism, and it would be great if its speed could also be controlled relative to real time from the system clock, so if I want to say a motor or something is driving a gear at some RPM, the rotations would appear in the correct positions at least once per second.

I know I can’t expect absolute realism, but I thought it would be as simple as updating the rotation amounts by using a value from second(), and dividing that into portions to calculate the rotation angles.

I’ve seen clock examples where the second hand ticks 6 degrees once per second, what I thought I’d be able to do is make a continuous rotation but not have it lose time from one minute to the next. It seems like any solution to make a continuous second hand that is always accurate on the minute will have a varying speed that would probably be noticeable.

I’m starting to think that maybe it’s just not worth the trouble for what I want to do, and it doesn’t help that I’m not exactly sure what I want to do yet, I was just trying to learn the concept of setting translation and rotation of graphic objects based on real time.

And now I can see that there just isn’t a really basic way to do that.

Maybe I will just need to back away from the idea of literally being real time and just let close enough be okay.

The last sketch I posted will probably let me do the things I want, even if it’s not real time.

This sounds like floating point rounding error .

I am familiar with the idea that floats are going to have imprecision. I think I was hoping that there would be some way to take the value of millis() and do calculations on it and force it to round to the nearest hundredth, but it seems like rounding only works on integers. Seems strange to me that there isn’t an easy way to round a float to a number of decimal places, but I’m sure there’s a reason for that. Probably explained in the link you sent

I just found this, maybe it will solve my problem.

Analogue Clock Second hand Sweep not Tick

Thanks for all the suggestions!

Is tying calculations to frame rate a typical way to keep things synchronized?

Some information on this topic (scroll down to “Movement speed”):
https://learnopengl.com/Getting-started/Camera

1 Like

Actually, it’s probably a terrible idea for what you’re trying to do, because it assumes the framerate is accurate. If you skip frames or vsync is different, your timing will be all over the place. Using millis() is a better approach.

I found Quarks post about using the gregoriancalendar and millis() and that seems to cover what I thought I was trying to do.

https://discourse.processing.org/t/analogue-clock-second-hand-sweep-not-tick/2331/4

I’ll just see how it goes for a while with all of the suggestions.

Thanks again

uprage this one ?

int tmpsstart = 0;
int amili1 = 0;
int mili1 = 0;
int tour1 = 0;
int atour1 = 0;
String chrono1 = "00 : 00 : 00";
int best1=0;
String bests1 = "00 : 00 : 00";
int amili2 = 0;
int mili2 = 0;
int tour2 = 0;
int atour2 = 0;
String chrono2 = "00 : 00 : 00";
int best2=0;
String bests2 = "00 : 00 : 00";
int amili3 = 0;
int mili3 = 0;
int tour3 = 0;
int atour3 = 0;
String chrono3 = "00 : 00 : 00";
int best3=0;
String bests3 = "00 : 00 : 00";
String total = "00 : 00 : 00";
color couleur = color(0, 255, 0);                                                              //CHRONO DE 3 VOITURES

void setup() {
  size(1360, 768);
  background(#000000);
  noStroke();
}

void draw() {
                                                          
  background(#94A593);
  if (tmpsstart!=0) {
    mouse();
    total();

    fill(#FF0000);
    rect(0, height-(height/6), width, height/6);           // affichage du rectangle
    fill(#000000);
    text("STOP", (width/2)-100, height-height/20);         // affichage du STOP
    fill(#FFFFFF);
    text(total, (width/2.5)-10, (height/10)) ;             // affichage du chrono general

    fill(#94A593);
    rect(125, 125, 250, 225, 1);
    fill(#94A593);
    rect(535, 125, 250, 225, 1);
    fill(#94A593);
    rect(955, 125, 250, 225, 1);
                                                            //voiture 1
    fill(#000000);
    text("Temp du tour :", 150, 170);
    if (atour1>tour1&&atour1!=0) {
      fill(#00FF00);
    } else {
      fill(#FF0000);
    }
    text(chrono1, 150, 210);
    fill(#000000);
    text("Meilleur temp :", 150, 250);
    fill(#000000);
    text(bests1, 150, 290);
                                                            //voiture 2
    fill(#000000);
    text("Temp du tour :", 560, 170);
    if (atour2>tour2&&atour2!=0) {
      fill(#00FF00);
    } else {
      fill(#FF0000);
    }
    text(chrono2, 560, 210);
    fill(#000000);
    text("Meilleur temp :", 560, 250);
    fill(#000000);
    text(bests2, 560, 290);
                                                            //voiture 3
    fill(#000000);
    text("Temp du tour :", 980, 170);
    if (atour3>tour3&&atour3!=0) {
      fill(#00FF00);
    } else {
      fill(#FF0000);
    }
    text(chrono3, 980, 210);
    fill(#000000);
    text("Meilleur temp :", 980, 250);
    fill(#000000);
    text(bests3, 980, 290);

    PImage img;                                              //Variabe des images, cliquez sur les voitures pour affichez le temps
    img = loadImage("lotus exige 2k.jpg");                   //Image voiture 1   
    image(img, 100, height-(height/6)*3);
    img = loadImage("challenger.jpg");                       //Image voiture 2
    image(img, 525, height-(height/6)*3);
    img = loadImage("911.jpg");                               //Image voiture 3
    image(img, 950, height-(height/6)*3);
  } else {
    if (height-(height/6)<=mouseY&&mouseY<=height) {
      cursor(HAND);
    } else {
      cursor(ARROW);
    }
    fill(#0FBF00);
    rect(0, height-(height/6), width, height/6);
    fill(#000000);
    text("LANCER LE CHRONO", (width/5)-10, height-height/20);
  }
}
void mousePressed() {
  if (mouseButton == LEFT) {
    if (height-(height/6)<=mouseY&&mouseY<=height) {
      chrono() ;
      tour(4);
    } else if (100<=mouseX&&mouseX<=400&& height-(height/6)*3<=mouseY&&mouseY<=(height-(height/6)*3)+168&&tmpsstart!=0) {
      tour(1);
    } else if (525<=mouseX&&mouseX<=800&& height-(height/6)*3<=mouseY&&mouseY<=(height-(height/6)*3)+183&&tmpsstart!=0) {
      tour(2);
    } else if (950<=mouseX&&mouseX<=1209&& height-(height/6)*3<=mouseY&&mouseY<=(height-(height/6)*3)+194&&tmpsstart!=0) { 
      tour(3);
    }
  }
}

void mouse() {
  if (height-(height/6)<=mouseY&&mouseY<=height) {
    cursor(HAND);
  } else if (100<=mouseX&&mouseX<=400&& height-(height/6)*3<=mouseY&&mouseY<=(height-(height/6)*3)+168&&tmpsstart!=0) {
    cursor(HAND);
  } else if (525<=mouseX&&mouseX<=800&& height-(height/6)*3<=mouseY&&mouseY<=(height-(height/6)*3)+183&&tmpsstart!=0) {
    cursor(HAND);
  } else if (950<=mouseX&&mouseX<=1209&& height-(height/6)*3<=mouseY&&mouseY<=(height-(height/6)*3)+194&&tmpsstart!=0) {
    cursor(HAND);
  } else {
    cursor(ARROW);
  }
}
void chrono() {
  if (tmpsstart==0) {
    background(#FFFFFF);
    tmpsstart = millis();
  } else {
    tmpsstart=0;
  }
}

void tour(int i) {
  if (i==1) {                                             // voiture 1
    amili1=mili1;
    mili1 = millis();
    atour1 = tour1; 
    if (amili1==0)tour1=(mili1-tmpsstart);
    else tour1=(mili1-amili1);
    int ms=(tour1/60000);
    int ss=((tour1/1000)-ms*60);
    int mils=(tour1-(ss*1000));
    String m=nf(ms, 2);
    String s=nf(ss, 2);
    String mil=nf(mils/10, 2);
    chrono1=m;
    chrono1+=" : ";
    chrono1+=s;
    chrono1+=" : ";
    chrono1+=mil;
    if (tour1<best1&&best1!=0) {
      best1=tour1;
      bests1=chrono1;
    } else if (best1==0) {
      best1=tour1;
      bests1=chrono1;
    }
  } else if (i==2) {                                      // voiure 2
    amili2=mili2;
    mili2 = millis();
    atour2 = tour2; 
    if (amili2==0)tour2=(mili2-tmpsstart);
    else tour2=(mili2-amili2);
    int ms=(tour2/60000);
    int ss=((tour2/1000)-ms*60);
    int mils=(tour2-(ss*1000));
    String m=nf(ms, 2);
    String s=nf(ss, 2);
    String mil=nf(mils/10, 2);
    chrono2=m;
    chrono2+=" : ";
    chrono2+=s;
    chrono2+=" : ";
    chrono2+=mil;
    if (tour2<best2&&best2!=0) {
      best2=tour2;
      bests2=chrono2;
    } else if (best2==0) {
      best2=tour2;
      bests2=chrono2;
    }
  } else if (i==3) {                                      // voiure 3
    amili3=mili3;
    mili3 = millis();
    atour3 = tour3; 
    if (amili3==0)tour3=(mili3-tmpsstart);
    else tour3=(mili3-amili3);
    int ms = (tour3 /60000);
    int ss = ((tour3 /1000)-ms*60);
    int mils = (tour3-(ss*1000));
    String m = nf(ms, 2);
    String s = nf(ss, 2);
    String mil = nf(mils/10, 2);
    chrono3 = m;
    chrono3+=" : ";
    chrono3+=s;
    chrono3+=" : ";
    chrono3+=mil;
    if (tour3<best3&&best3!=0) {
      best3=tour3;
      bests3=chrono3;
    } else if (best3==0) {
      best3=tour3;
      bests3=chrono3;
    }
  } else if (i==4) {                                       // Stop
    amili1 = 0;
    mili1 = 0;
    tour1 = 0;
    atour1 = 0;
    chrono1 = "00 : 00 : 00";
    best1=0; 
    bests1 = "00 : 00 : 00";
    amili2 = 0; 
    mili2 = 0; 
    tour2 = 0; 
    atour2 = 0; 
    chrono2 = "00 : 00 : 00"; 
    best2=0; 
    bests2 = "00 : 00 : 00";
    amili3 = 0; 
    mili3 = 0; 
    tour3 = 0; 
    atour3 = 0; 
    chrono3 = "00 : 00 : 00"; 
    best1=0; 
    bests3 = "00 : 00 : 00";
    total = "";
  }
}
void total() {
  int tmp=(millis()-tmpsstart);                           // calcul du temps passé entre le début (tmpsstart) et maintenant (mili)
  int ms = (tmp /60000);
  int ss = ((tmp /1000)-ms*60);
  int mils = (tmp-ss*1000-ms*60000);
  String m = nf(ms, 2);
  String s = nf(ss, 2);
  String mil = nf(mils/10, 2);
  total = m;
  total+=" : ";
  total+=s;
  total+=" : ";
  total+=mil;
}

Hey there Neals,

It might be better to start a new post instead of extending this one, because it seems your request doesn’t seamlessly match this topic’s discussion. Since you posted here it’s likely related, so in that case add the link of this topic in your new post.

Also don’t forget explaining your question or problem— we’re happy to help but we need a bit more context! :slight_smile:

1 Like