Multiple Buttons and LEDs


Yesterday's post showed the making of this little two button remote control so it seems only right to to include it in this post about reading multiple buttons. It's just two simple buttons that pull the data line down to ground when pressed and could easily be recreated on the breadboard if required. We're also going to talk about multiple LED's and set up the framework for using multiples even though the we're only going to using the one built in LED again.

// Old definition
const uint8_t btnPin = D1;
const uint8_t ledPin = LED_BUILTIN;
// New Definition
const uint8_t numBtn = 2;
const uint8_t btnPin[] = { D1, D2 };
const uint8_t numLed = 1;
const uint8_t ledPin[] = { LED_BUILTIN };
view raw MultipleIO1.ino hosted with ❤ by GitHub

The main difference between this program and the previous one is that the inputs and outputs are going to be defined as arrays of values instead of just single values. Right at the top of the program it's important to define how many inputs and LED's there around going to be and then create an array of values to declare which data lines these things are going to be on, in this instance D1 and D2 and the same LED as before. even though there is only one LED we can still define it in an array.

void setupInputs() {
for (uint8_t i = 0; i < numBtn; i++) {
pinMode(btnPin[i], INPUT_PULLUP); // Initialise the btn pin as an input
}
}
void setupLights() {
for (uint8_t i = 0; i < numLed; i++) {
pinMode(ledPin[i], OUTPUT); // Initialise the LED pin as an output
}
}
view raw MultipleIO2.ino hosted with ❤ by GitHub

Now when we need to set up the pins for the multiple inputs and outputs (IO) we can do it in a loop where we initialise each pin as required by first recovering the value from the array. The other subtle change here is that we're moving the initialisation code to a whole new functions, setupInputs() and setupLights() this is in preparation for future code when there will be different Inputs and Lights to configure and control. As you can imagine, it's now time to extract the code for controlling the inputs and lights to their own functions too. Again both of these functions are going to wrap the existing code in a for loop to control every item in the array. This code can be seen in the complete example linked at the end of the tutorial.

void setup() {
setupInputs();
setupLights();
}
void loop() {
EVERY_N_MILLISECONDS(INTERVAL) {
controlInputs();
// Do something with the inputs that changes the state of the lights
controlLights();
}
}
view raw MultipleIO3.ino hosted with ❤ by GitHub

The biggest change from this refactoring is the improved readability of our code. The setup function now has a few simple lines of code that clearly state what each function does and the same with the main loop of our program. We read the inputs and set the outputs accordingly, all that's left at this point is to do something with the inputs that creates some kind of change to the output.To round out today's, admittedly brief, tutorial I've decided to add some code that uses the two different buttons to switch between the various flashing modes for the single LED. 

enum class ledModes { Off,
On,
Blink,
PWM,
Flicker,
numItems };
const uint8_t numLedModes = (uint8_t)ledModes::numItems;
view raw MultipleIO4.ino hosted with ❤ by GitHub

I realise I said before that we don't actually care what values of different modes in the ledModes enum, but this code does need to make a few assumptions about them. If the compiler isn't given specific values to use, the each item in an enumeration is given a sequential number starting from zero, we still don't care what order is, but if we add one we know we're going to move to the next mode. We do need to worry about how many modes there are because we don't want to ask for a mode that doesn't exist. There's a neat trick for this; we can add an additional value to the enum and call it numItems. By default this item will be allocated a value equal to the number of items in the enum. We need to explicitly convert this item to a numerical value before we can use it which is achieved by Type Casting to the kind of value we need (uint8_t)ledModes::numItems.

// Switch the led mode when the first button is pushed
if (btnDown(0)) {
// Increment the led mode by one and loop round after the last mode
ledMode[0] = (ledModes)(((uint8_t)ledMode[0] + 1) % numLedModes);
}
view raw MultipleIO5.ino hosted with ❤ by GitHub

Now switching up through the modes becomes straight forward. We've already got some code that checks for a button press event so in that code block we take the current mode and add one to it. We do need to convert the enum item and convert it to a numerical value before adding to it and afterwards we need to make sure it is converted back to an enum item with some more casting. The final part of this equation is to use the modulo (%) function which divides on number by another and then gives the remainder. In this case when we add one to the last item in the list it will refer to a mode that doesn't exist, we divide by the number of items in the list and the new value becomes zero, which essentially loops back to the first item in the list.

if (btnDown(1)) {
// Decrement the led mode by one and loop round.
// Do this by adding the number of modes first, this prevents the possibility of a negative number
ledMode[0] = (ledModes)(((uint8_t)ledMode[0] + numLedModes - 1) % numLedModes);
}
view raw MultipleIO6.ino hosted with ❤ by GitHub

We're going to set up the code block for the other button to rotate the other way round the list. This should be as simple as subtracting one from the current mode but we can fall fowl of the modulo function here. Mode 'zero' would become mode 'minus one' and the modulo function doesn't produce the desired result for negative numbers. So to rotate in the other direction, the first thing we do is add the number of items in the list and then subtract one. This ensures the value for the mode is always positive and makes the modulo wrap round correctly.

Now we have two buttons that switch between the different modes for the LED. There are two more things that I want to cover before moving on to other other topics. Next I think I'll talk about another LED mode where we smoothly cycle through the range of brightness before putting everything we have together to create our first puzzle. Beyond that please feel free to let me know if there's any part of the Nikol-AI box that you'd like to learn about after that and I'll work towards that.

The full code for this and all the other tutorials in this series can be found in my github repository.
https://github.com/msraynsford/Puzzling/

Popular posts from this blog

Wiring the Ruida Controller

Laser Cut Cryptex

Leetro to Ruida Settings