Skip to main content

Embedded Programming

Today, we delved into the realm of microcontrollers, specifically focusing on embedded programming. Our tool of choice was TinkerCAD, where we virtually programmed an Arduino to perform various tasks.

Our assignment for this week involved programming an Arduino to control three LEDs using two buttons in a creative manner. I opted to designate the buttons for forward and backward navigation, allowing us to cycle through the LEDs. Additionally, when both buttons are pressed simultaneously, all LEDs illuminate.

For the LEDs, I incorporated 220 Ohm resistors, and for the buttons, I used 10 kOhm resistors.

To manage the LED cycling process, I implemented a counter that increments or decrements based on button presses. I applied modulo 3 to ensure the counter never exceeds 2, causing it to roll over once it reaches its maximum or minimum value. The remaining code is very straightforward; you can review it below.

const int forwardButtonPin = 7;
const int backButtonPin = 10;

const int led1Pin = 13;
const int led2Pin = 12;
const int led3Pin = 11;

int ledCounter = 0; // variable for tracking the current LED

void setup()
{
pinMode(forwardButtonPin, INPUT);
pinMode(backButtonPin, INPUT);

pinMode(led1Pin, OUTPUT);
pinMode(led2Pin, OUTPUT);
pinMode(led3Pin, OUTPUT);
}

void loop()
{
// While both buttons are pressed, keep all LEDs on
while (digitalRead(forwardButtonPin) && digitalRead(backButtonPin))
{
digitalWrite(led1Pin, HIGH);
digitalWrite(led2Pin, HIGH);
digitalWrite(led3Pin, HIGH);
}

// Once buttons are released, resume cycling functionality
if (digitalRead(forwardButtonPin))
{
ledCounter = (ledCounter + 1) % 3;
delay(200); // debouncing delay
}

if (digitalRead(backButtonPin))
{
ledCounter = (ledCounter + 2) % 3; // adding 2 is equivalent to subtracting 1 in modulo 3
delay(200); // debouncing delay
}

digitalWrite(led1Pin, ledCounter == 0 ? HIGH : LOW);
digitalWrite(led2Pin, ledCounter == 1 ? HIGH : LOW);
digitalWrite(led3Pin, ledCounter == 2 ? HIGH : LOW);
}

Here you can see what the simulation in TinkerCAD would look like:

To create a practical version on a breadboard, I used an RP2040-Zero, a microcontroller board similar to the Raspberry Pi Pico, featuring the RP2040 MCU. I employed different resistors, each with a 150 Ohm resistance, to account for different specifications in the LEDs. The following illustrates the project in action:

After some thinking I came up with this code that handles the debouncing in a more sophisticated manner:

const int forwardButtonPin = 7;
const int backButtonPin = 10;

const int led1Pin = 13;
const int led2Pin = 12;
const int led3Pin = 11;

int ledCounter = 0;

int forwardButtonState;
int lastForwardButtonState = LOW;
unsigned long lastForwardDebounceTime = 0;
unsigned long forwardDebounceDelay = 50;

int backButtonState;
int lastBackButtonState = LOW;
unsigned long lastBackDebounceTime = 0;
unsigned long backDebounceDelay = 50;

void setup() {
pinMode(forwardButtonPin, INPUT);
pinMode(backButtonPin, INPUT);

pinMode(led1Pin, OUTPUT);
pinMode(led2Pin, OUTPUT);
pinMode(led3Pin, OUTPUT);
}

void loop() {
int reading;

reading = digitalRead(forwardButtonPin);
if (reading != lastForwardButtonState) {
lastForwardDebounceTime = millis();
}
if ((millis() - lastForwardDebounceTime) > forwardDebounceDelay) {
if (reading != forwardButtonState) {
forwardButtonState = reading;
if (forwardButtonState == HIGH) {
ledCounter = (ledCounter + 1) % 3;
}
}
}
lastForwardButtonState = reading;

reading = digitalRead(backButtonPin);
if (reading != lastBackButtonState) {
lastBackDebounceTime = millis();
}
if ((millis() - lastBackDebounceTime) > backDebounceDelay) {
if (reading != backButtonState) {
backButtonState = reading;
if (backButtonState == HIGH) {
ledCounter = (ledCounter + 2) % 3;
}
}
}
lastBackButtonState = reading;

digitalWrite(led1Pin, ledCounter == 0 ? HIGH : LOW);
digitalWrite(led2Pin, ledCounter == 1 ? HIGH : LOW);
digitalWrite(led3Pin, ledCounter == 2 ? HIGH : LOW);
}

What this essentially does is read the current state of button X and compare it with the last saved state (lastXState). If it's different, the current time is recorded (lastXDebounceTime). The program then checks if enough time (XDebounceDelay) has passed since the last state change. If so, it checks if the current reading is different from the last debounced state (XButtonState). If it is different, the state is updated.