Help with converting a pulsing input to a solid input when using a for loop and adding an off delay

I am working on a project for a friend that wants to tap into his directional's on his car to drive some leds and sequence them. I have ran into a problem with the code. I have a simple for loop that is going to sequence leds to be on that will repeat when the turn signal is high. The problem is the directional input signal to the Arduino is pulsing on and off every one second as expected from the source. That does not work well with the timing for the (for loop) sequence. I need the for loop to keep running until the input signal stays low for two seconds. It was at this point I realized that I cannot use a Millis() to watch this input to go low for two seconds because the for loop is blocking running the leds. I did try embedding the Millis() time to poll in the (for loop) but it still goes low then high which restarts the loop and not just keep it on. If it is not possible I can use a 555 timer on the input to accomplish the delay off time but I would rather keep it simple. Any ideas? I would post code but there is nothing to even post that remotely works with the few lines I had.

I'm pretty sure millis will be the answer. Post what you have so far.

Every time you see the turn signal go high, or turn on the light, set a flag (turning signal true) AND a timer.

When the timer exceeds 2 seconds, clear that flag.

The flag will then follow the turn signal switching by the user, but remain high.

Except: it will sometimes take 1.999 seconds for your flag signal to go false after the turn signal switch is switched by the user.

Better: go into the car electronics a bit deeper and derive your signal from the same switch that turns the blinkers on and off. No need to deal with the periodic nature of the signal you are currently attempting to exploit, and no lag when the user turns off the signal at his switch for the blinkers.

a7

Good point about going to the source. I will see if he has wiring diagrams. On the other thing about setting flags. That seems to be the issue. The issue is when it is in a for loop for cycling the leds no other code can be ran outside that for the for loop to detect the signal going low. That is what I am running up against. Unless I am missing something. I am already using my interrupts pins for something else so that is out. Bummer. I will see if I can get to the source or might have to build a 555 off delay.

Thanks for helping.

No no no! You won't need interrupts and I forbid you from introducing a '555 timer IC as a kludge-around.*

Slow your roll for a beat or two and look into "doing two things at once" and "blink without delay" and a few other very good and well known and widely used techniques for getting this to work using software only, whether you work off the periodic signal or one that is stable for the duration.

The idea is to esxpress all processor activities in a manner that does not block and tie up your program - no blind for loops &c.

Post the code you are working with now, we understand it doesn't do what you want, but it is hard to offer pointy advice without seeing the context.

*The '555, as a pulse detector, will have the same issues as watching the actual lamp turning on and off - no way to know for as long as 1.99999 seconds that the blinking has stopped…

Oh, welcome to the community! :expressionless:

a7

Haha I Understand. I will try to access my kludge code and post it after lunch. Stay tuned. :laughing:
Keep in mind I am still new to coding in this way so some terminology or suggestions do not mean anything to me yet.

Thanks

OK, holding my breath. Not.

Blink multiple LEDs:


Do several things at once:

Google is your friend for odd terminology or code concepts. Add Arduino and/or C/C++ to you searches to get a bit closer to truths you can use.

a7

Here is the progress code. It somewhat works. I am using delays but in a for loop I don't understand any other way to use a delay without using delay. for loops block anyways don't they?

int ledState = LOW;
unsigned long ledStarted = 0;
const long interval = 4000;
int ledArray[3] {3, 5, 6};
int buttonPin = 2;

void setup() {
  pinMode(ledArray[0], OUTPUT);
  pinMode(ledArray[1], OUTPUT);
  pinMode(ledArray[2], OUTPUT);
  pinMode (buttonPin, INPUT);
  pinMode (LED_BUILTIN, OUTPUT); // To see if off delay works. Onboard led
  Serial.begin (9600);
}


void loop() {

  int button = digitalRead(buttonPin);
  unsigned long currentMillis = millis();


  if (button == HIGH) {
    ledState = HIGH;
    ledStarted = currentMillis;
    digitalWrite(LED_BUILTIN, ledState);
    // ledStarted = currentMillis;

  }



  if (currentMillis - ledStarted >= interval && button == LOW) {
    ledState = LOW;
    digitalWrite(LED_BUILTIN, ledState);
  }



  if (ledState == HIGH) {

    for (int i = 0; i <= 255; i++) {
      analogWrite (ledArray[0], i);
      delay (1);
    }

    for (int i = 0; i <= 255; i++) {
      analogWrite (ledArray[1], i);
      delay (1);
    }

    for (int i = 0; i <= 255; i++) {
      analogWrite (ledArray[2], i);
      delay (1);
    }

    for (int i = 0; i <= 2; i++) {
      analogWrite (ledArray[0], 0);
      analogWrite (ledArray[1], 0);
      analogWrite (ledArray[2], 0);
      delay (400);
    }


  }
}

OK I’m out the door going to the beach this fine day, but…

You want to look into and learn up on FSM “finite state machine” techniques.

The classic over done example is traffic lights, googling

traffic lights arduino state machine

or variations will lead you to many examples. Try a few each for a few minutes to see if it is at an appropriate level in a tone you can tolerate and spend some time.

The idea is to not have any for loop at all, rather a function you can call over and over*. The function itself keeps track of where it is in a sequence ( like I am fading up the second LED at 47 now, so make it 48) and take the next (small!) step towards accomplish the larger goal.

Often FSMs are implemented with a switch/case. If you haven’t used the switch statement in C, there is no time like now to take a side look at that too.

*No for loop because loop() itself is called over and over and does the looping part.

a7

Lucky you. You must live in a warmer climate. It's getting to cold here to enjoy the outside.
This is when I plant myself in my lab to start tweaking on things.

Thanks so much. I have used switch cases. I will take a look at the information you provided and try to study up on it. I get what your saying about for loops. I would like to not use them but often find myself using them because of the simplicity it brings but it does have a cost.

I think I have come up with a way to do the non blocking sequences from your suggestions. Is this something like what you are talking about? It does seem to work.

The project code is not complete. This is just a sample of the led sequencing part to show how it works.

if (currentMillis - setMillis1 >= interval_1 && flag_1 == 0) {
          setMillis1 = currentMillis;
          setMillis2 = currentMillis;
          analogWrite (LEFT_LEDS[ledNumber], 255);
          ledNumber = ledNumber + 1;
        }
        if (ledNumber == 4) {
          flag_1 = 1;
          analogWrite (LEFT_LEDS[0], 0);
          analogWrite (LEFT_LEDS[1], 0);
          analogWrite (LEFT_LEDS[2], 0);
        
        }

        if (currentMillis - setMillis2 >= interval_2 && flag_1 == 1) {
          ledNumber = 0;
          flag_1 = 0;
        }
int ledState = LOW;
unsigned long ledStarted = 0;
const long interval = 4000;
int ledArray[3] {3, 5, 6};
int buttonPin = 2;

void setup() {
  pinMode(ledArray[0], OUTPUT);
  pinMode(ledArray[1], OUTPUT);
  pinMode(ledArray[2], OUTPUT);
  pinMode (buttonPin, INPUT);
  pinMode (LED_BUILTIN, OUTPUT); // To see if off delay works. Onboard led
  Serial.begin (9600);
}


void loop() {

  int button = digitalRead(buttonPin);
  unsigned long currentMillis = millis();


  if (button == HIGH) {
    ledState = HIGH;
    ledStarted = currentMillis;
    digitalWrite(LED_BUILTIN, ledState);
    // ledStarted = currentMillis;

  }

  if (currentMillis - ledStarted >= interval && button == LOW) {
    ledState = LOW;
    digitalWrite(LED_BUILTIN, ledState);
    analogWrite (ledArray[0], 0);
    analogWrite (ledArray[1], 0);
    analogWrite (ledArray[2], 0);
  }

  if (ledState == HIGH) {

    //0 to 255
    static unsigned long blinkMillis = ledStarted;
    unsigned long elapsedMillis = currentMillis - blinkMillis;
    //Serial.println(elapsedMillis);
    //for (int i = 0; i <= 255; i++) {
    if (elapsedMillis <= 255) {
      int i = elapsedMillis;
      analogWrite (ledArray[0], i);
      delay (1);
    }

    //256 to 511
    //for (int i = 0; i <= 255; i++) {
    if (elapsedMillis >= 256 && elapsedMillis <= 511) {
      int i = elapsedMillis - 256;
      analogWrite (ledArray[1], i);
      delay (1);
    }

    //512 to 767
    //for (int i = 0; i <= 255; i++) {
    if (elapsedMillis >= 512 && elapsedMillis <= 767) {
      int i = elapsedMillis - 512;
      analogWrite (ledArray[2], i);
      delay (1);
    }

    //768 to 1968
    //for (int i = 0; i <= 2; i++) {
    if (elapsedMillis >= 768 && elapsedMillis <= 1968) {
      int i = elapsedMillis - 512;
      analogWrite (ledArray[0], 0);
      analogWrite (ledArray[1], 0);
      analogWrite (ledArray[2], 0);
      delay (400);
    }

    if (elapsedMillis > 1968) {
      blinkMillis = millis();
    }



  }
}

Hey thanks. That works pretty well. I will study what you did here and try to understand your structure.

1 Like

I forgot to ask. What does the delay 1 ms do? I know it is a delay but what does it provide to the code?

My mistake. Actually all the delays should be commented out. Let me correct that.

int ledState = LOW;
unsigned long ledStarted = 0;
const long interval = 4000;
int ledArray[3] {3, 5, 6};
int buttonPin = 2;

void setup() {
  pinMode(ledArray[0], OUTPUT);
  pinMode(ledArray[1], OUTPUT);
  pinMode(ledArray[2], OUTPUT);
  pinMode (buttonPin, INPUT);
  pinMode (LED_BUILTIN, OUTPUT); // To see if off delay works. Onboard led
  Serial.begin (9600);
}


void loop() {

  int button = digitalRead(buttonPin);
  unsigned long currentMillis = millis();


  if (button == HIGH) {
    ledState = HIGH;
    ledStarted = currentMillis;
    digitalWrite(LED_BUILTIN, ledState);
    // ledStarted = currentMillis;

  }

  if (currentMillis - ledStarted >= interval && button == LOW) {
    ledState = LOW;
    digitalWrite(LED_BUILTIN, ledState);
    analogWrite (ledArray[0], 0);
    analogWrite (ledArray[1], 0);
    analogWrite (ledArray[2], 0);
  }

  if (ledState == HIGH) {

    //0 to 255
    static unsigned long blinkMillis = ledStarted;
    unsigned long elapsedMillis = currentMillis - blinkMillis;
    //Serial.println(elapsedMillis);
    //for (int i = 0; i <= 255; i++) {
    if (elapsedMillis <= 255) {
      int i = elapsedMillis;
      analogWrite (ledArray[0], i);
      //delay (1);
    }

    //256 to 511
    //for (int i = 0; i <= 255; i++) {
    if (elapsedMillis >= 256 && elapsedMillis <= 511) {
      int i = elapsedMillis - 256;
      analogWrite (ledArray[1], i);
      //delay (1);
    }

    //512 to 767
    //for (int i = 0; i <= 255; i++) {
    if (elapsedMillis >= 512 && elapsedMillis <= 767) {
      int i = elapsedMillis - 512;
      analogWrite (ledArray[2], i);
      //delay (1);
    }

    //768 to 1968
    //for (int i = 0; i <= 2; i++) {
    if (elapsedMillis >= 768 && elapsedMillis <= 1968) {
      int i = elapsedMillis - 512;
      analogWrite (ledArray[0], 0);
      analogWrite (ledArray[1], 0);
      analogWrite (ledArray[2], 0);
      //delay (400);
    }

    if (elapsedMillis > 1968) {
      blinkMillis = millis();
    }



  }
}

That is great. I like the simulator you showed me. I will have to save that site. I have two questions.

  1. I am not seeing how the delay between each sequences work. When all the leds are off.

  2. Is there a way to make sure all the leds go through the sequence? It seems sometimes it doesn't finish the sequence and leaves one or two on before the delay times out.

A somewhat different approach I took. This one uses a frame animation based idea and one call to millis() sets the timing for everything.

Too lazy burned out to comment much on it.

I noticed this in your code

    for (int i = 0; i <= 2; i++) {
      analogWrite (ledArray[0], 0);
      analogWrite (ledArray[1], 0);
      analogWrite (ledArray[2], 0);
      delay (400);
    }

You might have meant

    for (int i = 0; i <= 2; i++)
      analogWrite (ledArray[i], 0);
    
    delay (1200);

or you missed the whole point of the array!

a7

I didn't miss the point of the array. You said stay away from for loops and delays. Thanks I will check out your example.

"The idea is to esxpress all processor activities in a manner that does not block and tie up your program - no blind for loops &c."

for (int i = 0; i <= 2; i++) // <---------------------for loop
analogWrite (ledArray[i], 0);

delay (1200);    //   <-----------------------delay

Well you kinda did. Sorry for trying to cover two things in one post.

This, your original loop

    for (int i = 0; i <= 2; i++) {
      analogWrite (ledArray[0], 0);
      analogWrite (ledArray[1], 0);
      analogWrite (ledArray[2], 0);
      delay (400);
    }

does three times set three LEDs to 0. It d0es not use the loop for selecting the LEDs - they are referred to by constants. Nine (9) calls to analogWrite().

The delay(400) is invoked each of the three times. Once after the LEDs are first turned off, and twice more as the three LEDs are turned off. Again. They already off, so.

Read that code above carefully. It is exactly

      analogWrite (ledArray[0], 0);
      analogWrite (ledArray[1], 0);
      analogWrite (ledArray[2], 0);
      delay (400);

      analogWrite (ledArray[0], 0);
      analogWrite (ledArray[1], 0);
      analogWrite (ledArray[2], 0);
      delay (400);

      analogWrite (ledArray[0], 0);
      analogWrite (ledArray[1], 0);
      analogWrite (ledArray[2], 0);
      delay (400);

This

    for (int i = 0; i <= 2; i++)
      analogWrite (ledArray[i], 0);
    
    delay (1200);

uses the for loop variable to select and turn off one LED at a time, each time through the loop. It then goes on to call delay(1200), once. It uses the array nature and [i] indexing.

The delay(1200) was supposed to be a joke which certainly did not land... since there was a delay(400) that got called three times in your original, I placed one delay of 1200 after the loop, outside the loop syntactically. It gets executed once, hence the 3*400 to make up for the fact that yours called it three times…

I'm not trying to pick on you, it just looked like you had missed the point of using an array and a loop - expressing something iterative in a way other than writing out multiple lines of almost identical code.

Perhaps not important or valuable yet. But if you had 200 LEDs, look where this might lead you:

    for (int i = 0; i < 200; i++) {
      analogWrite (ledArray[0], 0);
      analogWrite (ledArray[1], 0);
      analogWrite (ledArray[2], 0);
      analogWrite (ledArray[3], 0);
      analogWrite (ledArray[4], 0);
      analogWrite (ledArray[5], 0);
      analogWrite (ledArray[6], 0);
      analogWrite (ledArray[7], 0);
.
.
.
.
     analogWrite (ledArray[197], 0);
     analogWrite (ledArray[198], 0);
     analogWrite (ledArray[199], 0);
}

Which would 200 times turn off all 200 LEDs, 40000 calls to analogWrite() you might start noticing hand cramps from wiring all that code and sluggishness as your processor dutifully did things it had already done over and over and over.

for loops are not a problem. Unless they take too long to finish. It can mean too many iterations, or iterations that take too long. One sure way to make a step in a for loop take longer (too long?) is to plant a fat delay() in there.

So delay() is the bad guy usually, not the looping expressions.

Sry and HTH

a7

No problem. I am still getting a grasp on things. I have been trying to learn this type of coding on and off for the past few years with little leaps of improvement then putting it aside. Not much time to devote to it. I program in ladder logic for work on large PLC controllers and HMI's. The whole structured text style is a way different form for me. Luckily some things are very similar, like data types so that helps a little. In PLC land the whole timer thing is not an issue. It does not block. Getting my head around how the microcontroller deals with it has been a learning curve.

The post you referenced about the array was my first try, which was as you said. I goofed when rushing to get the code up for help.

So the second approach is not great as well? I know you guys are way more advanced than me in this department. I am only posting what I can comprehend and make work at the moment.

if (currentMillis - setMillis1 >= interval_1 && flag_1 == 0) {
          setMillis1 = currentMillis;
          setMillis2 = currentMillis;
          analogWrite (LEFT_LEDS[ledNumber], 255);
          ledNumber = ledNumber + 1;
        }
        if (ledNumber == 4) {
          flag_1 = 1;
          analogWrite (LEFT_LEDS[0], 0);
          analogWrite (LEFT_LEDS[1], 0);
          analogWrite (LEFT_LEDS[2], 0);
        
        }

        if (currentMillis - setMillis2 >= interval_2 && flag_1 == 1) {
          ledNumber = 0;
          flag_1 = 0;
        }