Serial.print using millis() instead of delay

I have a project where I want to Serial.print at different times - for example Serial.print('r'), wait one second, then print 's', wait a half a second, then print 't', wait two seconds, and so forth. It's easy with delays, but can't figure out using millis(). Here's the code:

unsigned long currentMillis = 0;
unsigned long previousMillis = 0;

unsigned int time1 = 461;

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

void loop() {

 currentMillis = millis();
 if (Serial.available()) {
   int characterRead = Serial.read();



   if (characterRead == '1') {
     quarter1('r');
     quarter1('s');
     quarter1('t');
     quarter1('f');
     


   }
 }
}

void sixteenth1(char letter) {
 if (currentMillis - previousMillis >= time1/4) {
 Serial.println(letter);
   previousMillis = currentMillis;
 }
}

void sixteenth1(char letter) {
 if (currentMillis - previousMillis >= time1/2) {
 Serial.println(letter);
   previousMillis = currentMillis;
 }
}


void quarter1(char letter) {
 if (currentMillis - previousMillis >= time1) {
 Serial.println(letter);
   previousMillis = currentMillis;
 }
}

void half1(char letter) {
 if (currentMillis - previousMillis>= time1 * 2) {
   Serial.println(letter);
   previousMillis = currentMillis;
 }
}

void full1(char letter) {
 if (currentMillis - previousMIllis >= time1 * 4) {
   Serial.println(letter);
   previousMillis = currentMillis;
 }
}

When I run the code, only the first 'r' prints.

I suspect that each of these

previousMillis = currentMillis;

needs to be

previousMillis = millis();

so that it keeps moving on

And please use the code button </>

so your code looks like this

and is easy to copy to a text editor

…R

It's easy with delays, but can't figure out using millis(). Here's the code:

How would YOU type a letter 10 seconds after typing one, given a pencil, paper, and a stopwatch?

Pretty simple, isn't it? Start the stopwatch. Type a character. Write down the time you did that. Periodically, see if now (according to the stopwatch) minus then (when you last typed a character) exceeds some threshold. If it does, type another character and record the time.

Well, millis() takes the place of the stopwatch, and variables and assignment statements take the place of the pencil and paper.

unsigned int time1 = 461;

Anything involving time is unsigned long, not int.

Note, in the above description, the "periodically" part. Not immediately, but over and over. You are NOT checking, over and over, that it is time to send another character (or note).

So, essentially, if I think in terms of a stopwatch, it's this:

if (millis() = 0) { Serial.print('r'); }

if (millis() = 461) { Serial.print('q'); } and so forth, correct?

Let the compiler give you some nice messages for that.

Lookup assignment and comparison operators in C.

Read and try to understand the documentation of millis().

Retry to write code that reflects at least a bit of the given suggestions.

:sob:

midascott: So, essentially, if I think in terms of a stopwatch, it's this:

if (millis() = 0) { Serial.print('r'); }

if (millis() = 461) { Serial.print('q'); } and so forth, correct?

Well if you want to know how long since the board was powered that works. But what if you want the time between two events? Like to get the time between the last time you printed and now?

Let me ask a question that might make it clearer for you. If at the last print millis was 461 and right now millis gives 861, how many milliseconds have passed since the last print? How did you calculate that?

Use == in the if statements.

you can either write 4 custom timers that will either all record the same beginning time then print as each finishes

a=250ms b=500ms c=1000ms d=2000ms

or you can run one timer that resets at the end of every print and changes the interval

a=250ms b=250ms after a c=500ms after b d=2000ms after c

or you could take a timer and make a counter lets say 250ms

a=1 b=2 c=4 d=8

planning is harder than the code.

midascott: So, essentially, if I think in terms of a stopwatch, it's this:

The problem is that you cannot know what is the current value of millis() so you have to rely on differences. Did my suggestion in Reply #1 not help?

What happened when you tried it?

...R

Well I tried this, but still not working. It just prints r and q simultaneously instead of a second apart and doesn't print K or L. Am I at least on the right track?

unsigned long currentMillis = 0;
unsigned long previousMillis = 0;


unsigned long time1 = 1000;    //time interval for one beat at 92.5bpm

void setup() {
  Serial.begin(9600);
}

void loop() {
currentMillis = millis();

  if (Serial.available()) {
    int characterRead = Serial.read();

    

    if (characterRead == '1') {

      Serial.print('r');
      if (currentMillis - previousMillis >= time1) {
        Serial.print ('q');
        previousMillis = currentMillis;
      }
      if (currentMillis - previousMillis >= time1) {
        Serial.print ('K');
        previousMillis = currentMillis;
        
      }
      if (currentMillis - previousMillis >= time1) {
        previousMillis = currentMillis;
        Serial.print('L');
        
      }
    }
  }
}

You're getting closer. You just need to think about logic now. What do you think currentMillis - previousMillis will equal immediately after you set previousMillis equal to currentMillis? Remember that the loop function runs over and over and over again really fast, it doesn't wait for that if statement to be true.

You need some sort of way to remember which letter you are up to printing. Right now, anytime it has been more than time1 since you printed q and reset previousMillis, you print q and reset previousMillis. I think you only want to print q once.

a few ways to play with millis

your code with notes in it.

unsigned long currentMillis = 0;
unsigned long previousMillis = 0;


unsigned long time1 = 1000;    //time interval for one beat at 92.5bpm

void setup() {
  Serial.begin(9600);
}

void loop() {
currentMillis = millis();

  if (Serial.available()) {
    int characterRead = Serial.read();

    

    if (characterRead == '1') {

      Serial.print('r');
      if (currentMillis - previousMillis >= time1) {
        Serial.print ('q');
        previousMillis = currentMillis;// this line reset the time stamp 
        //all the "if" are using the same time stamp
        //so you ended up with the same code as
        
        
        //print this all the time
        // Serial.print('r');
        
        //if (currentMillis - previousMillis >= time1) {
          //print this when time is done
        //  Serial.print ('q');
       // Serial.print ('K');
       //Serial.print('L');
       // previousMillis = currentMillis;
      }
      if (currentMillis - previousMillis >= time1) {
        Serial.print ('K');
        previousMillis = currentMillis;
        
      }
      if (currentMillis - previousMillis >= time1) {
        previousMillis = currentMillis;
        Serial.print('L');
        
      }
    }
  }
}

a different way that’s not very neat

unsigned long currentMillis = 0;
unsigned long previousMillis1 = 0;
unsigned long previousMillis2 = 0;
unsigned long previousMillis3 = 0;
unsigned long previousMillis4 = 0;
unsigned long time1 = 250;    //time interval for one beat at 92.5bpm
unsigned long time2 = 500;
unsigned long time3 = 1000;
unsigned long time4 = 2000;

void setup() {
  Serial.begin(9600);
}

void loop() {
  currentMillis = millis();

  if (Serial.available()) {
    int characterRead = Serial.read();



    if (characterRead == '1') {
      if (currentMillis - previousMillis1 >= time1) {
        Serial.print ('r');

      }
      if (currentMillis - previousMillis2 >= time2) {
        Serial.print ('q');

      }
      if (currentMillis - previousMillis3 >= time3) {
        Serial.print ('K');


      }
      if (currentMillis - previousMillis4 >= time4) {

        Serial.print('L');
        previousMillis1 = currentMillis;
        previousMillis2 = currentMillis;
        previousMillis3 = currentMillis;
        previousMillis4 = currentMillis;
      }
    }
  }
}

another way using a switch

unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
byte printThis=0;

unsigned long time1 = 1000;    //time interval for one beat at 92.5bpm

void setup() {
  Serial.begin(9600);
}

void loop() {
  currentMillis = millis();

  if (Serial.available()) {
    int characterRead = Serial.read();


    if (characterRead == '1') {
      if (currentMillis - previousMillis >= time1) {
        printThis++;
        previousMillis = currentMillis;
      }
      switch (printThis) {
        case 0:
          time1 = 250;
          break;
        case 1:
          Serial.print('r');
          time1 = 500;
          break;
        case 2:
          Serial.print ('q');
          time1 = 500;
          break;
        case 3:
          Serial.print ('K');
          time1 = 500;
          break;
        case 4:
          Serial.print('L');
          time1 = 500;
          printThis = 0;
          break;
        default :
          printThis = 0;
          break;
      }
    }
  }
}

these are examples to show how to use millis not a recommendation. Like I said earlier a good plan will tell you how to write the timers

midascott: Well I tried this, but still not working.

So you did not try what I suggested in Reply #1

...R

Robin2: So you did not try what I suggested in Reply #1

...R

Here's the code again, with your suggestion, replacing currentMillis with millis(). With this change, it prints 'q', but nothing else.

unsigned long previousMillis = 0;


unsigned long time1 = 1000;    //time interval for one beat at 92.5bpm

void setup() {
  Serial.begin(9600);
}

void loop() {


  if (Serial.available()) {
    int characterRead = Serial.read();

   

    if (characterRead == '1') {

     
      if (millis()- previousMillis >= 461) {
        Serial.print ('q');
        previousMillis = millis();
      }
      if (millis()- previousMillis >= 230) {
        Serial.print ('K');
        previousMillis = millis();
       
      }
      if (millis() - previousMillis >= 922) {
        previousMillis = millis();
        Serial.print('L');
       
      }
    }
  }
}

gpop1: a few ways to play with millis

Your switch case example looks like it might work with some tweaking. Thanks for the examples!

midascott:
Here’s the code again, with your suggestion, replacing currentMillis with millis(). With this change, it prints ‘q’, but nothing else.

Thanks for trying that. I think I had the wrong idea (a regular occurrence when debugging).

Looking again at your original Post I think you need a slightly more complex solution. At the moment that code reads Serial in each iteration of loop() but many (hundreds or thousands) of iterations of loop() will be needed until all the values have been printed. And for that to happen it must NOT take account of a new value from Serial.read() until all the values have been printed. Something like this is needed

   if (characterRead == '1' && printing == false) {
        printing = true;
    }
    
    if (printing == true) {
        quarter1('r');
        quarter1('s');
        quarter1('t');
        quarter1('f');
    }

However I don’t think even that is sufficient - but try it.

I have not figured out where in the code to set printing back to false.

…R

Thank you all for your suggestions! I got it to do what I wanted. Here's the code:

unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
int state = 0;
int beat = 648;
byte printThis = 0;

unsigned long time1 = 648;    //time interval for one beat at 92.5bpm

void setup() {
  Serial.begin(9600);
}

void loop() {
  currentMillis = millis();

  if (Serial.available()) {
    int characterRead = Serial.read();

    if (characterRead == '1') {
      state = 1;
    }
    if (characterRead == '2') {
      state = 0;
      Serial.print('p');
    }
  }
  useSerialData();
}


void useSerialData() {
  if (state != 1) {
    printThis = 0;
  }

  if (state == 1) {
    if (currentMillis - previousMillis >= time1) {



      switch (printThis) {
        case 0:
          Serial.print('/');
          Serial.print('5');
          Serial.print('x');
          time1 = beat * 16;
          break;
        case 1:
          Serial.print('~');
          Serial.print('/');
          Serial.print('4');
          Serial.print('Q');
          time1 = beat;
          break;
        case 2:
          Serial.print('!');
          Serial.print('5');
          Serial.print('Q');
          time1 = beat;
          break;
        case 3:
          Serial.print('@');
          Serial.print('6');
          Serial.print('Q');
          time1 = beat;
          break;
        case 4:
          Serial.print('#');
          Serial.print('9');
          Serial.print('Q');
          time1 = beat;
          break;
        case 5:
          Serial.print('~');
          Serial.print('/');
          Serial.print('4');
          Serial.print('Q');
          time1 = beat;
          break;
        case 6:
          Serial.print('!');
          Serial.print('5');
          Serial.print('Q');
          time1 = beat;
          break;
        case 7:
          Serial.print('@');
          Serial.print('6');
          Serial.print('Q');
          time1 = beat;
          break;
        case 8:
          Serial.print('#');
          Serial.print('9');
          Serial.print('Q');
          time1 = beat;
          break;
        case 9:
          Serial.print('p');
          state = 0;
          break;

      }
      printThis++;
      previousMillis = currentMillis;
    }
  } else {
    state = 0;
  }
}