Using millis() multiple times

Hello,

I've a annoying problem for you. I want to build a model railroad decoder that simulates serveral kinds of lamps, eg. a vapour lamp or a fluorescent lamp. So I have multiple timers on multiple times. I use the
Model Railroading with Arduino library for decoding the bus signal and set up parameters.

my current code (that doesn't really work) is attached.

Please take a look at the for loop. It should walk through the 10 ports with leds wired onto them and decides which function (eg. vapor lamp, case 3) should be simulated. But this code isn't working. Should I use two dimensional arrays for previousMillis[]? Is there a way to start easy a timer based on millis only once ( see: case 3: // vapor lamp
if ( break_[1] == LOW ) {
)? There's also a promlem: How could I set break_[1] to LOW if I want to stop the light and switch on again without influencing the other leds?
Is there a timer function possible which is easy to use like delay()?
Is there a way for using an operating system on the AtMega 328?

Please help!

Yours, Stefan

lightdecoder.ino (10.7 KB)

post code

seems like you could use a single array to keep track of it all, but its hard to tell whats happening without seeing it

void loop() {
  long currentMillis = millis();
  
  // You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
  Dcc.process();

  brightness[0] = Dcc.getCV(513);
  brightness[1] = Dcc.getCV(514);
  brightness[2] = Dcc.getCV(515);
  brightness[3] = Dcc.getCV(516);
  brightness[4] = Dcc.getCV(517);
  brightness[5] = Dcc.getCV(518);
  brightness[6] = Dcc.getCV(519);
  brightness[7] = Dcc.getCV(520);
  brightness[8] = Dcc.getCV(521);
  brightness[9] = Dcc.getCV(522);

  mode[0] = Dcc.getCV(523);
  mode[1] = Dcc.getCV(524);
  mode[2] = Dcc.getCV(525);
  mode[3] = Dcc.getCV(526);
  mode[4] = Dcc.getCV(527);
  mode[5] = Dcc.getCV(528);
  mode[6] = Dcc.getCV(529);
  mode[7] = Dcc.getCV(530);
  mode[8] = Dcc.getCV(531);
  mode[9] = Dcc.getCV(532);


  
  if( FactoryDefaultCVIndex && Dcc.isSetCVReady())
  {
    FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array 
    Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV, FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
  }

  for ( i = 0; i < 10; i++ ) {
    if ( set_port[i] == HIGH ) {
      switch ( mode[i] ) {
        case 0:  // on/off
          SoftPWMSet(port[i], brightness[i]);
        break;
        case 1: // flicker tube damaged
          random_value[0] = random(200, 1000);
          random_value[1] = random(200, 1000);
  
          if ( currentMillis > previousMillis[0] ) {
            SoftPWMSet(port[i], brightness[i]) ;
            previousMillis[0] = currentMillis + random_value[0];
          }

          if ( currentMillis > previousMillis[1] ) {
            SoftPWMSet(port[i], 0) ;
            previousMillis[1] = currentMillis + random_value[1];
          }
        break;  
        case 2: // welding
          random_value[2] = random(50, 100);
          random_value[3] = random(50, 100);
          random_value[4] = random(500, 10000);

          if ( currentMillis > previousMillis[2] && break_[0] == HIGH) {
            SoftPWMSet(port[i], brightness[i]) ;
            previousMillis[2] = currentMillis + random_value[2];
          }

          if ( currentMillis > previousMillis[3] ) {
            SoftPWMSet(port[i], 0);
            previousMillis[3] = currentMillis + random_value[3];
          }

          if ( currentMillis > previousMillis[4] ) {
            break_[0] ^= 1;
            previousMillis[4] = currentMillis + random_value[4];
          }
        break;
        case 3: // vapor lamp
          if ( break_[1] == LOW ) {
            SoftPWMSet(port[i], 255);
            previousMillis[5] = currentMillis + 20;
            break_[1] = HIGH;
            break_[2] = LOW;
            j = 0;
          }
          if ( currentMillis > previousMillis[5] && j < brightness[i] && break_[2] == LOW ) {
            SoftPWMSet(port[i], j++ );
            previousMillis[5] = currentMillis + 200;
          }
          if ( j == brightness[i] ) {
            break_[2] = HIGH;
          }
        break;
        case 4: // flourescent light
          if ( break_[3] == LOW ) {
            SoftPWMSet(port[i], 255);
            previousMillis[6] = currentMillis + 100;
            break_[3] = HIGH;
            break_[4] = LOW;
          }
          if ( currentMillis > previousMillis[6] && break_[4] == LOW) {
            SoftPWMSet(port[i], 0 );
            previousMillis[7] = currentMillis + 300;
            break_[4] = HIGH;
            break_[5] = LOW;
          }          
          if ( currentMillis > previousMillis[7] && break_[5] == LOW) {
            SoftPWMSet(port[i], brightness[i] );
            break_[5] = HIGH;
          }
        break;
        case 5: // 5 + time(max 2,5s)=blink
          if ( currentMillis > previousMillis[8]) {
            if ( last[0] == 0 ) {
              last[0] = brightness[i];
              SoftPWMSet(port[i], last[0] );
            }
            else {
              last[0] = 0;
              SoftPWMSet(port[i], last[0] );
            }
            previousMillis[8] = currentMillis + 500;
          }          
        break;
        case 6: // 5 + time(max 2,5s)=blink
          if ( currentMillis > previousMillis[9]) {
            if ( last[0] == 0 ) {
              last[0] = brightness[i];
              SoftPWMSet(port[i], last[0] );
            }
            else {
              last[0] = 0;
              SoftPWMSet(port[i], last[0] );
            }
            previousMillis[9] = currentMillis + 1000;
          }          
        break;
        case 7: // 5 + time(max 2,5s)=blink
          if ( currentMillis > previousMillis[10]) {
            if ( last[0] == 0 ) {
              last[0] = brightness[i];
              SoftPWMSet(port[i], last[0] );
            }
            else {
              last[0] = 0;
              SoftPWMSet(port[i], last[0] );
            }
            previousMillis[10] = currentMillis + 1500;
          }          
        break;
        case 8: // 5 + time(max 2,5s)=blink
          if ( currentMillis > previousMillis[11]) {
            if ( last[0] == 0 ) {
              last[0] = brightness[i];
              SoftPWMSet(port[i], last[0] );
            }
            else {
              last[0] = 0;
              SoftPWMSet(port[i], last[0] );
            }
            previousMillis[11] = currentMillis + 2000;
          }          
        break;
        case 9: // 5 + time(max 2,5s)=blink
          if ( currentMillis > previousMillis[12]) {
            if ( last[0] == 0 ) {
              last[0] = brightness[i];
              SoftPWMSet(port[i], last[0] );
            }
            else {
              last[0] = 0;
              SoftPWMSet(port[i], last[0] );
            }
            previousMillis[12] = currentMillis + 2500;
          }          
        break;
      }
    }
    else {
      SoftPWMSet(port[i], 0);     
    }
    old_port[i] = set_port[i];
  }
if ( currentMillis > previousMillis[19] ) {

      Serial.print("set_port[0] :");
      Serial.println(set_port[0], DEC);
      Serial.print("break_[1] :");
      Serial.println(break_[1], DEC);
      
      previousMillis[19] = currentMillis + 1500;
}
  
  
}

I don't want to sound difficult, but I don't understand that. What are you doing exactly? Just explain in your own words.

void loop() {
  long currentMillis = millis(); //take the current runtime
  
  // You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
  Dcc.process(); //run the nmra dcc library (decodes  bus signal - I'm getting the set_port[] variable set

  brightness[0] = Dcc.getCV(513); //read the brightness from the eeprom using the nmra dcc lib
  brightness[1] = Dcc.getCV(514);
  brightness[2] = Dcc.getCV(515);
  brightness[3] = Dcc.getCV(516);
  brightness[4] = Dcc.getCV(517);
  brightness[5] = Dcc.getCV(518);
  brightness[6] = Dcc.getCV(519);
  brightness[7] = Dcc.getCV(520);
  brightness[8] = Dcc.getCV(521);
  brightness[9] = Dcc.getCV(522);

  mode[0] = Dcc.getCV(523); // read the mode (0=on/off, 1=flicker fluorescent lamp damaged, 2=welding, 3=vapor lamp, 4=fluorescent lamp, 5 + time(max 2,5s)=blink, 11=nothing
  mode[1] = Dcc.getCV(524);
  mode[2] = Dcc.getCV(525);
  mode[3] = Dcc.getCV(526);
  mode[4] = Dcc.getCV(527);
  mode[5] = Dcc.getCV(528);
  mode[6] = Dcc.getCV(529);
  mode[7] = Dcc.getCV(530);
  mode[8] = Dcc.getCV(531);
  mode[9] = Dcc.getCV(532);


  
  if( FactoryDefaultCVIndex && Dcc.isSetCVReady())
  {
    FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array 
    Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV, FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
  }

  for ( i = 0; i < 10; i++ ) { //walk through the ports
    if ( set_port[i] == HIGH ) { // go on if defined port goes high
      switch ( mode[i] ) { //decide what mode to use
        case 0:  // on/off
          SoftPWMSet(port[i], brightness[i]);
        break;
        case 1: // flicker tube damaged, goes on/off randomly
          random_value[0] = random(200, 1000); //first random value
          random_value[1] = random(200, 1000); //second random value
  
          if ( currentMillis > previousMillis[0] ) { //if time is come
            SoftPWMSet(port[i], brightness[i]) ; // set to port the brightness on the led
            previousMillis[0] = currentMillis + random_value[0]; //calculate the new time for running the timer
          }

          if ( currentMillis > previousMillis[1] ) {
            SoftPWMSet(port[i], 0) ;
            previousMillis[1] = currentMillis + random_value[1];
          }
        break;  
        case 2: // welding - you know? random flashes of one led
          random_value[2] = random(50, 100);
          random_value[3] = random(50, 100);
          random_value[4] = random(500, 10000);

          if ( currentMillis > previousMillis[2] && break_[0] == HIGH) {
            SoftPWMSet(port[i], brightness[i]) ;
            previousMillis[2] = currentMillis + random_value[2];
          }

          if ( currentMillis > previousMillis[3] ) {
            SoftPWMSet(port[i], 0);
            previousMillis[3] = currentMillis + random_value[3];
          }

          if ( currentMillis > previousMillis[4] ) {
            break_[0] ^= 1;
            previousMillis[4] = currentMillis + random_value[4];
          }
        break;
        case 3: // vapor lamp, starts with a flash and then the brightness goes slowly to max brightness
          if ( break_[1] == LOW ) { //be shure to run once (the flash at the beginning)
            SoftPWMSet(port[i], 255); //set the led port to maximum
            previousMillis[5] = currentMillis + 20; //set the new timer time
            break_[1] = HIGH; //be shure this timer runs only once - I have problems with this how to start this case again with set_port[i] == true
            break_[2] = LOW; //set the second timer
            j = 0; // couter variable for fading
          }
          if ( currentMillis > previousMillis[5] && j < brightness[i] && break_[2] == LOW ) { //start the second timer if time is come and brightness isn't maximum
            SoftPWMSet(port[i], j++ ); //increase the brightness
            previousMillis[5] = currentMillis + 200; // set the next time the second timer should run
          }
          if ( j == brightness[i] ) { //stop the second timer, brightness maximum is reached
            break_[2] = HIGH;
          }
        break;
        case 4: // flourescent light, flashes once, then full light, like case 3 but simpler
          if ( break_[3] == LOW ) {
            SoftPWMSet(port[i], 255);
            previousMillis[6] = currentMillis + 100;
            break_[3] = HIGH;
            break_[4] = LOW;
          }
          if ( currentMillis > previousMillis[6] && break_[4] == LOW) {
            SoftPWMSet(port[i], 0 );
            previousMillis[7] = currentMillis + 300;
            break_[4] = HIGH;
            break_[5] = LOW;
          }          
          if ( currentMillis > previousMillis[7] && break_[5] == LOW) {
            SoftPWMSet(port[i], brightness[i] );
            break_[5] = HIGH;
          }
        break;
        case 5: // 5 + time(max 2,5s)=blink
          if ( currentMillis > previousMillis[8]) {
            if ( last[0] == 0 ) {
              last[0] = brightness[i];
              SoftPWMSet(port[i], last[0] );
            }
            else {
              last[0] = 0;
              SoftPWMSet(port[i], last[0] );
            }
            previousMillis[8] = currentMillis + 500;
          }          
        break;
        case 6: // 5 + time(max 2,5s)=blink
          if ( currentMillis > previousMillis[9]) {
            if ( last[0] == 0 ) {
              last[0] = brightness[i];
              SoftPWMSet(port[i], last[0] );
            }
            else {
              last[0] = 0;
              SoftPWMSet(port[i], last[0] );
            }
            previousMillis[9] = currentMillis + 1000;
          }          
        break;
        case 7: // 5 + time(max 2,5s)=blink
          if ( currentMillis > previousMillis[10]) {
            if ( last[0] == 0 ) {
              last[0] = brightness[i];
              SoftPWMSet(port[i], last[0] );
            }
            else {
              last[0] = 0;
              SoftPWMSet(port[i], last[0] );
            }
            previousMillis[10] = currentMillis + 1500;
          }          
        break;
        case 8: // 5 + time(max 2,5s)=blink
          if ( currentMillis > previousMillis[11]) {
            if ( last[0] == 0 ) {
              last[0] = brightness[i];
              SoftPWMSet(port[i], last[0] );
            }
            else {
              last[0] = 0;
              SoftPWMSet(port[i], last[0] );
            }
            previousMillis[11] = currentMillis + 2000;
          }          
        break;
        case 9: // 5 + time(max 2,5s)=blink
          if ( currentMillis > previousMillis[12]) {
            if ( last[0] == 0 ) {
              last[0] = brightness[i];
              SoftPWMSet(port[i], last[0] );
            }
            else {
              last[0] = 0;
              SoftPWMSet(port[i], last[0] );
            }
            previousMillis[12] = currentMillis + 2500;
          }          
        break;
      }
    }
    else {
      SoftPWMSet(port[i], 0);     
    }
    old_port[i] = set_port[i];
  }
if ( currentMillis > previousMillis[19] ) {

      Serial.print("set_port[0] :");
      Serial.println(set_port[0], DEC);
      Serial.print("break_[1] :");
      Serial.println(break_[1], DEC);
      
      previousMillis[19] = currentMillis + 1500;
}
  
  
}

I've added comments, and hope you could help me to do this right!

Thanks.

Not sure what you are doing but you are not using arrays correctly for example:-

brightness[0] = Dcc.getCV(513); //read the brightness from the eeprom using the nmra dcc lib
  brightness[1] = Dcc.getCV(514);
  brightness[2] = Dcc.getCV(515);
  brightness[3] = Dcc.getCV(516);
  brightness[4] = Dcc.getCV(517);
  brightness[5] = Dcc.getCV(518);
  brightness[6] = Dcc.getCV(519);
  brightness[7] = Dcc.getCV(520);
  brightness[8] = Dcc.getCV(521);
  brightness[9] = Dcc.getCV(522);

should be:-

for(int i=0; i<10; i++) brightness[i] = Dcc.getCV(513 +i);

Likewise:-

mode[0] = Dcc.getCV(523);
  mode[1] = Dcc.getCV(524);
  mode[2] = Dcc.getCV(525);
  mode[3] = Dcc.getCV(526);
  mode[4] = Dcc.getCV(527);
  mode[5] = Dcc.getCV(528);
  mode[6] = Dcc.getCV(529);
  mode[7] = Dcc.getCV(530);
  mode[8] = Dcc.getCV(531);
  mode[9] = Dcc.getCV(532);

can be replaced with:-

for(int i=0; i<10; i++) mode[i] = Dcc.getCV(523 + i);

Is there a way to start easy a timer based on millis only once

No, but there is no need, you simply copy the value in millis to your own variable as a mark to measure everything else.

Is there a way for using an operating system on the AtMega 328?

There is but please don't go there, operating system on boards such as an arduino is death.

You code does seem backwards, you are stepping through each state with a case statement, where as you should be scanning through timers to see if it is time to do anything and doing it if it is.

Is there a way to start easy a timer based on millis only once

No, but there is no need, you simply copy the value in millis to your own variable as a mark to measure everything else.[/quote]

Could you add a example please?
And what about my break[1] problem (see commented code), any other ideas?

Is there a way for using an operating system on the AtMega 328?
There is but please don't go there, operating system on boards such as an arduino is death.

You code does seem backwards, you are stepping through each state with a case statement, where as you should be scanning through timers to see if it is time to do anything and doing it if it is.

Could you please explain the last sentence you wrote, because I don't understand it.

Should I take two dimensional array for walking throuh the cases? I think if one port has eg mode[3] active one other mode[4] and the the next also mode[3] that the currentMillis[] are interferecing another.

Thanks a lot!

Could you add a example please?

startTimeTask1 = millis();
startTimeTask2 = millis();

then in your loop you simply check if it is time to do task1 and 2
if(millis() - startTimeTask1 > task1rate) {
// do task 1 then reset start time
startTimeTask1 = millis();
}

if(millis() - startTimeTask2 > task2rate) {
// do task 2 then reset start time
startTimeTask2 = millis();
}

Could you please explain the last sentence you wrote, because I don't understand it.

I am not sure how else I can say it. You look should check if it is time to do each of the tasks in the loop function. Do it with out using arrays with just two or three tasks before you delve into using arrays.

Millis() is just a way of accessing a counter that increments every millisecond. if I have a statement - someVar - millis(); then I have just loaded someVar with the value of millis() at that instant.

So I have a set of devices that I want to do something with, say turn off after a certain amount of time, will all devices being a different time -

When I turned them on I had a bunch of variable - either in an array or just simple variables -

when I turned them on -
dev1time = millis() +1000; // on for 1 second
dev2time = millis() +3000; //on for 3 seconds
dev3time = millis() + 1750; // on for 1.75 seconds

The in the code to turn them off -

if(dev1time > millis()) { // code to turn off dev1) }
if(dev2time > millis()) { // code to turn off dev2) }
if(dev3time > millis()) { // code to turn off dev3) }

You have 1 timer, but you are just using it as a reference, not tieing it to a specific task. Kind of like the clock on the wall - you look at it and say, in 15 minutes I need to leave for work, you kid looks at it and sees that they have an hour to kill, and you wife looks at it and says in 3 hours I go shopping. Same idea with millis().