digitalRead button state change

Hello all!

I am new to arduino, programming, and electronics in general and was looking for a bit of advice. This is also my first forum post so I hope that I am doing this correctly. I am putting together a 6 channel led dimmer and am having trouble getting the channels to read and update correctly. I have all six channels working and if I press each button individually, they each perform the required task. However, when I press 2 buttons (or more) at once, the first LED acts appropriately but the second does not respond until I release the first. Here is my setup:

Arduino Uno
6 momentary switches (with pull down resistors)
6 LEDs (with resistors)

The momentary switches are connected to pins 2,4,7,8,12, and 13
The LEDs are connected to pins 3, 5, 6, 9, 10, and 11

Here is my code (I am sure there are a lot of improvements that can be made):

// Initialize Button and LED variables
const int channel_A_button = 2;
const int channel_B_button = 4;
const int channel_C_button = 7;
const int channel_D_button = 8;
const int channel_E_button = 12;
const int channel_F_button = 13;
const int channel_A_LED = 3;
const int channel_B_LED = 5;
const int channel_C_LED = 6;
const int channel_D_LED = 9;
const int channel_E_LED = 10;
const int channel_F_LED = 11;
// Initialize button state variables for state tracking
int A_buttonstate = 0;
int B_buttonstate = 0;
int C_buttonstate = 0;
int D_buttonstate = 0;
int E_buttonstate = 0;
int F_buttonstate = 0;
int A_lastbuttonstate = 0;
int B_lastbuttonstate = 0;
int C_lastbuttonstate = 0;
int D_lastbuttonstate = 0;
int E_lastbuttonstate = 0;
int F_lastbuttonstate = 0;
// Initialize PWM variables
int fade = 5;
int brightness = 0;

void setup() {                
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(2, INPUT);
  pinMode(4, INPUT);
  pinMode(7, INPUT);
  pinMode(8, INPUT);
  pinMode(12, INPUT);
  pinMode(13, INPUT);
 }

void loop() { 
  A_buttonstate = digitalRead(channel_A_button);
  B_buttonstate = digitalRead(channel_B_button);
  C_buttonstate = digitalRead(channel_C_button);
  D_buttonstate = digitalRead(channel_D_button);
  E_buttonstate = digitalRead(channel_E_button);
  F_buttonstate = digitalRead(channel_F_button);
  if (A_buttonstate != A_lastbuttonstate) {  //Verify previous button state
    if (A_buttonstate == HIGH)  {  //If different state is detected, Fade LED in or out depending on state
      while(brightness >= 0)  {
      analogWrite(channel_A_LED, brightness);
      brightness = brightness - fade;
      delay(10);
      }      
    }
  else {
      while(brightness <= 255) {
      analogWrite(channel_A_LED, brightness);
      brightness = brightness + fade;
      delay(10);
      }
  }
  }
  A_lastbuttonstate = A_buttonstate;  // Set new button state for check on next run
  if (B_buttonstate != B_lastbuttonstate) {
    if (B_buttonstate == HIGH)  {
      while(brightness >= 0)  {
      analogWrite(channel_B_LED, brightness);
      brightness = brightness - fade;
      delay(10);
      }      
    }
  else {
      while(brightness <= 255) {
      analogWrite(channel_B_LED, brightness);
      brightness = brightness + fade;
      delay(10);
      }
  }
  }
  B_lastbuttonstate = B_buttonstate;
  if (C_buttonstate != C_lastbuttonstate) {
    if (C_buttonstate == HIGH)  {
      while(brightness >= 0)  {
      analogWrite(channel_C_LED, brightness);
      brightness = brightness - fade;
      delay(10);
      }      
    }
  else {
      while(brightness <= 255) {
      analogWrite(channel_C_LED, brightness);
      brightness = brightness + fade;
      delay(10);
      }
  }
  }
  C_lastbuttonstate = C_buttonstate;
  if (D_buttonstate != D_lastbuttonstate) {
    if (D_buttonstate == HIGH)  {
      while(brightness >= 0)  {
      analogWrite(channel_D_LED, brightness);
      brightness = brightness - fade;
      delay(10);
      }      
    }
  else {
      while(brightness <= 255) {
      analogWrite(channel_D_LED, brightness);
      brightness = brightness + fade;
      delay(10);
      }
  }
  }
  D_lastbuttonstate = D_buttonstate;
  if (E_buttonstate != E_lastbuttonstate) {
    if (E_buttonstate == HIGH)  {
      while(brightness >= 0)  {
      analogWrite(channel_E_LED, brightness);
      brightness = brightness - fade;
      delay(10);
      }      
    }
  else {
      while(brightness <= 255) {
      analogWrite(channel_E_LED, brightness);
      brightness = brightness + fade;
      delay(10);
      }
  }
  }
  E_lastbuttonstate = E_buttonstate;
  if (F_buttonstate != F_lastbuttonstate) {
    if (F_buttonstate == HIGH)  {
      while(brightness >= 0)  {
      analogWrite(channel_F_LED, brightness);
      brightness = brightness - fade;
      delay(10);
      }      
    }
  else {
      while(brightness <= 255) {
      analogWrite(channel_F_LED, brightness);
      brightness = brightness + fade;
      delay(10);
      }
  }
  }
  F_lastbuttonstate = F_buttonstate;
}

I appreciate any insights!

Jonathan

You need a "brightness" variable for each LED.

Outstanding! Thank you! That did the trick!

jhagler:
Outstanding! Thank you! That did the trick!

The next thing you need to do is read the "blink without delay" sketches and do that. Then you could have them fading and brightening all at the same time.

Hint: You'll need a "last action" variable for each LED. Set that to the value of millis() each time you change the output PWM value, then compare the current value of millis() to the value of the "last action" variable. You may also need an array of booleans for each LED to indicate that the LED is being changed.

To speed things up, use hardware -- a small capacitor -- to debounce the switches. That way you're not doing delay()'s to make sure the switches are stable.

Thank you for the extra help. I was actually trying to use the millis() function to remove the delay()s but have not had a lot of success yet. I am getting close. I have not yet attempted to tackle an array but it is next on my list now.

Thanks again for your help!

Jonathan

I have another question if you have a second to take a look:

I am attempting to do as you suggested and I may be completely off here but I seem to get stuck in a while loop and never come out. Does this look anywhere close to how it should? It is just a snippet for channel A:

if (A_buttonstate != A_lastbuttonstate) {  //Verify previous button state
    if (A_buttonstate == HIGH)  {  //If different state is detected, Fade LED in or out depending on state
    A_current_millis = millis();
      while(A_brightness >= 0 && A_brightness <= 255 && (A_current_millis - A_previous_millis == fadetime))  {
          analogWrite(channel_A_LED, A_brightness);
          A_brightness = A_brightness - fade;
          A_previous_millis = A_current_millis;
          //delay(fadetime);
        }
      }      
    }
  else {
      A_current_millis = millis();
      while(A_brightness <= 255 && A_brightness >= 0 && (A_current_millis - A_previous_millis == fadetime))  {
        if (A_current_millis - A_previous_millis == fadetime) {
          analogWrite(channel_A_LED, A_brightness);
          A_brightness = A_brightness + fade;
          A_previous_millis = A_current_millis;
          //delay(fadetime);
        }
      }
    }
  A_lastbuttonstate = A_buttonstate;  // Set new button state for check on next run

Thanks again for all the help! Also, I added the capacitors for debouncing and that eliminated a weird flicker when pressing buttons!

Jonathan

          A_brightness = A_brightness - fade;

Learn to use the compound operators, and type a whole lot less.

          A_brightness -= fade;

Put each { on it's own line.

      while(A_brightness >= 0 && A_brightness <= 255 && (A_current_millis - A_previous_millis == fadetime))  {

One should almost never use == to test if time matches some interval. Use >= or <= as appropriate.

          //delay(fadetime);

Bad boy. Even commented out delay() calls have no place in your code.

Some of your variable names are longer than they need to be. currTimeA, prevTimeA, etc. convey the same amount of information, and read in the proper order.

Thanks Paul! I appreciate the input! With the help of you guys I will learn this stuff way more quickly! I will redo the code.

Paul,

Some of what you described is "style". Having written about 500KLOC of code in my life, and supervised the writing of around 3MLOC in that same time, I like my "{" on the same line as the statement that starts the block, and I like my names LONG. Long names don't take any longer to execute, and there's no confusion about "CurrentTime_A" and no one is tempted to make names shorter if "long" is the agreed-upon convention.

Also, delay(xxx) is more precise than hoping the loop makes it back around in time. Sometimes precise timing is important, sometimes it isn't. Understand the problem, use the right tools.

500KLOC of code

Must...fight...pedant...reflex.

dxw00d:

500KLOC of code

Must...fight...pedant...reflex.

Yeah, I know. Department of Redundancy Department. It's a bad habit. Too many years at IBM where acronyms lose all meaning except as their letters ...

I like my "{" on the same line as the statement that starts the block

If it was good enough for K&R, it's good enough for me.

AWOL:

I like my "{" on the same line as the statement that starts the block

If it was good enough for K&R, it's good enough for me.

That's pretty much how it was done before programmers started being paid by the newline :wink:

The only thing Ken and Dennis did back in the day that I disagreed with was that whole 8 character external variable name limit. Of course, RL02, RM05 and RP06 drives weren't exactly large -- unless you mean =physically= large :grin: -- and keeping files as small as possible was important on a machine with maybe 10MB of free disk space ...

"Finally, C, like any other language, has its blemishes. Some of the operators have the wrong precedence; some parts of the syntax could be better;there are several versions of the language extant, differing in minor ways."
The C Programming Language (K&R) 1st Edition, Introduction

f it was good enough for K&R, it's good enough for me.

My word! - and are you still defining function parameters between the closing parenthesis and the first brace, too? :grin:

My word! - and are you still defining function parameters between the closing parenthesis and the first brace, too?

No, that would be stupid and wouldn't compile (though it is how I learned C), but the "opening-brace-on-the-same-line-style" is standard for Linux kernel code, and since that's the day-job, it's a hard habit to break.

AWOL:

My word! - and are you still defining function parameters between the closing parenthesis and the first brace, too?

No, that would be stupid and wouldn't compile (though it is how I learned C), but the "opening-brace-on-the-same-line-style" is standard for Linux kernel code, and since that's the day-job, it's a hard habit to break.

C wasn't always the C we know and love today.

And that was the standard for UNIX long before there was such a thing as "Linux". I have no clue when the "{ on a new line" thing snuck in. It's a horrible coding convention and makes code harder to maintain as it reduces the amount of code that fits into a screen, which makes it more difficult to understand the code because you have to scroll up and down.

The best justification is this set of examples --

This is an if-then with a single statement --

if (condition)
    singleStatement;

nextStatementGoesHere;

That's a pretty standard coding style -- an extra blank line to separate the if-then from the following statement. If you add a second statement, this is how it is with the UNIX coding convention --

if (condition) {
    firstStatement;
    secondStatement;
}
nextStatementGoesHere;

And here it is for people who are paid by the newline --

if (condition)
{
    firstStatement;
    secondStatement;
}
nextStatementGoesHere;

Do that enough and the amount of code a programmer can view goes WAY down. Go make a bunch of functions with two or three lines in each and it'll be just about impossible to maintain the code.

That's true, and when I started programming professionally (COBOL), it was on IBM 3270s that, after XEDIT's own stuff, left something like 28 lines for program text. Now, on the 1680x1050 screen I'm using at the moment, I can fit 45 lines in the IDE screen at the default 12point font, and 55 if I go to 10point, so it's much less of a problem.

it was on IBM 3270s that, after XEDIT's own stuff,

3270 VDUs?
XEDIT?

Eee, luxury!
We used t'have program t'COBOL on t'punched cards!

Young folk today, they don't know they're born!

We still had a few card decks and a reader attached to the mainframe when I started. I could never get my head around the punch though. It had some weird combinations of key presses.

I think everyone finds a style that works best for them, and pretty-printers to convert to the local standard have been around for a long time. My pre-C assembly language background shows up in my commenting and, even with a 2560x1440 display to work with, my 5081 roots still show up in 80-character wide source files. :slight_smile:

C has managed to evolve more or less gracefully, thanks to the dedicated efforts of the folks who've served on the various standards committees and an amazingly engaged user base who've learned to value maximization of portability. Ritchie occasionally dropped by comp.lang.c and seemed genuinely pleased with the evolutionary path followed.