Beginner here - RGB LED common anode, single button, need to change colors at push

So I have a couple Cores and a photon, I want to to use a single button to switch a RGB LED between 2-3 colors. I saw some arduino examples using 3 buttons, but I want to try and slim that down to cut down on wiring and such. I’ve been able to loop colors, but I want to keep the colors until I press the button. This is for some costume pieces so I won’t be able to have it internet controlled.

Any assistance would be great!

@dewser great question! Instead of just giving you the code, let’s take it one step at a time and see what you come up with. Forgive me if you know this stuff already… since you left it open I’m just throwing out all of the basics. If you’d prefer just the code though, no worries.

Button facts

  • If you want to press, press, press to cycle through the colors you’ll likely need a Normally Open SPST (Single Pole Single Throw) switch. This means the contacts are not connected (normally open) until you press the button. Then they are said to be closed.
  • You need the pin to be read as HIGH when you’re not pressing it and LOW when you are pressing it.
    • To do this connect one side of the switch to a digital input (all I/O on the Core/Photon can be set as a digitalRead() pin).
    • You connect the other side of the switch to GND typically, this will bring the pin LOW when you press it.
    • To make it read HIGH when not pressing it, typically you would add a pull up resistor to 3V3. This can be done in code with an internal pull up resistor (typ. 40 k ohms) with pinMode(D2, INPUT_PULLUP);
  • Most switches have a bit of a “bounce” to them as the contacts close. This connects and disconnects the connects several times very fast in succession until the switch finally rests in a closed (connected) state. The opposite is also true when releasing the switch. This bouncing period usually lasts up to 10-50ms on a variety of switches.

Reading the switch, and acting on a “button press”

  • Set your pinMode() as described above in the setup() { } function.
  • in your loop() { } function you can test if the switch is pressed with if ( digitalRead(D2) == LOW ) { }
  • Because of the “bounce” we talked about, and the fact that loop() { } is iterating very quickly, the test above to read the switch will be true then false then true again over and over when you press the switch just once. Since you want your program to process the button as if you just pressed it once, we have to “debounce” the switch input.
    • Simplest debouncing routines are just a delay. In your if ( digitalRead(D2) == LOW ) { } block, simply add a delay that will ensure you are past all of the bouncing action, so in this case delay(50); to delay 50 milliseconds.
    • After the delay you can simply assume your switch is debounced and you are still pressing it. It should be LOW. At this point you have a “button press” and can write some code that acts on that, i.e., will cycle your LED color. You could just increment a counter from 1 to 3, and if it’s greater than 3 set it to 1. This is called a ring counter, because it goes around in a ring. Don’t forget to initialize this counter to 1 in setup().
    • After your color increment code you want to wait until the button is released, or else this if () test will continue to be true and your color will be incremented every 50 milliseconds. To do this we can put a hard while() loop next that stays in a hard loop while the input is LOW. while ( digitalRead(D2) == LOW ); The moment your button is released this while() loop will be false because the input will be pulled HIGH, so code will continue to execute.
  • At this point remember that the button is bouncing open and closed, so even though the HIGH level kicked it out the hard while() loop, it’s still going LOW, HIGH, LOW, HIGH. If we exited this if() block now, loop() would run this code again potentially fast enough to catch the switch bouncing and enter the test once again. To prevent that from happening, we can add another delay just like in the beginning. So a delay(50); would keep us from exiting this block of code until well after the switch stopped bouncing and the input stayed HIGH.

Processing the Ring Counter

  • in loop(), there are two tasks. One is to read your switch, and act on button presses. The other is to update the color based on the Ring Counter.
  • A simple switch(ringCounter) { case 1: RGB.color(255,0,0; break; } statement can help you manage testing the ring counter value, and taking an appropriate action to set your RGB LED color.
  • Or you can use a few if() statements that test to see if the ring counter equals one of three different values, e.g., if (ringCounter == 1) RGB.color(255,0.0);
  • RGB.color() is just an example of setting the on-board RGB LED. You may have an external RGB LED that you are driving the segments of with PWM outputs using analagWrite();

Other thoughts

  • This is a very basic overview of how to read switches and take some action. You can make this MUCH more complicated in varying degrees. However this WILL work reliably and do what you want. As you write more and more code though, using blocking delays to debounce your buttons will be costly to the processing performance of the rest of your code. Once you have this working, try to make it better in pieces… and then see if you can do the same thing in different ways.
  • For a deeper dive, you can look up all of the functions and statements I’ve included above in the Particle Docs: https://docs.particle.io/reference/firmware/photon/
  • Good luck!
4 Likes

More Other Thoughts:

Check out the clickButton library available in the web IDE (search the libraries for “clickbutton”) or grab it for local compile/CLI from https://github.com/pkourany/clickButton. You can get way more use out of a button (short clicks, long clicks, and multiple clicks) and let it handle debounce for you.

HOWEVER, do read every bit of what @BDub posted above. Having that knowledge will allow you to program more effectively and aid in any troubleshooting!

4 Likes