Go Down

Topic: controlling a traffic advisor lightbar (Read 150 times) previous topic - next topic

Duey1234

Hi All,
First post so please be gentle!

I have an Arduino Nano that I'm trying to code like a traffic advisor light bar (basically, a light bar with multiple flash modes)

I know the Arduino won't be able to drive the light bar directly, the outputs will be used to drive transistors in the finished item (for personal use, won't be sold) but I'm using some 5mm green LED's just to see the pin state.

I would like it to change patterns using a mode button and to remember the state it was last in when powered on again.

I may have cut out too much of the code below, but it's currently 230 lines long!
If you think I've cut out too much, let me know and I'll put up the full, unedited code.

Note: I've tried both EEPROM.write & EEPROM.set, both have the same results.

Code: [Select]


#include <EEPROM.h>
const int LED1 = 9;
const int LED2 = 10;
const int LED3 = 11;
const int LED4 = 12;
const int Wait = 250;
const int buttonPin = 6;
int ButtonPushCount = EEPROM.read(1); // I will be using address 1 of the EEPROM for mode memory & selection.
int ButtonState = 0; // Button State
int LastButtonState = 0; // Last Button State

void setup() {
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(LED4, OUTPUT);
  pinMode(buttonPin, INPUT);
  digitalWrite(LED1, LOW);
  digitalWrite(LED2, LOW);
  digitalWrite(LED3, LOW);
  digitalWrite(LED4, LOW);
}

void loop() {
  ButtonState = digitalRead(buttonPin);
  if (ButtonState != LastButtonState) {
    if (ButtonState == HIGH) {
      ButtonPushCount++;
    }
    delay(50);
    LastButtonState = ButtonState;
    if (ButtonPushCount >= 4) {
      ButtonPushCount = 0;
    }
  }
  if (EEPROM.read(1) == 0) { // sample flash pattern below. the 2 consecutive delays are because other patterns need half the delay and I didn't see the point in declaring 2 constants.
    digitalWrite(LED1, HIGH);
        digitalWrite(LED2, HIGH);
        digitalWrite(LED3, LOW);
        digitalWrite(LED4, LOW);
        delay(Wait);
        delay(Wait);
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, HIGH);
        digitalWrite(LED4, HIGH);
        delay(Wait);
        delay(Wait);
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, LOW);
        digitalWrite(LED3, LOW);
        digitalWrite(LED4, LOW);
    ButtonState = digitalRead(buttonPin);
    if (ButtonState != LastButtonState) {
      if (ButtonState == HIGH) {
        ButtonPushCount++;
        EEPROM.put(1, ButtonPushCount);
      }
      delay(50);
      LastButtonState = ButtonState;
      if (ButtonPushCount >= 4) {
        ButtonPushCount = 0;
      }
    }
    if (EEPROM.read(1) == 1) {
      // same code as above, different flash pattern
    }

    if (EEPROM.read(1) == 2) {
      // same code as above, different flash pattern
    }

    if (EEPROM.read(1) == 3) {
      // same code as above, different flash pattern
    }
  }
}




As I'm trying to troubleshoot the code myself at the moment, there is a bunch of serial.println that I have removed from this code, but what it has told me is that it is writing to, and reading from the EEPROM fine, but at any time that the number stored in the EEPROM isn't a 0, it has no code to run, so it just keeps reading the EEPROM and waiting for a button press to change the EEPROM value back to 0, where it will then run the first flash pattern again.

buttonPin is pulled to ground via a 220ohm resistor (the only value I have to hand) and I'm using the built in 3.3v output as the + to pull the pin high when pressed via a momentary switch.

Duey1234

After troubleshooting some more, it looks like I'd simply got a } in the wrong place. After the code of ButtonPushCount = 0 in the first flash pattern; I only had 2 close tags. I needed to have 3, to take it out of that flash pattern loop!

Something so simple!

after that, I had to change the range from 0 - 3, to 1 - 4, but it now works

The only thing I'd like to change now is for it to accept a button press at any time, regardless of whether it's in the middle of a flash pattern or not. I don't mind if it finishes the current flash pattern before moving to the next one. Hopefully, I don't have to put the button checking code after every LED status change.

Grumpy_Mike

#2
Nov 25, 2017, 12:19 am Last Edit: Nov 25, 2017, 12:20 am by Grumpy_Mike
What you have to do is to either
Write your code as a state machine.
Or
Do not use the delay function but write your own using a while loop and the mills function. But include a bit of code that terminates the delay when it sees the button being pressed. Then at the start of the code that chooses the flash function you put a block where you hold until the button is released.

Duey1234

Hi Mike,
Thanks for taking the time to reply on a Friday evening, and black Friday, no less!

This is only the second code I've written, and I've never written in C or C++ before, so I'm not really sure what you actually mean.

Could you possibly show me an example code that I could work from?

Grumpy_Mike

You need to replace all the delay calls in your code to commutableDelay. Here is a framework that combines the commutableDelay definition, with state change button press counting.

Code: [Select]

// commutableDelay with change state detection example by Grumpy_Mike Nov 2017
// change button is wired correctly - that is between input pin and ground
// note - remove the // to enable printing so you can see what is happening
// to extend this to more options alter the variable "countLimit" and add extra cases to the switch statement

byte changePin = 8; // or any other pin you want
byte buttonLastState;
byte buttonState;
byte count = 0;
byte countLimit = 1;
byte ledPin = 13;

void setup() {
Serial.begin(9600);
pinMode(changePin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
//Serial.println("code starting");
}

void loop() {
  //Serial.println("start of loop");
  buttonState = digitalRead(changePin);
  if(buttonState == LOW && buttonLastState == HIGH){ // we have a new press
       //Serial.println("button pressed");
       while(digitalRead(changePin) == LOW) {delay(50);} // do nothing until button is released
       //Serial.println("button released");
       count += 1 ; // move onto next count
       if(count > countLimit) count = 0; // wrap round count
       //Serial.print("count on button ");Serial.println(count);
  }
  else {
    buttonLastState = buttonState; // keep a note of the last button state
  }
  // now see what function we want to call depending on the value in count
  switch(count) {
     case 0:
           flash1();
           break;
     case 1:
           flash2();
           break;
  }
}

// flash functions - these are just oversimplified examples
void flash1(){
  for(int i = 0; i<20; i++){
    digitalWrite(ledPin, HIGH); // turn LED on
    commutableDelay(1000); // one second delay
    digitalWrite(ledPin, LOW); // turn LED off
    commutableDelay(1000); // one second delay
   }
}

void flash2(){
  for(int i = 0; i<20; i++){
    digitalWrite(ledPin, HIGH); // turn LED on
    commutableDelay(200); // delay
    digitalWrite(ledPin, LOW); // turn LED off
    commutableDelay(300); // delay
   }
}

void commutableDelay(int time){ // this will return when the delay time has expired or the change button is pushed.
unsigned long timeLimit, startTime;
startTime = millis(); // the millis count when we start
timeLimit = time; // the time interval we want put in a long int variable type
boolean keepGoing = true;

while(millis() - startTime < timeLimit && keepGoing == true) { // keep the loop going until one or other of these conditions fail
    if ( digitalRead(changePin) == LOW){  // if you see the button being held down abandon the delay
         keepGoing = false;
         buttonLastState == HIGH;
    }
 }
}

Go Up