Variables¶
In programming, a variable is like a storage container used to hold
data. Think of it as a labeled box where you can store a value—whether
it’s a number, a piece of text, or a true / false statement. Variables
allow programs to process and manipulate information dynamically.
Variables are similar to how they’re used in math, where you might have
an equation like x = 5. In programming, you can assign values to
variables and use them to perform calculations, make decisions, or
control hardware components. But how are variables defined in Arduino?
Defining a Variable¶
Let’s say you’re writing a program to control the speed of a motor (can be any example, not necessarily a motor). You might use a variable to store the speed value, which can then be adjusted during the program’s execution.
int motorSpeed = 100; // Variable to store the motor speed
The above line of code creates a variable named motorSpeed and
assigns it a value of 100.
To define a variable in Arduino, you use the following syntax:
<variable type> <variable name> = <value>;
In our motor speed example, int (the variable type) before the
variable name indicates that it’s an integer, or a whole number without
a decimal point. We’ll discover more of these Data Types as
we go along.
Variable Mutability¶
Sometimes you want to change the value of a variable as your program. This is called mutability, and it describes the process of mutating (changing) the value of a variable.
To change the value of a variable, you assign a new value to it. For example,
you could change the value of motorSpeed later in the program, allowing you to
control the motor’s speed dynamically:
// This variable's value can change.
int speed = 100;
// We can change it simply by assigning a new value.
speed = 200;
Conversely, a variable that cannot change its value is known as immutable, or a constant. We’ll cover this in the Variable Qualifiers section.
Data Types¶
Every variable in Arduino has a type, which defines the kind of data it can store. The type determines the size of the variable in memory and the operations you can perform on it. Types vary from simple numbers to complex structures, each with its own rules and limitations.
Here are some of the most commonly used data types:
int(Integer): Stores whole numbers, such as1,42, or-7.int myNumber = 10; // Stores the number 10
Trying to store a decimal number in an
intvariable will truncate (a word for remove) the decimal portion. For example,int pi = 3.14;will store3inpi. “Whole integers” (aka whole numbers) are numbers without a decimal point.This also goes for arithmetic operations. If you divide two integers, the result will be an integer. For example,
5 / 2will result in2, not2.5.int result = 5 / 2; // Stores 2, not 2.5
long: Anintcan only store numbers up to a certain size. If you need to store larger numbers, you can use along. Alongcan store larger numbers than anint.long bigNumber = 1000000L; // Stores a large number
Notice the
Lat the end of the number. This tells the compiler that the number is along. If you don’t include theL, the number will be treated as anint.A
longis useful when you need to store numbers that are too large for anint. It can store numbers up to2,147,483,647.longs can only store whole numbers, not decimals.Note
The
longtype is not used as often asintin this course. However, it is important to know that it exists as some libraries may require it. More on this later, though.float(Floating-Point Number): Stores numbers with decimals, such as3.14,0.5, or-2.718.float pi = 3.14; // Stores the value of pi with decimals
Floating-point numbers can represent a wide range of values, including fractions and very large or very small numbers. They are useful for calculations that require precision. They can also hold whole numbers, but they may use more memory than
intvariables.String(Text): Stores a sequence of characters, such as"Hello","Arduino", or"123".String message = "Hello, Arduino!"; // Stores a text message
A
Stringis how you store messages, words, or sentences in code. When creating a string, it must be enclosed in double quotes ("). In Arduino, you can manipulate strings, such as combining them or extracting parts of them (covered in Math Operations later). Strings are useful for displaying messages, reading input, or storing text-based data. We’ll cover these in more detail later.char(Character): Stores a single character, such as'A','b', or'7'.char grade = 'A'; // Stores the letter A
Characters are enclosed in single quotes (
') to distinguish them fromStrings. Characters only represent individual letters, digits, or symbols. Acharcannot hold multiple characters, it can only store a single character. These are not often used in the course, however, they may be important in some specific cases.bool(Boolean): Storestrueorfalsevalues.bool isLightOn = true; // Indicates whether a light is on
Internally,
trueis represented as1andfalseas0. Booleans are used for logical operations, comparisons, and decision-making in your code. You may see abooldisplay as a1or0because of this.
Caution
Note the distinction between a char and a String.
A char stores a single character and uses '' (single quotes), while a
String stores multiple characters and uses "" (double
quotes). chars can only hold a single character, while
Strings can hold multiple characters. Thus,
char letter = 'A'; // Correct
String word = "Hello"; // Correct
char word = "Hello"; // Incorrect! "" is a String
Defining a variable with the wrong type will result in a compilation. error. Make sure to use the correct type for your data.
Variable Qualifiers¶
Variable qualifiers are additional keywords that modify the behavior
of variables. They provide information about how the variable
should be treated or used in the program. One common qualifier is
const, which we’ll cover here.
const¶
The const keyword is used to define a constant variable, which
is a variable whose value cannot be changed once it’s set. This is also
known as an immutable variable. Constants are
useful for storing values that should not be modified during the
program’s execution, such as mathematical constants or pin numbers.
Defining a const Variable¶
To define a constant variable, you use the following syntax:
const <variable type> <variable name> = <value>;
For example, let’s say you wanted to define a pin number for an LED that is connected to pin 13 on your Arduino board. This pin does not change during the execution of your code so it’s a good candidate for a constant.
const int LED_PIN = 13; // Defines a constant for the LED pin
As a general rule of thumb, you want to declare any variable you know
will not change as a const. This is because it is good practice to
make sure that you do not accidentally change the value of a variable
that should not be changed.
const int LED_PIN = 13; // Defines a constant for the LED pin
LED_PIN = 10; // Error! You cannot change the value of a constant.
Sometimes you want an error to be thrown if you accidentally change the
value of a variable. This is where const comes in handy.
Note
Advanced Note: Constants vs Preprocessor Directives [OPTIONAL]: When defining pins to variables, it is recommended to use preprocessor directives instead of constants. This is because preprocessor directives are more efficient and cleaner. However, for the purposes of this course, we will be using constants. You can read more about this in the Macros and Preprocessor Directives section.
See also
There are many other modifiers in the Arduino Language, however, you do not need to know them for this course. You can find them on the Arduino Language Reference if you are interested, but you do not need to.
Variable Initialization vs Definition¶
So far, we’ve discussed how to define variables and assign them values. Common examples have shown a variable being defined and a value being assigned to it at the same time. However, this is not the only way to create a variable. You can either,
Define a variable and assign it a value at the same time (Initialization). This is the most common way to create variables and what you have seen so far.
Define a variable without assigning it a value (Definition).
How do these two differ, and when should you use one over the other?
Initialization¶
Initialization is the process of assigning an initial value to a
variable when it is declared. This often happens at the time the
variable is created in the program. For example, if you declare a
variable int x = 5;, you are both declaring the variable x
and initializing it with the value 5. Initialization ensures that
a variable has a valid value before it is used, preventing undefined
behavior.
For example,
int x = 5; // Variable 'x' is defined and initialized to 5
int y; // Variable 'y' is defined but not initialized
// Trying to use 'y' without a value is going to crash
// your program!
Serial.println(y); // Error: 'y' is not initialized
Tip
Serial.println() tries to use the y variable in
the code above. This will cause an error because y has not been
initialized with a value. Do not worry about what Serial.println()
is yet, this is covered in Functions.
All you need to know is that the program crashes.
Definition¶
Definition refers to the process of declaring a variable’s type and name
without necessarily assigning it an initial value. For example,
int x; defines the variable x but does not initialize it,
leaving its value indeterminate until it is explicitly assigned later in
the code. Using an uninitialized variable can lead to unpredictable
behavior or errors in your program.
int y; // Variable 'y' is defined but not initialized
y = 10; // 'y' is assigned a value after definition
Key Difference Between Initialization and Definition¶
The key difference between initialization and definition is whether a variable is given a value at the time it is declared. Sometimes in programming you want to define a variable without giving it a value, and then assign it a value later in the program. However, You should initialize variables whenever possible to ensure they have a valid value before being used.
// Initialization:
int a = 10; // Variable 'a' is defined and initialized to 10
// Definition:
// Variable 'b' is defined but not initialized.
// if you try and use 'b' without giving it a value,
// your program will crash!
int a;
// Usage
a = 10; // 'b' is assigned a value after definition
These two code blocks are functionally equivalent, but the first is considered better practice because it ensures the variable has a valid value from the start.
To sum this, initialization combines the steps of definition and value assignment, while definition by itself only reserves memory and specifies the type without assigning a value.
Tip
In this course, you will mostly see variables being initialized when they are defined. This is because it is good practice to ensure that variables have a valid value before they are used. When you start to use Libraries and more complex code, you will see variables being defined without being initialized.
We will cover those cases when they come up.
Built-in Variables and Constants¶
Arduino provides a set of predefined constants (variables that cannot change) to simplify working with hardware components. These constants are used to control pins, set input/output modes, and interact with external devices.
See also
You can view all the builtin constants on the Arduino documentation, however, we will only be covering exactly what you need to know in this course.
HIGH and LOW¶
Two of the most commonly used constants are HIGH and
LOW. These are used in conjunction with digital pins to
represent the states of those pins.
HIGH: Represents a digital signal of1or a voltage of approximately5V(on most boards). It’s often used to turn on an LED, power a device, or indicate an active state.LOW: Represents a digital signal of0or a voltage of0V. It’s typically used to turn off an LED, cut power, or indicate an inactive state.
When working with Arduino pins, these constants allow you to control devices like LEDs, relays, or other components in an easy-to-read manner:
digitalWrite(13, HIGH); // Turns on an LED connected to pin 13
digitalWrite(13, LOW); // Turns off the LED
In practical terms, HIGH and LOW correspond to the electrical
state of a given pin.
INPUT and OUTPUT¶
In addition to HIGH and LOW, Arduino provides two more
constants: INPUT and OUTPUT. These constants are used to
set the mode of a pin, indicating whether it should be used for reading
input or writing output.
INPUT: Sets a pin as an input, allowing your code to read external signals or sensor data.OUTPUT: Sets a pin as an output, enabling your code to send signals to external devices like LEDs, motors, or relays.
pinMode(2, INPUT); // Sets pin 2 as an input
pinMode(13, OUTPUT); // Sets pin 13 as an output
LED_BUILTIN¶
LED_BUILTIN is a constant that represents the built-in LED on most
Arduino boards, including your Arduino Uno. This constant is useful when
you want to control the built-in LED without specifying a pin number.
digitalWrite(LED_BUILTIN, HIGH); // Turns on the built-in LED
digitalWrite(LED_BUILTIN, LOW); // Turns off the built-in LED
Tip
HIGH / LOW and INPUT / OUTPUT will be covered in more detail when
discussing controlling pins and
interacting with external components in the Your First
Arduino Program section.
These variables will be used extensively in your Arduino projects. Don’t worry about memorizing them now; you’ll become familiar with them over time.
Variable Scope¶
In programming, there are rules that determine where a variable can be used in your code. This is known as variable scope. Understanding variable scope is crucial, as it affects how you structure your programs and how you manage data.
In Arduino, variables can have global scope or local scope, and the distinction impacts how you structure your programs.
Global Scope¶
Variables with global scope are declared outside of any function. They can be accessed and modified by any part of the program, including all functions.
Example: Global Variable:
int counter = 0; // Global variable
void setup() {
Serial.begin(9600);
}
void loop() {
counter++; // Increment the global counter
Serial.println(counter); // Accessible in loop()
delay(1000);
}
In this example, counter is accessible throughout the entire
program. However, overusing global variables can make debugging
difficult, as changes in one part of the code may unintentionally affect
another.
Note
Typically global variables are defined using UPPER_SNAKE_CASE to
distinguish them from local variables. This is a common convention in
programming.
int GLOBAL_VARIABLE = 0;
Local Scope¶
Variables with local scope are declared inside a function or block of
code (e.g., inside {}). They are only accessible within that
specific function or block.
Example: Local Variable¶
void setup() {
Serial.begin(9600);
}
void loop() {
int localCounter = 0; // Local variable
localCounter++; // Increment local variable
Serial.println(localCounter); // Always prints 1
delay(1000);
}
Here, localCounter is recreated each time loop() runs, so its
value doesn’t persist between iterations. This ensures that changes to
the variable do not affect other parts of the program.
Nested Functions and Variable Scope¶
In Arduino, while you cannot define functions directly inside other functions, you can create a structure where functions call other functions. This allows for modular code while maintaining the scope of variables within individual functions.
Example: Nested Function Calls¶
int calculateSum(int a, int b) { // Function used within another function
return a + b;
}
void printResult(int num1, int num2) {
int sum = calculateSum(num1, num2); // Call a helper function
Serial.print("The sum of ");
Serial.print(num1);
Serial.print(" and ");
Serial.print(num2);
Serial.print(" is ");
Serial.println(sum);
}
void setup() {
Serial.begin(9600);
printResult(5, 7); // Prints: The sum of 5 and 7 is 12
}
void loop() {
// No code needed here
}
In this example:
calculateSumis a helper function used by printResult.The variable
sumis local toprintResultand cannot be accessed outside of it, ensuring modularity and minimizing potential bugs.
Why Scope Matters¶
Avoiding Conflicts: Keeping variables local where possible reduces the chances of accidental changes elsewhere in the program.
Improved Readability: Local variables make it clear where and how a variable is used.
Memory Efficiency: Local variables are created and destroyed as needed, reducing memory usage compared to global variables.
By carefully managing variable scope, you can write cleaner, more efficient, and less error-prone programs. Aim to use global variables sparingly and rely on local variables whenever possible for modular, maintainable code.