Help: Sending Serial data every ms using millis()

Hello all,

I'm running into a problem. I'm trying to send some serial data every ms but every 42ms I'm missing a millisecond. I'll post my code below. does anybody knows why? thanks in advance.

// Start code:

long current_millis = 0;
long prev_millis = 0;
int counter = 0;

void setup()
{
Serial.begin(500000);
Serial.println("Monitor");
}

void loop()
{
current_millis = millis();

if (current_millis != prev_millis) {
prev_millis = current_millis;

Serial.print(current_millis);
Serial.print(" ");

counter ++;
if (counter > 24) counter = 0;

switch (counter){
case 0: Serial.print("000000"); break;
case 1: Serial.print("010000"); break;
case 2: Serial.print("020000"); break;
case 3: Serial.print("040000"); break;
case 4: Serial.print("080000"); break;
case 5: Serial.print("100000"); break;
case 6: Serial.print("200000"); break;
case 7: Serial.print("400000"); break;
case 8: Serial.print("800000"); break;
case 9: Serial.print("000100"); break;
case 10: Serial.print("000200"); break;
case 11: Serial.print("000400"); break;
case 12: Serial.print("000800"); break;
case 13: Serial.print("001000"); break;
case 14: Serial.print("002000"); break;
case 15: Serial.print("004000"); break;
case 16: Serial.print("008000"); break;
case 17: Serial.print("000001"); break;
case 18: Serial.print("000002"); break;
case 19: Serial.print("000004"); break;
case 20: Serial.print("000008"); break;
case 21: Serial.print("000010"); break;
case 22: Serial.print("000020"); break;
case 23: Serial.print("000040"); break;
case 24: Serial.print("000080"); break;
}

Serial.println();

} // end millis timer
} // end loop

What do you mean by missing a millisecond? That the program skips one?

That would not be surprising. Every serial print takes time.

That is some code to show you how long the Serial.print takes.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(500000);
}

void loop() {
  // put your main code here, to run repeatedly:

  uint32_t now;

  now = micros();
  Serial.print(millis());
  Serial.print(" ");
  now = micros() - now;

  Serial.println(now);

  delay(10000);
}

If you run it you can see, that it takes about 500µs to send the current_millis. Then you send another 8 bytes of data. Now it takes almost 700 µs to send the data out.
The higher the current_millis will count the longer it will take to send so you will start having problems because the time to send is longer then one µs at some time. This is why the program will eventually skip and not print out once every millisecond.

You are still going to run out of time at some point, but there are a couple of things you can do...

  1. your time variables should be unsigned long, not long
  2. your counter can fit within a byte so make it smaller
  3. If you time based on micros(), you get finer resolution so have a better chance at fighting the time creep.
// Start code:

unsigned long current = 0;
unsigned long prev = 0;
const unsigned long interval = 1000UL;
byte counter = 0;

void setup()
{
  Serial.begin(500000);
  Serial.println("Monitor");
}

void loop()
{
  current =  micros();

  if (current - prev >= interval ) {
    prev+=interval;

    Serial.print(current/1000);
    Serial.println(" ");

    counter = (counter+1) % 25;

    switch (counter) {
      case 0:  Serial.println("000000");     break;
      case 1:  Serial.println("010000");     break;
      case 2:  Serial.println("020000");     break;
      case 3:  Serial.println("040000");     break;
      case 4:  Serial.println("080000");     break;
      case 5:  Serial.println("100000");     break;
      case 6:  Serial.println("200000");     break;
      case 7:  Serial.println("400000");     break;
      case 8:  Serial.println("800000");     break;
      case 9:  Serial.println("000100");     break;
      case 10: Serial.println("000200");     break;
      case 11: Serial.println("000400");     break;
      case 12: Serial.println("000800");     break;
      case 13: Serial.println("001000");     break;
      case 14: Serial.println("002000");     break;
      case 15: Serial.println("004000");     break;
      case 16: Serial.println("008000");     break;
      case 17: Serial.println("000001");     break;
      case 18: Serial.println("000002");     break;
      case 19: Serial.println("000004");     break;
      case 20: Serial.println("000008");     break;
      case 21: Serial.println("000010");     break;
      case 22: Serial.println("000020");     break;
      case 23: Serial.println("000040");     break;
      case 24: Serial.println("000080");     break;
    }
  } // end millis timer
} // end loop

The interrupt that drives the millis() counter is a little slower than 1 kHz. That means that the millis() counter has to jump by 2 every once in a while to not run slow. I think that is what you are seeing. Perhaps instead of looking for millis() to increase by 1 you could look for micros() to increase by 1000:

void loop()
{
  current_micros =  micros();


  if (current_micros - prev_micros >= 1000)
  {
    prev_micros += 1000;

johnwasser:
The interrupt that drives the millis() counter is a little slower than 1 kHz. That means that the millis() counter has to jump by 2 every once in a while to not run slow. I think that is what you are seeing. Perhaps instead of looking for millis() to increase by 1 you could look for micros() to increase by 1000:

Isn't that exactly what my code does?

Goodmorning,

Just tested my code with micros(); and it did the trick :D, I'm going to use an uno to monitor a Can-bus device that could send data every ms, this code allows me to test the arduino uno and serial communication to my processing gui. Thank you for the great support!

have a nice day all

blh64:
Isn't that exactly what my code does?

Yes. Yes it is.