Getting started with Arduino and want to try an easy project? This project takes about 45 minutes, is fun to do, and it even has blinking lights!
There are two versions of this article:
It's interesting to do both of the projects if you have time. You'll learn how to take the same application, one real world and one virtual.
Let's get started.
Introduction
This article will get you up and running with a simple project involving an Arduino, a breadboard, and a handful of LEDs.
The project guides you through the steps required to make an 8-bit binary counter. Once it's working, you can easily experiment with other sequences and effects. It's a great first project for Arduino, or if you haven't used your Arduino it's a great way to get back into it.
Getting Started
To control some LEDs via an Arduino, you’ll need the following: 1 Arduino board, 1 breadboard, 1 resistor between 200 and 1000 ohms, 8 red LEDs, and 11 jumper wires.
- Connect a jumper wire from the GND pin on the Arduino to pin 1a on the breadboard.
- Add a 220-1000 ohm resistor between pin 1b and 1g on the breadboard.
- Add a wire from pin 1j on the breadboard to the upper negative (-) power rail.
- It’s now time to add the LEDs to the breadboard. To reduce the number of wires we need, we’ll be plugging each LED’s cathode (short pin) directly into the upper negative power rail. Start by placing an LED with its anode (long pin) into 14j, and its cathode into the upper power rail. You’ll probably need to bend the anode pin to make this work.
- Next, plug another LED into the breadboard with its anode in 16j and its cathode in the upper power rail. Continue like this until you’ve added all 8 LEDs. To make sure your LEDs are spaced properly, try to plug each LEDs anode two rows to the right of the previous one. We’ll end up with LED anodes plugged in to rows 14, 16, 18, 20, 22, 24, 26, and 28.
- Now, connect a wire from digital pin 12 on the Arduino to the row of the leftmost LED - row 14
- Then, connect a wire from pin 11 to the next LED’s row: 16.
- Continue like this, connecting from left to right, until you’ve connected wires from the Arduino to all of the LEDs. If all goes as planned, you’ll finish up by connecting pin 5 on the Arduino to row 28 on the breadboard.
- Connect a wire from analog pin 0 to any unused row on the breadboard. In the example below, the wire has been plugged in to port 7a.
And that’s it! You’re ready to move on and write the code to make your binary counter work.
Adding the Code
Note that this is exactly the same as in the companion article "Connecting an Arduino to a Breadboard to in a Tinkercad dashboard".
Now, it’s time to write the code to make our binary counter work. In the Arduino world, programs that are written and uploaded to an Arduino device are called ‘sketches’. A sketch can contain multiple files, as long as they’re all saved in the same directory. Since this is a very simple project, we’re going to put all our code in a single file.
To enter your code, you’ll need to download the Arduino IDE if you are using physical Arduino hardware, or open the code view if you’re using Tinkercad.
When you open the Arduino IDE, you’ll see a code editing screen with a simple toolbar at the top. At the left, side of the toolbar, there will be two buttons: a check mark, and an arrow. They’ll look something like this:
Clicking on the check mark will check and compile your code. Clicking on the arrow will check and compile your code and then upload it to your Arduino. To successfully upload, you’ll have to plug your Arduino into your computer with a USB cable. Then, open the Tools menu, open Ports, and choose the port your Arduino is connected to. If you’ve only got one Arduino connected, there will be only one port to choose from.
Arduino sketches are written in C++. The Arduino IDE supports C++11, so you can use all of its new features if you’d like. We’re going to stick to C++98, since that’s what the Tinkercad Arduino supports. That way, we end up with code that’ll work no matter where you want to run it. Note that the Arduino environment doesn’t include the C++ Standard Library. So you won’t be able to use things like vectors or C++ string
s. There’s an implementation of the C++ Standard Library for Arduino available on GitHub, but we won’t need it for this project.
To begin, we’ll cover a few functions that we’ll need to use to get the Arduino to light up our LEDs. These functions are all available globally. You won’t need to import anything to use them.
The first function we’ll need to use is pinMode
. This function is used to tell the Arduino whether a pin will be used for input or output. It is used like this:
pinMode(10, OUTPUT);
This would, as expected, set pin 10 to output mode. The second parameter can be set to either INPUT
or OUTPUT
. These are both constants that are available globally in any Arduino sketch.
Next up is digitalWrite
. This function lets us turn a specific output pin on or off. To turn a pins off or on, we can use two more global constants: LOW
and HIGH
. These constants aren’t anything special. If you look them up in the Arduino source code, you’ll see that LOW
is 0
and HIGH
is 1
. So, for example, to turn pin 10 off, you could write either of the following:
digitalWrite(10, LOW);
digitalWrite(10, 0)
and to turn pin 10 on, you could use either of:
digitalWrite(10, HIGH);
digitalWrite(10, 1)
Next is analogRead
. As its name implies, analogRead
is used to read an analog value from one of the Arduino’s analog pins.
The final function we’ll need is delay. This function does exactly what its name implies. It causes the Arduino to pause before executing any more code. It takes a single parameter: the number of milliseconds to pause for. So, to pause execution for 1 second, you’d write:
delay(1000);
The delay
function will be important to our binary counter, because we’ll want to pause for a moment after each time we increment the encounter. Although the Arduino is small and not very powerful, it can still count from 0 to 255 so quickly that we wouldn’t even see the counter LEDs changing.
To enter your code, you’ll need to download the Arduino IDE if you are using physical Arduino hardware, or open the code view if you’re using Tinkercad. To do this, click on the ‘Code’ button, and in the drop-down choose ‘Text’ - it is set to ‘Blocks’ by default.
Now that we understand the Arduino functions we’ll need, let’s dive in to the code that will make our binary counter tick. To keep everything nice and organized, we’re going to wrap our counter in a class:
class BinaryCounter {
};
Next, let’s fill out our class with the variables and methods it will need to do its work:
class BinaryCounter {
private:
static const uint8_t digitCount = 8;
const uint8_t pins[digitCount] = {12, 11, 10, 9, 8, 7, 6, 5};
uint8_t count = 0;
static const uint8_t showSingleValuePin = 0;
uint8_t valuetoShow = 255;
public:
BinaryCounter();
void increment();
uint8_t* getBinaryDigits(uint8_t num);
void clearLights();
};
Let’s take a quick look at the variables just added. Up first, we’ve got a count of the number of digits our binary counter will contain. We could easily just hardcode this number 8 if we wanted to, but storing it in one place makes it very easy to update our code if we want to change the number of digits our binary counter contains.
Next up, we have an array and contains the pin numbers each LED is connected to. We’ve listed them in left to write order. This will make things nice and easy later on when we want to iterate through the pins from left to write to turns LEDs on or off.
Next, we have a variable that will store the counter’s current value.
Why uint8_t,
you might ask, instead of just int
? On Arduino, an int
takes up 16 bits of memory. As its name implies, uint8_t
only takes up 8 bits. And since we’re using 8 LEDs, we only need 8 bits to represent any number we’re capable to displaying. Although it makes no difference in a small program like this one, it’s good to get in the habit of only using what you need. Arduinos only have 2KB of RAM, so it’s easy to use it all up very quickly.
Now it’s time to move on to the meat of our code. We’ll start by writing a constructor for the BinaryCounter
class.
BinaryCounter::BinaryCounter() {
for(int i = 0; i < digitCount; i++) {
pinMode(pins[i], OUTPUT);
}
The constructor only does one thing: it iterates through the pins we’ll be using, and sets them to output mode.
Next, we need a method that takes a uint8_t
, and returns an array of uint8_t. There’s just one catch: C and C++ won’t let you return an entire array. But we can easily make an array on the heap and return a pointer to its first element, so that’s what we’ll do. We’ll just have to remember to delete it when we’re done with it.
uint8_t* BinaryCounter::getBinaryDigits(uint8_t num) {
uint8_t *digits = new uint8_t[digitCount];
memset(digits, 0, digitCount * sizeof(uint8_t));
uint8_t remaining = num;
uint8_t currentPosition = digitCount-1;
while(remaining > 0) {
digits[currentPosition] = remaining % 2;
remaining /= 2;
currentPosition--;
}
return digits;
}
The first line of code creates a uint8_t
array large enough to hold all of our digits, and the second line of code sets all elements of the array to 0
.
Next, we go through the process of actually converting the integer to a binary number.
The math behind integer to binary conversion is relatively simple. We just take our number, and continually divide it by 2 until we’re left with 0. At every step, if the current number remaining is divisible by 2, then the step’s binary digit will be 0. If the current number is not divisible by two, the binary digit at that step is 1. For a more in-depth explanation, see this blog post. The best way to gain an understanding of integer to binary conversion is to do a few conversions by hand. Once you’ve done this, you’ll find the whole process much easier to understand.
Note that we’re walking through the digits array backward to set the values. The method we’re using to convert integers to binary gives us the digits in right-to-left order. By stepping through the array backwards, we’re ensuring that we set the correct values for each pin.
Once we’re done, we return the pointer to the beginning of the digits array.
Next, we’ll add code to increment the counter and turn on the LEDs that represent the current binary count:
void BinaryCounter::increment() {
uint8_t* binaryDigits;
if(analogRead(showSingleValuePin) > 500) {
binaryDigits = getBinaryDigits(valuetoShow);
}
else {
binaryDigits = getBinaryDigits(count);
if(count == 255) {
count = 0;
} else {
count++;
}
}
for(int i = 0; i < digitCount; i++) {
digitalWrite(pins[i], binaryDigits[i]);
}
delete binaryDigits;
delay(500);
}
We start by checking to see if a wire is connected to analog pin 0 (set in showSingleValuePin
). If so, we generate an 8 bit code from the value in valuetoShow
. If no wire is connected, then we run the code that increments our counter.
If we’re running the counter, we start by calling getBinaryDigits
to turn the current count into an array of binary digits.
Since the array we get back from getBinaryDigits
contains the digits in left-to-right order, and our pins array contains the output pins in left-to-right order, we can use a simple for loop to step through both arrays and set each pin to its corresponding value the binaryDigits array.
Since digitalWrite
turns a pin off if passed a 0 as its second parameter, and turns a pin on if it receives a 1, the net result is that our LEDs display the current binary count: any digit that is a zero results in a turned off LED, and any digit that is a 1 results in a turned on LED.
After we loop through and turn the LEDs on and off, we delete the binaryDigits
array since we’re done with it. It’s very important not to forget this step. If you do, your program will have a memory leak. The Arduino will run out of memory and lock up very quickly. This won’t damage the Arduino, but your counter will only work for a short time and then freeze.
Next, we check if the count has reached 255, since 255 is the largest number we can represent in binary with 8 LEDs. If we’ve reached 255, we reset the counter to 0. Otherwise, we increase the count by 1. You might notice that we could actually skip this check and just increment the counter. Since 255 is the largest value a uint8_t
can hold, if we try to increment the count when it is already 255, it will just overflow to 0.
In cases like this, though, it’s best to be explicit. Relying on the uint8_t
to overflow from 255 back to 0 would work, but doing so would make our code harder to understand. Even if you’re the only person who will ever look at the code, you might end up confusing yourself if you come back to the code after not seeing it for a few months.
Finally, we delay for 500 milliseconds. This causes the Arduino to pause for half a second before moving on to the next digit.
And that’s it for the BinaryCounter
class. We’re almost done! We just need a few more lines of code to initializer our counter and make it count:
BinaryCounter *counter;
void setup()
{
counter = new BinaryCounter();
}
void loop()
{
counter->increment();
}
We start off by creating a global variable to store a pointer to a BinaryCounter
. In general, global variables are something that developers try to avoid. The Arduino programming environment is a bit limited, though. Creating a global BinaryCounter
is the only way to make it available to Arduino’s loop function.
Next, we see two functions: setup
and loop
. These are both part of the Arduino programming environment. The setup
function runs once when the Arduino boots up and executes your program. The Arduino then runs the loop
function continually.
The rest of our code is simple: in setup
, we create a new instance of BinaryCounter
. Then, every time loop
runs, we call the counter’s increment
function to set the LEDs and increment the counter.
And...that’s it! That’s all the code we need to make the binary counter work.
In the Arduino IDE, make sure your Arduino is connected to your computer and then click the upload button.
Now, just kick back, relax, and watch your binary counter in action!
Complete Code Listing
class BinaryCounter {
private:
static const uint8_t showSingleValuePin = 0;
static const uint8_t digitCount = 8;
const uint8_t pins[digitCount] = {12, 11, 10, 9, 8, 7, 6, 5};
uint8_t count = 0;
uint8_t valuetoShow = 255;
public:
BinaryCounter();
void increment();
uint8_t* getBinaryDigits(uint8_t num);
};
BinaryCounter::BinaryCounter() {
for(int i = 0; i < digitCount; i++) {
pinMode(pins[i], OUTPUT);
}
}
uint8_t* BinaryCounter::getBinaryDigits(uint8_t num) {
uint8_t *digits = new uint8_t[digitCount];
memset(digits, 0, digitCount * sizeof(uint8_t));
uint8_t remaining = num;
uint8_t currentPosition = digitCount-1;
while(remaining > 0) {
digits[currentPosition] = remaining % 2;
remaining /= 2;
currentPosition--;
}
return digits;
}
void BinaryCounter::increment() {
uint8_t* binaryDigits;
if(analogRead(showSingleValuePin) > 500) {
binaryDigits = getBinaryDigits(valuetoShow );
}
else {
binaryDigits = getBinaryDigits(count);
if(count == 255) {
count = 0;
} else {
count++;
}
}
for(int i = 0; i < digitCount; i++) {
digitalWrite(pins[i], binaryDigits[i]);
}
delete binaryDigits;
delay(500);
}
BinaryCounter *counter;
void setup()
{
counter = new BinaryCounter();
}
void loop()
{
counter->increment();
}
I spent 5 years working for Ottawa startups before returning home to Toronto. During Covid I decided to escape the big city and moved north to Penetanguishene, Ontario.
I'm a .NET/JavaScript/TypeScript developer and technical writer by day, but in the evening you'll often find me cooking up projects in Ruby, Haskell, Clojure, Elixir, and F#.