Exercise 2: Macro Definitions & Build Automation¶
This section is still a work-in-progress!
In the last exercise, you blinked an LED, and in this lesson, you’ll be doing the same thing. The difference is that in this exercise, I’ll cover things to make you a far more effective programmer.
What I’m about to say may be surprising to some of you, but the “best” code is code that is easiest to read and maintain. “But wait!” I hear one of you yelling, “Isn’t the best code the code that runs the fastest!?” I too was once under this impression, but soon you realize this isn’t the rule, rather the exception.
Programming is an art form. It is a constant balance between efficiency and elegance. Is it worth spending hours writing something that will run marginally faster? I don’t know about you, but I value my time over the computer’s time. The only time you should be writing the most efficient code is when you’ve settled on a final design, and you know it will be worth the investment to write marginally more efficient code. If this is the case, then yes, spend that extra time perfecting your code.
I’ll explain more in the breakdown, for now, create sketch
ex2 and type out the following code in the appropriate files.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
If you are using the Arduino IDE, you can safely ignore this file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
There isn’t much to do in regards to automating builds when using the Arduino IDE because it already does this for you. Although builds are automated, it doesn’t hurt to learn some keybinds to save a second or two every time you want to compile or flash your program.
To compile your program press Ctrl+R
To flash your program press Ctrl+U
Up until now, using Arduino CLI over the Arduino IDE has been more work.
And this is where I would like to introduce you to your new friend,
make is a UNIX tool, if you are a Windows user, you’ll have to look elsewhere for a build automation system.
make you need something called a
Makefile has targets and dependencies, for instance
All you need to understand is that
make will let us automate typing out long commands that are prone to user error.
To compile your program, open a terminal at the root of your sketch, in this case
What you should see when running the
tree command is as follows:
. ├── ex2.ino └── Makefile 0 directories, 2 files
From the output we can see that the current directory
. has files
To compile your program simply run
You should have output like this:
arduino-cli compile --fqbn arduino:avr:uno ../ex2 Sketch uses 960 bytes (2%) of program storage space. Maximum is 32256 bytes. Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes.
This should look familiar as we can see the compile command from the previous exercise, along with compiler output.
To flash your program to the Arduino, we must first determine the device port like in execrise 1.
Next we simply run
make PORT=/dev/ttyACM1 flash.
arduino-cli compile --fqbn arduino:avr:uno ../ex2 Sketch uses 960 bytes (2%) of program storage space. Maximum is 32256 bytes. Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes. arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:avr:uno ../ex2
From the output we see the flash command we ran in the previous exercise as well as the output from Arduino CLI.
#define is a directive that allows for the definition of macros within your source code.
In this instance, we are creating constants for our source code.
LED corresponds to
DELAY corresponds to
When the program gets compiled, the C preprocessor will first evaluate each macro before moving on to the next steps of the compiling process.
ex2.ino:11: After the C preprocessor runs through this line, it will evaluate to
ex2.ino:17: This line may look cryptic to a beginner, but it’s simple.
We know from the last lesson that
digitalWrite() takes two arguments, the pin and the state.
The first argument,
LED is a macro which will evaluate to
The second argument is the inverse of the value returned by
This function takes one parameter, a digital pin, and returns an
int value that is either
! will then get the inverse of the return value, or in other words, enable us to toggle the pin.
In the end, this line will evaluate to either
digitalWrite(LED_BULITIN, 0); or
ex2.ino:18: This line shouldn’t be too difficult; it will just evaluate the
DELAY macro to
If you don’t understand the structure of a
Makefile don’t stress it.
Makefile‘s are beyond the scope of this course, all you need to know is it automates builds.
Makefile:1: Here we declare a variable called
?= will declare the variable to nothing if it is not declared a value at runtime.
Makefile:3: Here we have our first (and default)
make target called
Makefile:4: Here we have the command that will be run for the
You can see that it is the same command we ran in the previous command with
arduino-cli to compile the sketch.
The only thing that might be unfamiliar is the
This will evaluate the
PWD to get the current working directory.
Makefile:6-7: This is our second target
clean: which will clean out any compiled binaries from our working directory.
@ idicates to not print the command being run to stdout1.
Standard Output ↩