Go Down

Topic: Need Help With Police Light Modes (Read 2203 times) previous topic - next topic

wally_z

Hello,

I am having quite a bit of trouble getting my code to work the way I would like. I am a volunteer firefighter, and I refuse to spend $200 on a light bar, when I can make one for around $30. This is how it works:

-Turn on, set button state, and mode to 0 "off"
-Once button is pressed, add 1 to the mode
-In mode 0, (the default), all the LED's are off
-In mode 1, the LED's flash a certain pattern (Supposed to loop UNTIL you push the button)
-Every time you push the button, the mode changes
-In mode 2, the LED's flash a different pattern
-In mode 3, the mode is reset to 0, making the LED's stop flashing.

The problem is that when the board is in the first mode, the LED's flash FOREVER, and it cannot change mode. I need it to change mode so I can use different flash patterns. This probably isn't working because of the "while" statements used.

TL;DR I want it to loop until I push the button. I cannot figure out how. It loops forever and won't change mode.

I cannot paste the code into this window because it exceeds the maximum allowed length of a post, so I will be attaching it to this post so you may review it at your pleasure.

Jack Christensen

All those calls to delay() are your enemy. Learn to flash the LEDs without delay(). See the BlinkWithoutDelay example that comes with the Arduino IDE. Also search the forum, there are probably literally hundred of posts about BlinkWithoutDelay.

I'd probably implement it as a state machine, where each button push advances to the next state. Each flashing pattern could be implemented as a separate function.
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

wally_z

How could I make each one as a separate function? I don't really understand what you mean.

Jack Christensen

I just happened to have a board here with a few LEDs, so I threw this together quickly. Not as many LEDs, and just two flashing modes, but just to show the idea. Note there are no calls to delay().

Also note the switch is wired differently from yours, just from the pin to ground, no resistor, since the internal pullup resistor is used instead.

There are three modes: Off, Blinker and Chaser. There is a function for each: ledsOff(), blinker() and chaser().

Each has one argument to indicate initialization, i.e. the first call after changing modes. This is to ensure each mode always starts in a consistent state.

Therefore the state machine has six modes, an init mode for each mode, that only calls the function once, then a run mode for each.

Code: [Select]
#include <Button.h>                            //https://github.com/JChristensen/Button
#define BUTTON_PIN 2                           //wire the button between this pin and ground
#define TACT_DEBOUNCE 25                       //debounce time for tact switches, milliseconds
#define BLINK_INTERVAL 500                     //interval for blinker(), ms
#define CHASE_INTERVAL 200                     //interval for chaser(), ms
int8_t LED[] = { 5, 6, 7, 8 };                 //LEDs connected from these pins to ground, through appropriate current-limiting resistors
#define nLED sizeof(LED)/sizeof(LED[0])        //number of LEDs

Button btnMode = Button(BUTTON_PIN, true, true, TACT_DEBOUNCE);            //instantiate the button
enum MODES {OFF_INIT, OFF, BLINK_INIT, BLINK, CHASE_INIT, CHASE, LAST};    //LAST is not a "real" mode, just used to wrap back to the first mode.
uint8_t MODE;
unsigned long ms;

void setup(void)
{
   for (int i = 0; i < nLED; i++) {
       pinMode(LED[i], OUTPUT);
   }
}

void loop(void)
{
   ms = millis();
   btnMode.read();
   if ( btnMode.wasReleased() )
       if (++MODE >= LAST) MODE = OFF_INIT;
   
   switch (MODE) {
       case OFF_INIT:
           ledsOff(true);
           ++MODE;
           break;
       
       case OFF:
           ledsOff(false);
           break;
       
       case BLINK_INIT:
           blinker(true);
           ++MODE;
           break;
           
       case BLINK:
           blinker(false);
           break;
           
       case CHASE_INIT:
           chaser(true);
           ++MODE;
           break;

       case CHASE:
           chaser(false);
           break;
           
   }        
}

void ledsOff(boolean init)
{
   if (init) {
       for (int i = 0; i < nLED; i++) {
           digitalWrite(LED[i], LOW);
       }
   }
}

void blinker(boolean init)
{
   static unsigned long msLast;
   static boolean ledState;
   
   if (init) {
       msLast = ms;
       ledState = HIGH;
       for (int i = 0; i < nLED; i++) {
           digitalWrite(LED[i], ledState);
       }
   }
   else if (ms - msLast >= BLINK_INTERVAL) {
       msLast = ms;
       ledState = !ledState;
       for (int i = 0; i < nLED; i++) {
           digitalWrite(LED[i], ledState);
       }
   }      
}
   
void chaser(boolean init)
{
   static unsigned long msLast;
   static uint8_t onLED;

   if (init) {
       msLast = ms;
       onLED = 1;
       digitalWrite(LED[0], HIGH);
       for (int i = 1; i < nLED; i++) {
           digitalWrite(LED[i], LOW);
       }
   }
   else if (ms - msLast >= CHASE_INTERVAL) {
       msLast = ms;
       for (int i = 0; i < nLED; i++) {        //iterate through all the LEDs
           if (i == onLED)                     //just turn the current LED on, turn the rest off
               digitalWrite(LED[i], HIGH);
           else
               digitalWrite(LED[i], LOW);
       }
       if (++onLED >= nLED) onLED = 0;         //next LED, or start over with the first LED
   }      

}
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

wally_z

I appreciate the reply, but this doesn't really help me at all. I would rather use delay(), and if I try to use "for()" it just loops until it reaches the certain integer that it is supposed to stop at.

Arrch


I appreciate the reply, but this doesn't really help me at all. I would rather use delay(), and if I try to use "for()" it just loops until it reaches the certain integer that it is supposed to stop at.

If you insist on using delays, then you'll have to liter your code if a bunch of selection statements checking for a state change (implemented through interrupts). That would be a pretty poor way to implement it, though. Another option is to tie the button to reset, and store the state change in EEPROM. On startup, it pulls the data from memory, increments it, and goes into the loop. That method would work if you plan on doing nothing else with the micro.

I'm not sure what you mean by the for() loops. Yes that's what it does, why is that a problem?

Jack Christensen

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

AWOL

Quote
I would rather use delay()

There's no free lunch, I'm afraid; delay() is conceptually simple, but because it stop the processor doing anything except respond to interrupts, you can't write simple but responsive software.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

wally_z

The only things the Arduino should be doing is blinking the lights, and changing state when the button is pressed. I don't need it to be doing anything else, so delay() works fine for me in this situation.

Using "for()" is not the way i want to go. It will only loop until it reaches that certain integer. I need it to loop continuously until the buttonMode variable changes.

AWOL

#9
May 11, 2012, 02:13 pm Last Edit: May 11, 2012, 02:16 pm by AWOL Reason: 1
Quote
The only things the Arduino should be doing is blinking the lights, and changing state when the button is pressed.

That's two things, and whilst you're in delay(), you can do only one thing (which is sit twiddling thumbs), so the button goes unnoticed.

Code: [Select]
Using "for()" is not the way i want to go. It will only loop until it reaches that certain integer.
As the old song goes, 'tain't necessarily so. Here's a for loop that just goes on and on.
Code: [Select]
for (;;)
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

wildbill

#10
May 11, 2012, 02:29 pm Last Edit: May 11, 2012, 02:31 pm by wildbill Reason: 1
Since you are apparently in NJ, unless you are a fire chief, as far as I know, you may not use red lights - just blue.

Edit: It's certainly that way in my town

wally_z

@wildbill
The blue lights in my area are legal. As long as they are only blue. I only plan on using blue lights. I don't really want to pay a fine, or anything like that.

Quote
As the old song goes, 'tain't necessarily so. Here's a for loop that just goes on and on.
Code: [Select]
for (;;)


The question is, will it stop looping once the state is changed? I will be home in ~6 hours to test it. I'll let you know if it works properly. If it doesn't, then I will consider using BlinkWithoutDelay because as a few of you have said, delay() stops everything until that delay period is over.

So I'm guessing I would implement the "for()" statement as such:
Code: [Select]
while (buttonMode == 1){  //Do I even need the while if the "for()" statement is there?
for(;;){
digitalWrite(ledFrontLeft, HIGH);
delay(50); //Yes, I know. I'm using delays until I can figure out how to use BlinkWithoutDelay
digitalWrite(ledFrontLeft, LOW);
delay(50);
}
}

AWOL

Quote
because as a few of you have said, delay() stops everything until that delay period is over.

No, not just a few of us; EVERYONE says that.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

wally_z

At one point in troubleshooting, I had the flashing patterns in their own "void()" statement, and tried looping like that. I might just redo that to shorten my code.

wildbill

A fundamental issue here is that all your modes have loops like this:
Code: [Select]

while (buttonMode == 2){ //All LED's are off.
digitalWrite(ledFrontLeft, LOW);
digitalWrite(ledFrontRight, LOW);
digitalWrite(ledFogLightLeft, LOW);
digitalWrite(ledFogLightRight, LOW);
digitalWrite(ledBackLeft, LOW);
digitalWrite(ledBackRight, LOW);
digitalWrite(ledReverseLeft, LOW);
digitalWrite(ledReverseRight, LOW);
}

There is nothing in the body of the while loop that will change buttonMode, hence those loops never exit. You need to either restructure you code as Jack suggested (good) or check the button within each of your while loops (bad)

Go Up