Maker Pro
Maker Pro

Arduino control of model train signalling (was: LED Power Supply for Control Panel)

Thanks Steve

Getting one of "these" , Got "That" and " Them"

Thanks for your help

Actually the screw terminal Arduino connection thing was just an idea whilst playing around with the circuits but the final boards will be pcb etched as i know how to do that lol.

Mark
Happy Times Ahead I Hope
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Now go here and get the latest version.

Depending on how motivated you are, you can start writing programs to see if they will compile. Mind you, that's a bit like watching paint dry.

You can take a look at some of the simpler examples (the LED flashing one is the classic) to see what is required to write simple code.
 
Depending on how motivated you are, you can start writing programs to see if they will compile. Mind you, that's a bit like watching paint dry.


Highly Motivated :) Not for me....bit like watching paint dry and wondering why the heck it won't dry.....lol

You can take a look at some of the simpler examples (the LED flashing one is the classic) to see what is required to write simple code.[/QUOTE]

Simple ones don't have debounce circuits and other variables to consider lol

Mark

BTW
ArduinoNanoBreadboard.jpg

Didn't realize the nano fitted onto the Breadboard like this... now i understand the response to the screw terminal sheild i was gonna get that you talked me out of :) Makes more sense
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
If you don't need to consider a previous state, then you don't have to worry about debouncing switches.

In addition you can do some debouncing in hardware (even if only initially) and later add it to the software. But, I'll grant that doing it in software is probably the path to take from the beginning.

There are some software tricks that will make all of this easier. I can show you some.

There's a few simple concepts to get your head around with arduino programming that pretty much don't even require you to know how to program.

Understanding them will help you figure out where things need to go.

Let us know when you have the development environment up and running and I'll run through a few with you.

Do you have any programming experience at all? If so, what?
 
If you don't need to consider a previous state, then you don't have to worry about debouncing switches.

In addition you can do some debouncing in hardware (even if only initially) and later add it to the software. But, I'll grant that doing it in software is probably the path to take from the beginning.

There are some software tricks that will make all of this easier. I can show you some.

There's a few simple concepts to get your head around with arduino programming that pretty much don't even require you to know how to program.

Understanding them will help you figure out where things need to go.

Let us know when you have the development environment up and running and I'll run through a few with you.

Do you have any programming experience at all? If so, what?

No Programming Experience what so ever apart from setting the dvd recorder lmao
 
So this is what i have so far

// Project Eridge Sequence Control Panel - Part 1

// Part 1 - Dispatching Trains from Fiddle Yards to Main Layout.

// Objective - Using a series of pushbuttons and LEDs to communicate to different layout operators the correct sequence of operation.

// 1. Operator 1 will inform Operator 2 that the Train is Ready for Dispatch by pressing a pushbutton (Ready Dispatch) this will illuminate an Amber LED on both Operator 1 and 2 control panels and cancel Ready for Next Service.
// 2. Operator 2 will confirm Train Accepted by pressing pushbutton (Train Accepted), cancelling the output described above, and this will illuminate a Green LED on both Operators Control Panels.
// 3. Operator 2 will notify Operator 1 when Ready for Next service by pressing a pushbutton (Ready Next Service) this will illuminate an Orange LED on both operator Control Panels and switch all above outputs to low (restarting sequence)




//Program starts here:

// constants won't change. They're used here to set pin numbers

const int buttonReadyDispatch = 2; // Assign Ready Dispatch Pushbutton Pin2
const int buttonTrainAccepted = 3; // Assign Train Accepted Pushbutton Pin 3
const int buttonReadyNextService = 4; // Assign Ready Next Service Pushbutton Pin 4

const int ledReadyDispatch = 11; // Assign Amber LED Train Ready fotr Dispatch Pin 11
const int ledTrainAccepted = 12; // Assign Green LED Train Accepted Pin 12
const int ledReadyNextService = 13; // Assign Orange LED Ready Next Service Pin 13


// variables will change:
int buttonState = 0; // variable for reading the pushbutton state


void setup()
{
// Sets the pushbuttons as Input:

pinMode(buttonReadyDispatch, INPUT);
pinMode(buttonTrainAccepted, INPUT);
pinMode(buttonReadyNextService, INPUT);

// Sets the LEDs as Outputs:

pinMode(ledReadyDispatch, OUTPUT);
pinMode(ledTrainAccepted, OUTPUT);
pinMode(ledReadyNextService, OUTPUT);

// LED StartUp Test, Light All LEDs and Switch Off with 1 sec delay between each one

digitalWrite (buttonReadyDispatch, HIGH);
delay(2000);
digitalWrite (buttonReadyDispatch, LOW);

digitalWrite (buttonTrainAccepted, HIGH);
delay(3000);
digitalWrite (buttonTrainAccepted, LOW);

digitalWrite (buttonReadyNextService, HIGH);
delay(4000);
digitalWrite (buttonReadyNextService, LOW);

}

void loop()
{
// Informing program that LEDs are ON only when assigned button is HIGH

// read the state of the Ready for Dispatch pushbutton value:

buttonState = digitalRead(buttonReadyDispatch);

digitalWrite(ledReadyDispatch, buttonState);


// read the state of the Train Accepted pushbutton value:

buttonState = digitalRead(buttonTrainAccepted);

digitalWrite(ledTrainAccepted, buttonState);

// read the state of the Ready for Next Service pushbutton value:

buttonState = digitalRead(buttonReadyNextService);

digitalWrite(ledReadyNextService, buttonState);


// informing Program to cancel (set Output LOW) certain pushbutton Outputs when other pushbuttons Outputa are HIGH


// Objective 1. - Operator 1 will inform Operator 2 that the Train is Ready for Dispatch by pressing a pushbutton (Ready Dispatch) this will illuminate an Amber LED on both Operator 1 and 2 control panels and cancel Ready for Next Service.
// i.e When buttonReadyDispatch is HIGH then buttonReadyNextService must be LOW; also buttonTrainAccepted must be LOW:

// read the state of the Ready for Dispatch pushbutton value:

buttonState = digitalRead(buttonReadyDispatch);

// if it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn Ready Train Accepted pushbutton off:
digitalWrite(buttonTrainAccepted, LOW); }

buttonState = digitalRead(buttonReadyDispatch);

// if it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn Ready Next Service pushbutton off:
digitalWrite(buttonReadyNextService, LOW); }



//Objective 2. - Operator 2 will confirm Train Accepted by pressing pushbutton (Train Accepted), Cancelling the Ready Dispatch pushbutton and Ready Dispatch LED output to LOW:
// i.e When buttonTrainAccept is HIGH then buttonReadyDispatch must be LOW

// read the state of the Train Accepted pushbutton value:
buttonState = digitalRead(buttonTrainAccepted);

// if it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn Ready Dispatch pushbutton off:
digitalWrite(buttonReadyDispatch, LOW); }


buttonState = digitalRead(buttonTrainAccepted);

// if it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn Ready Next Service pushbutton off:
digitalWrite(buttonReadyNextService, LOW); }



// Objective 3. - Operator 2 will notify Operator 1 when Ready for Next service by pressing a pushbutton (Ready Next Service) this will illuminate an Orange LED on both operator Control Panels and switch all Other outputs to low (restarting sequence)
// i.e When buttonreadyNextservice is HIGH then buttonReadyDispatch and buttonTrainAccepted must be LOW

// read the state of the Ready for Next Service pushbutton value:
buttonState = digitalRead(buttonReadyNextService);

// if it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn Ready Dispatch pushbutton off:
digitalWrite(buttonReadyDispatch, LOW); }

// if it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn ReadyDispatch pushbutton off:
digitalWrite(buttonTrainAccepted, LOW);

}



}


Apology for Narrative there for my benefit only
NO Debounce added Yet

Also need to add command to the program thatsets prerequistes on push button switches....my sketch at present still means that any of the push buttons can be operated by the user and something will happen. It needs a sequence of events.

By which i mean if there are 3 push buttons, (button1, button2, button3). How do i tell the program that button 1 must be HIGH before button 2 can be HIGH, Button2 Must be HIGH before Button 3 can be High.


Mark

Hopefully My Hours learning have not been wasted
 
Last edited:
Was Gonna Add the Following Statement for Debounce to the above sketch


// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0; // the last time the output pin was toggled
long debounceDelay = 50; // the debounce time; increase if the output flickers

void setup() {

}

void loop() {
// Debounce statement for pushbutton Ready Dispatch

// read the state of the switch into a local variable:
int reading = digitalRead(buttonReadyDispatch);

// check to see if you just pressed the button
// (i.e. the input went from LOW to HIGH), and you've waited
// long enough since the last press to ignore any noise:

// If the switch changed, due to noise or pressing:
if (reading != lastButtonState) {
// reset the debouncing timer
lastDebounceTime = millis();
}

if ((millis() - lastDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state:
buttonState = reading;
}


// save the reading. Next time through the loop,
// it'll be the lastButtonState:
lastButtonState = reading;
}

// Debounce statement for pushbutton Train Accepted

// read the state of the switch into a local variable:
int reading = digitalRead(buttonTrainAccepted);

// check to see if you just pressed the button
// (i.e. the input went from LOW to HIGH), and you've waited
// long enough since the last press to ignore any noise:

// If the switch changed, due to noise or pressing:
if (reading != lastButtonState) {
// reset the debouncing timer
lastDebounceTime = millis();
}

if ((millis() - lastDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state:
buttonState = reading;
}


// save the reading. Next time through the loop,
// it'll be the lastButtonState:
lastButtonState = reading;

}
// Debounce statement for pushbutton Ready Next Service

// read the state of the switch into a local variable:
int reading = digitalRead(buttonReadyNextService);

// check to see if you just pressed the button
// (i.e. the input went from LOW to HIGH), and you've waited
// long enough since the last press to ignore any noise:

// If the switch changed, due to noise or pressing:
if (reading != lastButtonState) {
// reset the debouncing timer
lastDebounceTime = millis();
}

if ((millis() - lastDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state:
buttonState = reading;
}


// save the reading. Next time through the loop,
// it'll be the lastButtonState:
lastButtonState = reading;
}


One thing i'm not sure of is can this be simplified so one debounce statement covers the 3 buttons i have ?


Mark
 
Arduino Arrived

:)
Got my Arduino Nano so have been playing around with it...well learning i guess

So using pull down resistor circuit for the MO push buttons, I have managed to Latch each of the MO push buttons via programming and have them illuminate the LED's with the addition of a resistor.

const int buttonA = 8;
const int buttonB = 7;
const int buttonC = 6;

const int ledA = 11;
const int ledB = 10;
const int ledC = 9;

boolean lastButtonA = LOW;
boolean currentButtonA = LOW;
boolean ledOnA = false;

boolean lastButtonB = LOW;
boolean currentButtonB = LOW;
boolean ledOnB = false;

boolean lastButtonC = LOW;
boolean currentButtonC = LOW;
boolean ledOnC = false;

void setup()
{
pinMode(buttonA, INPUT);
pinMode(buttonB, INPUT);
pinMode(buttonC, INPUT);
pinMode(ledA, OUTPUT);
pinMode(ledB, OUTPUT);
pinMode(ledC, OUTPUT);

}

boolean debounceA(boolean lastA)

{
boolean currentA = digitalRead(buttonA);
if (lastA != currentA)
{
delay(5);
currentA = digitalRead(buttonA);
}
return currentA;
}

boolean debounceB(boolean lastB)
{
boolean currentB = digitalRead(buttonB);
if (lastB != currentB)
{
delay(5);
currentB = digitalRead(buttonB);
}
return currentB;
}

boolean debounceC(boolean lastC)

{
boolean currentC = digitalRead(buttonC);
if (lastC != currentC)
{
delay(5);
currentC = digitalRead(buttonC);
}
return currentC;
}
void loop()
{
// Debounce for Button A

currentButtonA = debounceA(lastButtonA);
if (lastButtonA == LOW && currentButtonA == HIGH)
{
ledOnA =!ledOnA;
}
lastButtonA = currentButtonA;
digitalWrite(ledA, ledOnA);

// Debounce for Button B

currentButtonB = debounceB(lastButtonB);
if (lastButtonB == LOW && currentButtonB == HIGH)
{
ledOnB =!ledOnB;
}
lastButtonB = currentButtonB;
digitalWrite(ledB, ledOnB);

// Debounce for Button C

currentButtonC = debounceC(lastButtonC);
if (lastButtonC == LOW && currentButtonC == HIGH)
{
ledOnC =!ledOnC;
}
lastButtonC = currentButtonC;
digitalWrite(ledC, ledOnC);

}

Now i've come unstuck a bit and need some direction to:

Cancel LED A when LED B is illuminated
Cancel LED B when LED C is illuminated
Cancel LED C when LED A is illuminated

Also i tried to alter the LED to blinking on LED A but whilst i can achieve the blink it prevents the push button from working, presumably cause of the debounce circuit and the fact the output is switching High, LOW to create the LED blinking.


Any advice would be appreciated

Mark
pleased with results so far in such a short time :)
Arduino Test Circuit1.jpg.jpg
 
Last edited:

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Congratulations, you seem to be doing *very* well.

Here are some pointers:

1) You should aim code that has *no* delay statements.
2) Separate the inputs (and maybe outputs) from the logic.
3) Make the code generic.

So how do you remove delay statements? Well, it looks like the debounce code in the code previous to last didn't have any, but your current code does.

The first thing to do is to treat the debouncing as a test which is carried out at a point in time, not over a period of time. The best way to do that is to restate the problem.

From here on in, you should retain all of this stuff as documentation of your work. You can probably put it in a block of comments in your code -- at least you won't lose it there. Doing this will mean that if you have problems you can get back into your head *exactly* what you were thinking and why.

A statement of the function we wish to perform:

We will no longer try to debounce the switch. We will, instead, only register a change of state if it has not changed for a certain time.

So, what do we know for each button? [and some names]

a) The current state (do we say it is currently on or off) [state]
b) The current input value (was it on or off last time we looked?) [lastVal]
c) The current input value (is it on or off now?) [val]
d) The last time we checked [lastTime]
e) The current time. [time]
f) The time the input state must remain unchanged to effect a change in state. [dbTime]

And what is the key value (or values) we want to set

g) The new state (do we say it is now on or off) [newState]

So now we can rewrite " We will only register a change of state if it has not changed for a certain time" first as an algorithm, then in pseudocode, then as a procedure.

If the [lastVal] is different to the [val] then set [lastTime] to [time] and [newState] to [state], otherwise if [time] exceeds [lastTime] by at least [dbTime] then set [newState] to [val].

We look at that, and we think about it, and it seems to say what we meant above, but we note that lastVal never changes. Clearly we've missed something.

At the end we need to add:

In all cases, set [lastVal] to [Val]

And now let's write that in something that looks like code, but without worrying too much about syntax (pseudocode)

Code:
if lastVal <> val then
    lastTime <- time
    newState <- state
else
    if (time - lastTime) >= dbTime then
        newState <- val
    endif
endif
lastVal <- val
This matches what we have said above.

But it's getting complex enough that we'll add some descriptive comments:

Code:
// We will only register a change of state if the input value has not
// changed for a certain time
if lastVal <> val then           // if the input value has changed
   lastTime <- time              // set the last time it changed to "now"
   newState <- state             // and return the old state
else                             // otherwise (input value has NOT changed)
   if (time - lastTime) >= dbTime then // has it remained stable long enough
     newState <- val             // change the state
  endif
endif
lastVal <- val                  // and retain the current input state
We look at it carefully, and we notice that there is a way through this where [newState] doesn't get a value.

After some thought, we realise that in all cases other than where [newState] receives the current value ([val]) it should remain unchanged. (and maybe we've also notes that we don't need to update [lastVal] unless [val] is different.

So we update the pseudocode to look like this:

Code:
// We will only register a change of state if the input value has not
// changed for a certain time
newState <- state               // by default the new state is the old state
if lastVal <> val then          // if the input value has changed
  lastTime <- time              // set the last time it changed to "now"
  lastVal <- val                // and retain the current input value
else                            // otherwise (input value has NOT changed)
  if (time - lastTime) >= dbTime then // has it remained stable long enough
    newState <- val             // change the state
  endif
endif
So now we can write some code:

But wait! First we need to separate the values our code will change from those it will not.

The ones it changes are on the left side of an assignment. these are

newState, lastVal, lastTime

And remaining unchanged are:

state, val, time, dbTime

newState is the main output of our function, so we can return this. The other variables will be passed to the function in a way that either allows them to be changed, or not...

This pretty much just involves getting the syntax right and adding extra stuff required by the language.

attachment.php


And how would we use that?

Something like this:

SwAState = debounce(SwAState, PinAVal, now, DBTIME, lastPinAVal, lastPinATime);

Now, that's all pretty cool, but it looks like quite a lot. Perhaps you notice that DBTIME is likely to be a global constant, and SwAState is both a parameter and gets the return value, and also that "now" is something we can possibly pick up internally to our function. Oh, and if PinAVal is just the digital value of a pin, why not get that internally too?

We could change our function, but that would make it more complex. So let's write another function which provides a simpler interface.

The requirements are:

A simpler debounce function which takes the digital pin number and sets the state variable using the debounce function.

And what we know:

a) Digital input pin [inPin]
b) state [state] (current AND returned)
c) internally access time [time]
d) internally fetch pin state [val]
e) internally access DBTIME global constant [DBTIME]
f) previous pin value [lastVal]
g) previous pin time [lastTime]

And the pseudocode is

time <- millis() // get the current time
val <- digitalRead(inPin) // get the current pin state
state <- debounce(state, val, time, DBTIME, lastVal, lastTime) // call the debounce routine

In this case, we are going to update the state passed to the function. So what should we return? Well, for various reasons, we should still return the state.

And then we can code it up...

attachment.php


And so how do we call that?

simpleDebounce(pinA, SwAState, lastPinAVal, lastPinATime);

Looks a little smaller, and if we want, we can also do:

if (simpleDebounce(pinA, SwAState, lastPinAVal, lastPinATime) == true) {
...

(We could do it with the previous version, but it uses a construction which looks odd to newcomers and might be misunderstood)

As promised, here is the code (unfortunately unformatted)

Code:
#define DBTIME 25;

bool debounce(bool state, bool val, long time, long dbTime, bool& lastVal, long& lastTime) {
// We will only register a change of state if the input value has not
// changed for a certain time
  bool newState;

  newState = state;         // by default the new state is the old state

  if (lastVal != val) {     // if the input value has changed
    lastTime = time;        // set the last time it changed to "now"
    lastVal = val;          // and retain the current input value
  } else {                  // otherwise (input value has NOT changed) 
    if ((time - lastTime) >= dbTime) { // has it remained stable long enough
      newState = val;       // change the state
    }
  }
  
  return newState;          // This is the new state
}

bool simpleDebounce(int inPin, bool& state, bool& lastVal, long& lastTime) {
// A debounce routine with a simpler interface.
  long time = millis();             // time in milliseconds
  bool val = digitalRead(inPin);    // current pin value
  long dbTime = DBTIME;               // the debounce interval
  
  state = debounce(state, val, time, dbTime, lastVal, lastTime); // debounce the pin
  
  return state;                     // return state
}
And I think I'll end here...
 

Attachments

  • Capture.PNG
    Capture.PNG
    92.3 KB · Views: 310
  • Capture2.PNG
    Capture2.PNG
    75.9 KB · Views: 302
Last edited:

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Part 2 :)

The other thing I mentioned is that you should keep the interface and the logic apart.

So we might do something like this

first a definition of various inputs and outputs. In this case I've grouped them by object rather than function. (there's a reason for this -- they look like an obvious class to me, and that's what I'd make them. However classes are a more advanced topic)

Code:
//
// INPUTS
//

// button A - The big blue one
const int buttonA = 8;
bool lastButtonA;
bool currentButtonA;
long lastButtonATime;

// Button B - the small green one
const int buttonB = 7;
bool lastButtonB;
bool currentButtonB;
long lastButtonBTime;

// Button C - the black one with black writing on a black background
const int buttonC = 6;
bool lastButtonC;
bool currentButtonC;
long lastButtonCTime;

//
// OUTPUTS
//

// LED A - the red one
const int ledA = 11;
bool ledOnA = false;

// LED B - the white one
const int ledB = 10;
bool ledOnB = false;

// LED C - the black one.
const int ledC = 9;
bool ledOnC = false;
I notes I needed to set the output states in more than one place, so I wrote a function. It's before the init code, because I use it in the init code and I prefer to define things before they're used (the compiler doesn't care).

Code:
void writeLEDStates() {
// writes the current states to the LEDs
  digitalWrite(ledA, ledOnA);
  digitalWrite(ledB, ledOnB);
  digitalWrite(ledC, ledOnC);
}

Then I have the setup code. Rather than assuming states for the inputs, I read their current state and assume it has been set that way for some time.

Code:
void setup()
{
  // initialize pins
  pinMode(buttonA, INPUT);
  pinMode(buttonB, INPUT);
  pinMode(buttonC, INPUT);
  
  pinMode(ledA, OUTPUT);
  pinMode(ledB, OUTPUT);
  pinMode(ledC, OUTPUT);
  
  // set output states
  writeLEDStates();
 
  // get the initial state of buttons
  currentButtonA = lastButtonA = digitalRead(buttonA); 
  lastButtonATime = millis();
  
  currentButtonB = lastButtonB = digitalRead(buttonB); 
  lastButtonBTime = millis();
  
  currentButtonC = lastButtonC = digitalRead(buttonC); 
  lastButtonCTime = millis();
}
I don't actually need a function to read the switch states, but I will define it because it makes the program easier to read. And remember, the source is written for you to read. You have to maintain it, so make life easy.

Code:
void readSwitchStates() {
// read the current button states
  simpleDebounce(buttonA, currentButtonA, lastButtonA, lastButtonATime);
  simpleDebounce(buttonB, currentButtonB, lastButtonB, lastButtonBTime);
  simpleDebounce(buttonC, currentButtonC, lastButtonC, lastButtonCTime);
}
Noe the main loop. It's not required to have the while loop in there, that will happen automatically, but I put it there because it helps tell me what's going on.

If I really run out of space I can remove it.

Inside the loop I read the buttons (note that they're debounced), perform the logic, and write the outputs.

Code:
void loop()
{
  while (true) {
    // get input states
    readSwitchStates();

    // do the button logic
    ledOnA = currentButtonA;
    ledOnB = currentButtonB;
    ledOnC = currentButtonC;
    
    // set output states
    writeLEDStates();
  }  
}
So, from looking at this small segment of code you can see everything that happens in a single glance.

And the logic is simple and obvious.

What is it? The LEDs follow the buttons.

:)
Now i've come unstuck a bit and need some direction to:

Cancel LED A when LED B is illuminated
Cancel LED B when LED C is illuminated
Cancel LED C when LED A is illuminated

Also i tried to alter the LED to blinking on LED A but whilst i can achieve the blink it prevents the push button from working, presumably cause of the debounce circuit and the fact the output is switching High, LOW to create the LED blinking.

OK, so your requirements are more complex. But it's just a fairly simple change to the logic.

As in the previous post, I'd advise you to state the requirements (you have almost done this) then determine the information you have, a,d then write pseudocode before cutting actual code. (But I'll leave that as an exercise :D)

So let's add some more requirements first.

1) Pressing a button when its LED is off causes the LED to turn on
2) pressing a button while its LED is on turns it off.
3) Cancel LED A when LED B is illuminated
4) Cancel LED B when LED C is illuminated
5) Cancel LED C when LED A is illuminated

Let's look at how to do that for button A first.

Firstly, turning on the LED

Sounds simple, right?

Code:
    if ((!ledOnA) && (currentButtonA)) {
      ledOnA = true;
    }
if the LED is off, and the button is pressed, turn the LED on. Cool!

And now to turn it off

Code:
    if ((ledOnA) && (!currentButtonA)) {
      ledOnA = false;
    }
If the LED is on and the button is pressed, turn the LED off

Cool, simple, done!

Not quite.

Look again and you'll notice that the LED will turn on and off if you keep the button pressed. Worse than that, it actually won't turn on because as soon as w turn it on we turn it off -- even before we set the LED state.

Ugh!

So we need to be a bit tricky...

Let's think about this.

All we're interested in is the button *being* pressed. We don't care that stays pressed, or is pressed, or gets released.

So what tells us that? Well, if currentButtonA is false and lastButtonA is true then we're pressing the button. And if in this state we set currentButtonA to true, we'll only get one indication.

Something like this should work

Code:
bool pressEdge(bool& state, bool lastVal) {
// detects the leading edge of a button press.
  if ((!state) && lastVal) { // button pressed, but state not yet set
    state = true;            // set the state without waiting for debouncing
    return true;             // return a valid edge
  } else {
    return false;            // it's not a press edge.
  }
}
And now, since we only get one indication, we can do this:

Code:
    if (pressEdge(currentButtonA, lastButtonA)) {
      ledOnA = !ledOnA;
    }
Simple. If we see an indication that the button is being pressed, we toggle the state of the LED

Code:
void buttonLogic() {
// 1) Pressing a button when its LED is off causes the LED to turn on
// 2) pressing a button while its LED is on turns it off.
// 3) Cancel LED A when LED B is illuminated
// 4) Cancel LED B when LED C is illuminated
// 5) Cancel LED C when LED A is illuminated
    if (pressEdge(currentButtonA, lastButtonA)) {
      ledOnA = !ledOnA;
    }
   
    if (pressEdge(currentButtonB, lastButtonB)) {
      ledOnB = !ledOnB;
    }
   
    if (pressEdge(currentButtonA, lastButtonA)) {
      ledOnB = !ledOnB;
    }
}
But that only does steps 1 and 2. We need to do steps 3, 4, and 5.

Where do we put them? The best place is with the code that toggles the LEDs.

Code:
void buttonLogic() {
// 1) Pressing a button when its LED is off causes the LED to turn on
// 2) pressing a button while its LED is on turns it off.
// 3) Cancel LED A when LED B is illuminated
// 4) Cancel LED B when LED C is illuminated
// 5) Cancel LED C when LED A is illuminated
    if (pressEdge(currentButtonA, lastButtonA)) { // rules 1 & 2 for LED A
      ledOnA = !ledOnA;
      
      if (ledOnA) { // rule 5
        ledOnC = false;
      }
    }
   
    if (pressEdge(currentButtonB, lastButtonB)) { // rules 1 & 2 for LED B
      ledOnB = !ledOnB;
      
      if (ledOnB) { // rule 3
        ledOnA = false;
      }
    }
   
    if (pressEdge(currentButtonA, lastButtonA)) { // rules 1 & 2 for LED C
      ledOnB = !ledOnB;
      
      if (ledOnC) { // rule 4
        ledOnB = false;
      }
    }
}
And now, putting it all together we have

Code:
#define DBTIME 25; // number of ms allowed to eliminate key bounce

bool debounce(bool state, bool val, long time, long dbTime, bool& lastVal, long& lastTime) {
// We will only register a change of state if the input value has not
// changed for a certain time
  bool newState;

  newState = state;         // by default the new state is the old state

  if (lastVal != val) {     // if the input value has changed
    lastTime = time;        // set the last time it changed to "now"
    lastVal = val;          // and retain the current input value
  } else {                  // otherwise (input value has NOT changed) 
    if ((time - lastTime) >= dbTime) { // has it remained stable long enough
      newState = val;       // change the state
    }
  }
  
  return newState;          // This is the new state
}

bool simpleDebounce(int inPin, bool& state, bool& lastVal, long& lastTime) {
// A debounce routine with a simpler interface.
  long time = millis();             // time in milliseconds
  bool val = digitalRead(inPin);    // current pin value
  long dbTime = DBTIME;               // the debounce interval
  
  state = debounce(state, val, time, dbTime, lastVal, lastTime); // debounce the pin
  
  return state;                     // return state
}

//
// INPUTS
//

// button A - The big blue one
const int buttonA = 8;
bool lastButtonA;
bool currentButtonA;
long lastButtonATime;

// Button B - the small green one
const int buttonB = 7;
bool lastButtonB;
bool currentButtonB;
long lastButtonBTime;

// Button C - the black one with black writing on a black background
const int buttonC = 6;
bool lastButtonC;
bool currentButtonC;
long lastButtonCTime;

//
// OUTPUTS
//

// LED A - the red one
const int ledA = 11;
bool ledOnA = false;

// LED B - the white one
const int ledB = 10;
bool ledOnB = false;

// LED C - the black one.
const int ledC = 9;
bool ledOnC = false;

void writeLEDStates() {
// writes the current states to the LEDs
  digitalWrite(ledA, ledOnA);
  digitalWrite(ledB, ledOnB);
  digitalWrite(ledC, ledOnC);
}

void setup()
{
  // initialize pins
  pinMode(buttonA, INPUT);
  pinMode(buttonB, INPUT);
  pinMode(buttonC, INPUT);
  
  pinMode(ledA, OUTPUT);
  pinMode(ledB, OUTPUT);
  pinMode(ledC, OUTPUT);
  
  // set output states
  writeLEDStates();
 
  // get the initial state of buttons
  currentButtonA = lastButtonA = digitalRead(buttonA); 
  lastButtonATime = millis();
  
  currentButtonB = lastButtonB = digitalRead(buttonB); 
  lastButtonBTime = millis();
  
  currentButtonC = lastButtonC = digitalRead(buttonC); 
  lastButtonCTime = millis();
}

void readSwitchStates() {
// read the current button states
  simpleDebounce(buttonA, currentButtonA, lastButtonA, lastButtonATime);
  simpleDebounce(buttonB, currentButtonB, lastButtonB, lastButtonBTime);
  simpleDebounce(buttonC, currentButtonC, lastButtonC, lastButtonCTime);
}

bool pressEdge(bool& state, bool lastVal) {
// detects the leading edge of a button press.
  if ((!state) && lastVal) { // button pressed, but state not yet set
    state = true;            // set the state without waiting for debouncing
    return true;             // return a valid edge
  } else {
    return false;            // it's not a press edge.
  }
}

void buttonLogic() {
// 1) Pressing a button when its LED is off causes the LED to turn on
// 2) pressing a button while its LED is on turns it off.
// 3) Cancel LED A when LED B is illuminated
// 4) Cancel LED B when LED C is illuminated
// 5) Cancel LED C when LED A is illuminated
    if (pressEdge(currentButtonA, lastButtonA)) { // rules 1 & 2 for LED A
      ledOnA = !ledOnA;
      
      if (ledOnA) { // rule 5
        ledOnC = false;
      }
    }
   
    if (pressEdge(currentButtonB, lastButtonB)) { // rules 1 & 2 for LED B
      ledOnB = !ledOnB;
      
      if (ledOnB) { // rule 3
        ledOnA = false;
      }
    }
   
    if (pressEdge(currentButtonA, lastButtonA)) { // rules 1 & 2 for LED C
      ledOnB = !ledOnB;
      
      if (ledOnC) { // rule 4
        ledOnB = false;
      }
    }
}

void loop()
{
  while (true) {
    // get input states
    readSwitchStates();

    // do the button logic
    buttonLogic();
    
    // set output states
    writeLEDStates();
  }  
}
I hope this helps.
 
Last edited:

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
part 3

It may not matter for your code, but the millis() value will loop back around to zero after about 50 days.

If your code will be left running for this long, you will need to take this into account.

As it stands, the button routines may stop responding after 50 days.
 
WOW !!!!!

Thank You Steve for the time and effort you've put into the posts.. believe me it's of immense help.

I am still working my way through the posts to understand fully the process to achieve the desired results.

There are still a few tweaks needed but for now its good to demonstrate. I have altered your version to get LED C to work...I'm sure this was just a test to check that i was following you :)

Now i have the basics working as intended i have added a repeater LED circuit using a LM555 chip to create an LED flash for certain button pushes. I've chosen this option as One of the control panels will have a solid LED whilst the other will flasher to indicate "User Intervention Required".

LM555 Repeater with Flasher Circuit Complete1.jpg

I'm also aware that I will be running out of I/O pins soon. So i will need to get my head round shift registers at some point.

BUT Now i'm happy to construct a mock up of the pcb boards to illustrate the newly acquired skills to my fellow P4 club Modellers.

I'm sure they will be as grateful as I am to see the results :D

Mark
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Sorry, I didn't show how to make a LED flash without using delays. It's probably easier than a 555 :)

Also, if you look at the price of a bare ATMega8 chip (around $1?) then it may be more economical in some cases just to use a second one rather than worry about shift registers. Of course, if they must be controlled from the same set of inputs the shift register makes more sense.

Sorry about the bugs. I didn't test any of the code. But I did try to check it as well as I could.

Don't be concerned if the process I described does not come as quickly to you as it may appear it should. I have a good year or two of experience at doing this.

The main thing is stepwise refinement. Don't try to do everything at once. Sneak up on the solution slowly enough and it won't see you coming :D

The real trick is knowing how best to refine the problem. In general you do broad control logic first, then you worry about finer and finer details.
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Hahaha, OK, I see the bug. Typical copy and paste error.

OK, let's assume that you want LEDC to flash rather than turn on when button C is pressed.

Really, nothing changes in the control logic. We can just say LEDC is on -- the fact that on means flashing is something that happens in the code which turns the LED on and off

So what does flashing mean? It means that we turn it on for some number of ms, and off for some number of ms and then we repeat. So perhaps we need to store the time of the last (or next) transition and whether it is currently on or off. Then we can check the time against the transition time and act accordingly.

So let's write that down:

When flashing is enabled, the program will record the time the LED was last turned on (or off) and it's current state (on or off). After this time + a delay, the state will be changed. When flashing is enabled it will be initialised so that the first change will turn it on (last state was off, a long time ago)

what will we have:

a) whether the LED is on or off [ledOn]
b) on and off duration [flashTime]
c) current time [time]
d) current state [ledFlashState]
e) time of last transition [ledLastFlashTime]

And in pseudocode:

to turn the flashing off:

Code:
ledOn <- false // disabled

To start flashing:

Code:
ledFlashState <- false // Off
ledLastFlashTime <- -[flashTime]
ledOn <- true // enabled

And to make it happen (in the writeLEDStates function):

Code:
if ledOn then
  if time >= (ledLastFlashTime + flashTime) then
    ledFlashState = not ledFlashState
    ledLastFlashTime = time
    if ledFlashState then
      turn LED ON
    else
      turn LED OFF
    endif
  endif
else
  turn LED OFF
endif

And then we modify the code (non-trivial, but not especially hard)

Code:
const long DBTIME = 25; // number of ms allowed to eliminate key bounce
const long FLASH_TIME = 250; // duration of on/off times for flashing LEDs

bool debounce(bool state, bool val, long time, long dbTime, bool& lastVal, long& lastTime) {
// We will only register a change of state if the input value has not
// changed for a certain time
  bool newState;

  newState = state;         // by default the new state is the old state

  if (lastVal != val) {     // if the input value has changed
    lastTime = time;        // set the last time it changed to "now"
    lastVal = val;          // and retain the current input value
  } else {                  // otherwise (input value has NOT changed) 
    if ((time - lastTime) >= dbTime) { // has it remained stable long enough
      newState = val;       // change the state
    }
  }
  
  return newState;          // This is the new state
}

bool simpleDebounce(int inPin, bool& state, bool& lastVal, long& lastTime) {
// A debounce routine with a simpler interface.
  long time = millis();             // time in milliseconds
  bool val = digitalRead(inPin);    // current pin value
  
  state = debounce(state, val, time, DBTIME, lastVal, lastTime); // debounce the pin
  
  return state;                     // return state
}

//
// INPUTS
//

// button A - The big blue one
const int buttonA = 8;
bool lastButtonA;
bool currentButtonA;
long lastButtonATime;

// Button B - the small green one
const int buttonB = 7;
bool lastButtonB;
bool currentButtonB;
long lastButtonBTime;

// Button C - the black one with black writing on a black background
const int buttonC = 6;
bool lastButtonC;
bool currentButtonC;
long lastButtonCTime;
bool ledCFlashState = false;
long ledCLastFlashTime = -(FLASH_TIME);

//
// OUTPUTS
//

// LED A - the red one
const int ledA = 11;
bool ledOnA = false;

// LED B - the white one
const int ledB = 10;
bool ledOnB = false;

// LED C - the black one.
const int ledC = 9;
bool ledOnC = false;

void writeLEDStates() {
// writes the current states to the LEDs
  digitalWrite(ledA, ledOnA);
  digitalWrite(ledB, ledOnB);

  // special code for flashing LEDC
  long time = millis();                   // get the time
  
  if (ledOnC) {                           // are we flashing?
    if (time >= (ledCLastFlashTime + FLASH_TIME)) { // time to chang LED state?
      ledCFlashState = !ledCFlashState;   // change LED state
      ledCLastFlashTime = time;           // and set the time 
      digitalWrite(ledC, ledCFlashState); // Change LED state
    }
  } else {                                // not flashing?
    digitalWrite(ledC, false);            // turn LED off
  }
}

void setup()
{
  // initialize pins
  pinMode(buttonA, INPUT);
  pinMode(buttonB, INPUT);
  pinMode(buttonC, INPUT);
  
  pinMode(ledA, OUTPUT);
  pinMode(ledB, OUTPUT);
  pinMode(ledC, OUTPUT);
  
  // set output states
  writeLEDStates();
 
  // get the initial state of buttons
  currentButtonA = lastButtonA = digitalRead(buttonA); 
  lastButtonATime = millis();
  
  currentButtonB = lastButtonB = digitalRead(buttonB); 
  lastButtonBTime = millis();
  
  currentButtonC = lastButtonC = digitalRead(buttonC); 
  lastButtonCTime = millis();
}

void readSwitchStates() {
// read the current button states
  simpleDebounce(buttonA, currentButtonA, lastButtonA, lastButtonATime);
  simpleDebounce(buttonB, currentButtonB, lastButtonB, lastButtonBTime);
  simpleDebounce(buttonC, currentButtonC, lastButtonC, lastButtonCTime);
}

bool pressEdge(bool& state, bool lastVal) {
// detects the leading edge of a button press.
  if ((!state) && lastVal) { // button pressed, but state not yet set
    state = true;            // set the state without waiting for debouncing
    return true;             // return a valid edge
  } else {
    return false;            // it's not a press edge.
  }
}

void buttonLogic() {
// 1) Pressing a button when its LED is off causes the LED to turn on
// 2) pressing a button while its LED is on turns it off.
// 3) Cancel LED A when LED B is illuminated
// 4) Cancel LED B when LED C is illuminated
// 5) Cancel LED C when LED A is illuminated
    if (pressEdge(currentButtonA, lastButtonA)) { // rules 1 & 2 for LED A
      ledOnA = !ledOnA;
      
      if (ledOnA) { // rule 5
        ledOnC = false;
      }
    }
   
    if (pressEdge(currentButtonB, lastButtonB)) { // rules 1 & 2 for LED B
      ledOnB = !ledOnB;
      
      if (ledOnB) { // rule 3
        ledOnA = false;
      }
    }
   
    if (pressEdge(currentButtonA, lastButtonA)) { // rules 1 & 2 for LED C
      ledOnC = !ledOnC;
      
      if (ledOnC) { // rule 4
        ledOnB = false;
        
        ledCFlashState = false;
        ledCLastFlashTime = -FLASH_TIME;
      }
   }
}

void loop()
{
  while (true) {
    // get input states
    readSwitchStates();

    // do the button logic
    buttonLogic();
    
    // set output states
    writeLEDStates();
  }  
}

Again, untested. If it doesn't work, see if you can fix it :D

You might note that if we implemented the flashing for several LEDs, they would not flash in sync. You'd have to change the code to make them flash in sync. Perhaps you could think about how to do that.

Also, you may have noted there are no calls to delay() in this code. This means the code runs through the loop many, many times per second. This means that the response time will be very quick so it can appear to be doing many things at once.

This code is starting to look reasonably complex.

The next step is to create a class (or classes) that will hide this complexity so that most of what is left is the main control logic. It will have the side-effect of re-using tested code so that it becomes harder to make mistakes like the one I made last time.

But that will have to wait until another day...
 
Hi Steve,

Been Studying Hard :D

BUT:

My Concern remains, as the amount of I/O pins will limit these flashing LEDs, as for each output so far, One LED will be constant and the other will be flashing to indicate "user intervention needed" (ie press a button).

So i think i need to study how to get more pins to do this job and the sequencing of the main control panel route indication. I had not calculated this into the equation when i started out with the Arduino, but it seems to me now, that this would be greatly enhanced if i do adopt this approach.
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
OK, now you need to think about how you can power more LEDs from fewer pins.

There are a number of ways (actually almost countless ways). Let's look at the various classes of options:

1) more microcontrollers

Just drive as many as you can from one, then move to a second, and a third , and so on, until you have handled everything.

This is pretty simple, but if you require interaction between the sets of LEDs, the code can get more complex as you need to communicate between the uCs.

2) multiplexing

This is a technique where you turn on only a few LEDs at a time, but with the same connections, you can also turn on others. So, with 8 outpus, you can control 4 LEDs in 4 columns -- 16 LEDs. With 10, you can control 25 LEDs. (Here is a circuit for a 35 LEDs from 12 pins)

Similar to multiplexing is charlieplexing. This allows even more LEDs to be controlled from the same number of outputs, but is even harder to code for.

This video is interesting, but he gets a lot wrong. For example, when showing multiplexing, he claims you can only have a single LED on at once. Wrong -- you can have an entire row or column under control at once. In the charlieplexing, he gets the location of the resistors wrong (they should be in the control leads) and thus he also gets the explanation of why other LEDs don't turn on wrong. I mention this, becayse this video seems to rank quite highly in Google searches.

Because you have control over the outputs to a large degree, you can even multiplex inputs and outputs on the same pins -- further decreasing pin requirements.

3) getting more outputs

Another way is to add devices that will allow you to obtain many more outputs.

There are several common examples:

4017 - this allows you to turn on 1 of 10 outputs at a time, in order. This requires 2 pins to control it (reset and clock). You may be able to share the reset with other devices. If you're multiplexing, this can be a great way to control the columns (only one of which must be turned on)

74HC595 - This allows you to set 8 pins. This takes 3 outputs, a clock, a data, and a latch input. What makes this even better is that these chips can be cascaded so the same 3 outputs can control many chips (10 chips would be 80 outputs). If you're multiplexing, this can control the rows, driving 8 pins at once.

A combination of a 4017, and a 74HC595 can be used to multiplex 80 LEDs from 5 outputs! (each 4017 output needs to turn on a transistor)

Even better, You can add more and more 74HC595's to add 80 LEDs at a time with no more pins!

The 4017 and the 74HC595 are pretty cheap, so no problems there.

The important things about multiplexing are:

1) Scanning the display fast enough that nobody notices flickering (at least 10 times per second)
2) Keeping the timing regular so that no column appears brighter than any other.
3) Giving each LED more current because it's only on for a short time (typically if it's only on 1/8th of the time you must give it 8 times the current)
4) Making sure the multiplexing doesn't freeze up. Because of (3) this could result in a VERY bright column of LEDs, possibly damaging them.

When you're writing code to control these, it's very important that you separate the logic to set what LEDs you want on from the code which controls them.

Using the code we have worked on previously, you might want to detect a gap of 1ms, and at this interval, displaying the next column of LEDs.

If you were using 8 x 8 pins to do this then the pseudocode might be:

Code:
if lasttime <> now then
  lasttime <- now  
  set all rows OFF
  set all cols OFF

  col <- col + 1
  if col > 7 then
    col <- 0
  endif

  set rows from data[col]
  turn on column col  
endif

This requires that you have an array of bytes "data" with 8 elements (0 to 7). When you want to turn LEDs on and off, you set or clear bits in the data array.

The display logic (above) paints the data array onto your LEDs.

In practice there's a little more to it than this, but not too much.

One enhancement is to drive the columns using a monostable. If the column stays set for too long, it switches off. This means that if your microcontroller freezes up, the display goes off rather than damaging the display. This is a good idea if your LED current is way in excess of the maximum continuous current. It's especially a good idea while you're testing because that's when things WILL go wrong.

There's no reason why a single arduino can't drive hundreds of LEDs.

If you look at multiplexing inputs as well (see how keypads are used), then you can have many inputs as well.

Controlling inputs is a little more tricky because you need to debounce an array of inputs, but you can do it.

If you immediately understand all I've written above then you're a bloody genius. This is likely to take some playing around with to become familiar with.
 
Maybe Bitten Off More Than I Can Chew

:rolleyes:

Thank You Steve for your reply. I Know its been a long time.

My focus has been on building the control panel and more work on the layout to get it to exhibition readiness.

It has become apparent to me, whilst wanting to learn more about the arduino, and understand how it works in connection with my project, that time is not on my side :mad:

Instead of getting bogged down in things that take a while to learn another route may be more advisable allowing me the time to focus elsewhere

The Control Panel is to have

1. Control the points (turnouts) to select the desired route
2. LED route to light the route the track is selected to.
3. Control the servo operated signals/ interfaced with LED Routing Display
4. Operator Sequence Control Panels

All have been outlined in the post though the concept may have changed slightly...

So i was wondering if there is a company/individual out there that may consider taking on the task of writing a program that caters for this and where i might find them?

Any Assistance as always would be grateful

Many Thanks

Mark
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
So i was wondering if there is a company/individual out there that may consider taking on the task of writing a program that caters for this and where i might find them?

See if you have a "Hackerspace" located nearby. See if someone there would be willing to assist you with this.

There are probably many other options, but hackerspaces and arduinos tend to go together :)
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
This may be it. They have a £5per month membership fee (and that's really cheap, my local one charges $70 per month for 24 hour access).

It also sounds like they don't have permanent infrastructure, but you may still be able to hook up with some people who can assist you.
 
After careful consideration I have decided to spend some time trying to write the sketch for the Arduino to suite my needs rather than find someone to do it for me. I think that if it was written for me I would still be in the dark as to what was happening, even though i'm sure it would work fine, if it didn't or needed tweaking then i wouldn't know how to achieve this.. understanding how and why it is written in the first place is key to upgrading or adding functions.

Winter is nearly upon us, the nights a drawing in and i will have more time to devote to this part of the project... so i'm sorry your stuck with me for a while longer. lmao

Having reflected upon my requirements stated in previous posts

1. Control the points (turnouts) to select the desired route
2. LED route to light the route the track is selected to.
3. Control the servo operated signals/ interfaced with LED Routing Display
4. Operator Sequence Control Panels

I am going to focus my efforts on Number 4 "Operator Sequence Control Panels" as this does not need to be linked to any of the other requirements. This will hopefully give me a chance to learn some of what i need to understand for future parts of the project.

As part of that i am going to continue with the Arduino Uno as delivered and when i run out of pins then i will look at ways around that issue....unless ofcourse i am advised otherwise .

At present i think i understand 80% of the code already written, the other 20% I'm working on.

Time for my first question:...Any Recommendations reading material on Arduino programming ?



Thanks for all your help so far :D ;)

Here is where we got too on that code.


Part 2 :)


Code:
#define DBTIME 25; // number of ms allowed to eliminate key bounce

bool debounce(bool state, bool val, long time, long dbTime, bool& lastVal, long& lastTime) {
// We will only register a change of state if the input value has not
// changed for a certain time
  bool newState;

  newState = state;         // by default the new state is the old state

  if (lastVal != val) {     // if the input value has changed
    lastTime = time;        // set the last time it changed to "now"
    lastVal = val;          // and retain the current input value
  } else {                  // otherwise (input value has NOT changed) 
    if ((time - lastTime) >= dbTime) { // has it remained stable long enough
      newState = val;       // change the state
    }
  }
  
  return newState;          // This is the new state
}

bool simpleDebounce(int inPin, bool& state, bool& lastVal, long& lastTime) {
// A debounce routine with a simpler interface.
  long time = millis();             // time in milliseconds
  bool val = digitalRead(inPin);    // current pin value
  long dbTime = DBTIME;               // the debounce interval
  
  state = debounce(state, val, time, dbTime, lastVal, lastTime); // debounce the pin
  
  return state;                     // return state
}

//
// INPUTS
//

// button A - The big blue one
const int buttonA = 8;
bool lastButtonA;
bool currentButtonA;
long lastButtonATime;

// Button B - the small green one
const int buttonB = 7;
bool lastButtonB;
bool currentButtonB;
long lastButtonBTime;

// Button C - the black one with black writing on a black background
const int buttonC = 6;
bool lastButtonC;
bool currentButtonC;
long lastButtonCTime;

//
// OUTPUTS
//

// LED A - the red one
const int ledA = 11;
bool ledOnA = false;

// LED B - the white one
const int ledB = 10;
bool ledOnB = false;

// LED C - the black one.
const int ledC = 9;
bool ledOnC = false;

void writeLEDStates() {
// writes the current states to the LEDs
  digitalWrite(ledA, ledOnA);
  digitalWrite(ledB, ledOnB);
  digitalWrite(ledC, ledOnC);
}

void setup()
{
  // initialize pins
  pinMode(buttonA, INPUT);
  pinMode(buttonB, INPUT);
  pinMode(buttonC, INPUT);
  
  pinMode(ledA, OUTPUT);
  pinMode(ledB, OUTPUT);
  pinMode(ledC, OUTPUT);
  
  // set output states
  writeLEDStates();
 
  // get the initial state of buttons
  currentButtonA = lastButtonA = digitalRead(buttonA); 
  lastButtonATime = millis();
  
  currentButtonB = lastButtonB = digitalRead(buttonB); 
  lastButtonBTime = millis();
  
  currentButtonC = lastButtonC = digitalRead(buttonC); 
  lastButtonCTime = millis();
}

void readSwitchStates() {
// read the current button states
  simpleDebounce(buttonA, currentButtonA, lastButtonA, lastButtonATime);
  simpleDebounce(buttonB, currentButtonB, lastButtonB, lastButtonBTime);
  simpleDebounce(buttonC, currentButtonC, lastButtonC, lastButtonCTime);
}

bool pressEdge(bool& state, bool lastVal) {
// detects the leading edge of a button press.
  if ((!state) && lastVal) { // button pressed, but state not yet set
    state = true;            // set the state without waiting for debouncing
    return true;             // return a valid edge
  } else {
    return false;            // it's not a press edge.
  }
}

void buttonLogic() {
// 1) Pressing a button when its LED is off causes the LED to turn on
// 2) pressing a button while its LED is on turns it off.
// 3) Cancel LED A when LED B is illuminated
// 4) Cancel LED B when LED C is illuminated
// 5) Cancel LED C when LED A is illuminated
    if (pressEdge(currentButtonA, lastButtonA)) { // rules 1 & 2 for LED A
      ledOnA = !ledOnA;
      
      if (ledOnA) { // rule 5
        ledOnC = false;
      }
    }
   
    if (pressEdge(currentButtonB, lastButtonB)) { // rules 1 & 2 for LED B
      ledOnB = !ledOnB;
      
      if (ledOnB) { // rule 3
        ledOnA = false;
      }
    }
   
    if (pressEdge(currentButtonA, lastButtonA)) { // rules 1 & 2 for LED C
      ledOnB = !ledOnB;
      
      if (ledOnC) { // rule 4
        ledOnB = false;
      }
    }
}

void loop()
{
  while (true) {
    // get input states
    readSwitchStates();

    // do the button logic
    buttonLogic();
    
    // set output states
    writeLEDStates();
  }  
}
I hope this helps.

So in the code above each Button illuminates a single LED. But actually that is not entirely true. What i need to happen is that:

If Button A is ON then LedA will Illuminate and new Led [ LedA1] will flash
This will be the same for all 3 Leds mentioned in this sketch

I can see that this could be achieved by simply assigning 3 more OUTPUTS and linking them to the relevant existing Push buttons

However i don't want to go out of my way to use the Arduino Pins up.
So i have read up on Charlieplexing and was wondering if i could use this function to gain the flashing led ?

From My Understanding albeit very limited

void buttonLogic() {
// 1) Pressing a button when its LED is off causes the LED to turn on
// 2) pressing a button while its LED is on turns it off.
// 3) Cancel LED A when LED B is illuminated
// 4) Cancel LED B when LED C is illuminated
// 5) Cancel LED C when LED A is illuminated

Charlieplex new flashing led A1 to existing LED C

Rule 5) Cancel LED C when LED A is illuminated

This will cause LED A to be illuminated and as LED C is cancelled (LOW) that will result in LED A1 being illuminated (write code to make this LED flash).
Result 2 LEDs illuminated (from Button A)

Charlieplex new flashing led B1 to existing LED A
Charlieplex new flashing led C1 to existing LED B

Saving Pins :) Keeping Existiing Logic which is correct :)

AM I ON THE RIGHT TRACKS :confused:

Charlieplex flashing led A1 to existing LED C
 
Top